Import Cobalt 9.34413
diff --git a/src/base/base.gyp b/src/base/base.gyp
index 7f8ec57..e7e004c 100644
--- a/src/base/base.gyp
+++ b/src/base/base.gyp
@@ -19,6 +19,7 @@
         'optimize': 'max',
       },
       'dependencies': [
+        '<(DEPTH)/nb/nb.gyp:nb',
         '<(DEPTH)/starboard/client_porting/eztime/eztime.gyp:eztime',
         '<(DEPTH)/starboard/starboard.gyp:starboard',
         '<(DEPTH)/testing/gtest.gyp:gtest_prod',
diff --git a/src/base/message_loop.cc b/src/base/message_loop.cc
index 2f8b032..83d2184 100644
--- a/src/base/message_loop.cc
+++ b/src/base/message_loop.cc
@@ -24,6 +24,7 @@
 #include "base/threading/thread_local.h"
 #include "base/time.h"
 #include "base/tracked_objects.h"
+#include "nb/memory_scope.h"
 
 #if defined(OS_MACOSX)
 #include "base/message_pump_mac.h"
@@ -675,6 +676,7 @@
 }
 
 bool MessageLoop::DoWork() {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   if (!nestable_tasks_allowed_) {
     // Task can't be executed right now.
     return false;
diff --git a/src/base/message_pump_default.cc b/src/base/message_pump_default.cc
index 649e2aa..ba6416e 100644
--- a/src/base/message_pump_default.cc
+++ b/src/base/message_pump_default.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/threading/thread_restrictions.h"
+#include "nb/memory_scope.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
@@ -19,6 +20,7 @@
 }
 
 void MessagePumpDefault::Run(Delegate* delegate) {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   DCHECK(keep_running_) << "Quit must have been called outside of Run!";
 
   for (;;) {
diff --git a/src/base/message_pump_io_starboard.cc b/src/base/message_pump_io_starboard.cc
index bbd1707..e08ddb8 100644
--- a/src/base/message_pump_io_starboard.cc
+++ b/src/base/message_pump_io_starboard.cc
@@ -21,6 +21,7 @@
 #include "base/observer_list.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/time.h"
+#include "nb/memory_scope.h"
 #include "starboard/socket.h"
 #include "starboard/socket_waiter.h"
 
@@ -164,6 +165,7 @@
 
 // Reentrant!
 void MessagePumpIOStarboard::Run(Delegate* delegate) {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   DCHECK(keep_running_) << "Quit must have been called outside of Run!";
   AutoReset<bool> auto_reset_in_run(&in_run_, true);
 
diff --git a/src/base/message_pump_ui_starboard.cc b/src/base/message_pump_ui_starboard.cc
index 74a71f5..a99be50 100644
--- a/src/base/message_pump_ui_starboard.cc
+++ b/src/base/message_pump_ui_starboard.cc
@@ -17,7 +17,7 @@
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/time.h"
-
+#include "nb/memory_scope.h"
 #include "starboard/event.h"
 #include "starboard/system.h"
 
@@ -105,6 +105,7 @@
 
 void MessagePumpUIStarboard::ScheduleDelayedWork(
     const base::TimeTicks& delayed_work_time) {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   base::TimeDelta delay;
   if (!delayed_work_time.is_null()) {
     delay = delayed_work_time - base::TimeTicks::Now();
diff --git a/src/base/platform_file_starboard.cc b/src/base/platform_file_starboard.cc
index cc116c6..144f61c 100644
--- a/src/base/platform_file_starboard.cc
+++ b/src/base/platform_file_starboard.cc
@@ -261,7 +261,7 @@
 }
 
 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo *info) {
-  if (!info || file < 0)
+  if (!info || !SbFileIsValid(file))
     return false;
 
   SbFileInfo file_info;
diff --git a/src/base/timer.cc b/src/base/timer.cc
index 952ce37..7c9f26c 100644
--- a/src/base/timer.cc
+++ b/src/base/timer.cc
@@ -8,6 +8,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/thread_task_runner_handle.h"
 #include "base/threading/platform_thread.h"
+#include "nb/memory_scope.h"
 
 namespace base {
 
@@ -31,6 +32,7 @@
   }
 
   void Run() {
+    TRACK_MEMORY_SCOPE("TaskProcessor");
     // timer_ is NULL if we were abandoned.
     if (!timer_)
       return;
@@ -142,6 +144,7 @@
 // PostNewScheduledTask() to ensure that the default behavior of Timer is
 // exactly the same as before.
 void Timer::PostNewScheduledTask(TimeDelta delay) {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   DCHECK(scheduled_task_ == NULL);
   is_running_ = true;
   scheduled_task_ = new BaseTimerTaskInternal(this);
@@ -157,6 +160,7 @@
 
 Timer::NewScheduledTaskInfo Timer::SetupNewScheduledTask(
     TimeDelta expected_delay) {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   DCHECK(scheduled_task_ == NULL);
   DCHECK(is_task_run_before_scheduling_next_);
   DCHECK(thread_id_);
@@ -176,6 +180,7 @@
 
 void Timer::PostNewScheduledTask(NewScheduledTaskInfo task_info,
                                  TimeDelta delay) {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   // Some task runners expect a non-zero delay for PostDelayedTask.
   if (delay.ToInternalValue() == 0) {
     ThreadTaskRunnerHandle::Get()->PostTask(task_info.posted_from,
@@ -196,6 +201,7 @@
 }
 
 void Timer::RunScheduledTask() {
+  TRACK_MEMORY_SCOPE("TaskProcessor");
   // Task may have been disabled.
   if (!is_running_)
     return;
diff --git a/src/cobalt/accessibility/accessibility.gyp b/src/cobalt/accessibility/accessibility.gyp
index 50de511..08db41c 100644
--- a/src/cobalt/accessibility/accessibility.gyp
+++ b/src/cobalt/accessibility/accessibility.gyp
@@ -33,6 +33,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
       ],
     },
   ]
diff --git a/src/cobalt/accessibility/screen_reader.cc b/src/cobalt/accessibility/screen_reader.cc
index db4b5e5..13a554e 100644
--- a/src/cobalt/accessibility/screen_reader.cc
+++ b/src/cobalt/accessibility/screen_reader.cc
@@ -54,7 +54,9 @@
   init.set_character_data(true);
   init.set_child_list(true);
 
-  live_region_observer_->Observe(document_->body(), init);
+  if (document_->body()) {
+    live_region_observer_->Observe(document_->body(), init);
+  }
 }
 
 void ScreenReader::OnFocusChanged() {
diff --git a/src/cobalt/account/user_authorizer.h b/src/cobalt/account/user_authorizer.h
new file mode 100644
index 0000000..a128c0b
--- /dev/null
+++ b/src/cobalt/account/user_authorizer.h
@@ -0,0 +1,86 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_ACCOUNT_USER_AUTHORIZER_H_
+#define COBALT_ACCOUNT_USER_AUTHORIZER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
+#include "base/time.h"
+#include "starboard/user.h"
+
+namespace cobalt {
+namespace account {
+
+struct AccessToken {
+  // The token value.
+  std::string token_value;
+
+  // The absolute time that this token expires, if any.
+  base::optional<base::Time> expiry;
+};
+
+// Platform-specific mechanism to authorize a user to access protected resources
+// in a web app running in Cobalt. Manages getting, refreshing, and discarding
+// an access token associated with a platform user identified by a |SbUser|.
+class UserAuthorizer {
+ public:
+  UserAuthorizer() {}
+
+  virtual ~UserAuthorizer() {}
+
+  // Initiates a process to get an access token authorizing |user| to use
+  // application resources (i.e. sign-in).  This call will block until the
+  // authorization process is complete, which may involve user input.
+  // After this call completes successfully, subsequent calls to
+  // RefreshAuthorization for the same |user| will return valid tokens.
+  // Returns a scoped_ptr holding NULL if the process failed for any reason
+  // including an invalid |user|, or user cancellation of the authorization
+  // process.
+  // On success, a scoped_ptr holding a valid AccessToken is returned.
+  virtual scoped_ptr<AccessToken> AuthorizeUser(SbUser user) = 0;
+
+  // Remove authorization for |user| to use application resources (i.e.
+  // sign-out).  This call will block until the linking process is complete,
+  // which may involve user input.
+  // After this call completes successfully, subsequent calls to
+  // RefreshAuthorization for the same |user| will fail.
+  // Returns true if the process to remove authorization unlink the token
+  // succeeded.
+  // Returns false if the process failed for any reason including an invalid
+  // |user|, or user cancellation.
+  virtual bool DeauthorizeUser(SbUser user) = 0;
+
+  // Requests a new access token to extend authorization for |user| to continue
+  // using application resources.
+  // This call will block until the token has been received.
+  // Returns a scoped_ptr holding NULL if the process failed for any reason
+  // including an invalid |user|, or |user| not already being authorized (in
+  // which case AuthorizeUser should be called).
+  // On success, a scoped_ptr holding a valid AccessToken is returned.
+  virtual scoped_ptr<AccessToken> RefreshAuthorization(SbUser user) = 0;
+
+  // Instantiates an instance of the platform-specific implementation.
+  static UserAuthorizer* Create();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UserAuthorizer);
+};
+
+}  // namespace account
+}  // namespace cobalt
+
+#endif  // COBALT_ACCOUNT_USER_AUTHORIZER_H_
diff --git a/src/cobalt/audio/audio.gyp b/src/cobalt/audio/audio.gyp
index 09fcbda..23be191 100644
--- a/src/cobalt/audio/audio.gyp
+++ b/src/cobalt/audio/audio.gyp
@@ -48,6 +48,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
       ],
     },
 
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index e2be55a..c70a97e 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -32,6 +32,7 @@
     MacroOpWithNameOnly(assertive)                            \
     MacroOpWithNameOnly(attributes)                           \
     MacroOpWithNameOnly(blur)                                 \
+    MacroOpWithNameOnly(boundary)                             \
     MacroOpWithNameOnly(canplay)                              \
     MacroOpWithNameOnly(canplaythrough)                       \
     MacroOpWithNameOnly(change)                               \
@@ -40,6 +41,7 @@
     MacroOpWithNameOnly(close)                                \
     MacroOpWithNameOnly(durationchange)                       \
     MacroOpWithNameOnly(emptied)                              \
+    MacroOpWithNameOnly(end)                                  \
     MacroOpWithNameOnly(ended)                                \
     MacroOpWithNameOnly(error)                                \
     MacroOpWithNameOnly(focus)                                \
@@ -55,6 +57,7 @@
     MacroOpWithNameOnly(loadedmetadata)                       \
     MacroOpWithNameOnly(loadend)                              \
     MacroOpWithNameOnly(loadstart)                            \
+    MacroOpWithNameOnly(mark)                                 \
     MacroOpWithNameOnly(message)                              \
     MacroOpWithNameOnly(needkey)                              \
     MacroOpWithNameOnly(nomatch)                              \
@@ -71,6 +74,7 @@
     MacroOpWithNameOnly(removesourcebuffer)                   \
     MacroOpWithNameOnly(removetrack)                          \
     MacroOpWithNameOnly(result)                               \
+    MacroOpWithNameOnly(resume)                               \
     MacroOpWithNameOnly(securitypolicyviolation)              \
     MacroOpWithNameOnly(seeked)                               \
     MacroOpWithNameOnly(seeking)                              \
@@ -79,6 +83,7 @@
     MacroOpWithNameOnly(sourceclose)                          \
     MacroOpWithNameOnly(sourceended)                          \
     MacroOpWithNameOnly(sourceopen)                           \
+    MacroOpWithNameOnly(start)                                \
     MacroOpWithNameOnly(storage)                              \
     MacroOpWithNameOnly(stalled)                              \
     MacroOpWithNameOnly(suspend)                              \
@@ -90,6 +95,7 @@
     MacroOpWithNameOnly(update)                               \
     MacroOpWithNameOnly(updateend)                            \
     MacroOpWithNameOnly(updatestart)                          \
+    MacroOpWithNameOnly(voiceschanged)                        \
     MacroOpWithNameOnly(volumechange)                         \
     MacroOpWithNameOnly(waiting)
 
@@ -110,7 +116,7 @@
     MacroOpWithNameAndValue(document_name, "#document")                 \
     MacroOpWithNameAndValue(domcontentloaded, "DOMContentLoaded")       \
     MacroOpWithNameAndValue(empty_pseudo_class_selector, "empty")       \
-    MacroOpWithNameAndValue(false_token, "false")                         \
+    MacroOpWithNameAndValue(false_token, "false")                       \
     MacroOpWithNameAndValue(focus_pseudo_class_selector, "focus")       \
     MacroOpWithNameAndValue(hover_pseudo_class_selector, "hover")       \
     MacroOpWithNameAndValue(id_selector_prefix, "#")                    \
diff --git a/src/cobalt/bindings/contexts.py b/src/cobalt/bindings/contexts.py
index 2a54a78..eb14d72 100644
--- a/src/cobalt/bindings/contexts.py
+++ b/src/cobalt/bindings/contexts.py
@@ -19,6 +19,7 @@
 """
 
 from idl_definitions import IdlTypedef
+from idl_types import IdlPromiseType
 from idl_types import IdlSequenceType
 from name_conversion import capitalize_function_name
 from name_conversion import convert_to_cobalt_constant_name
@@ -42,6 +43,10 @@
   return isinstance(idl_type, IdlSequenceType)
 
 
+def is_promise_type(idl_type):
+  return isinstance(idl_type, IdlPromiseType)
+
+
 def idl_literal_to_cobalt_literal(idl_type, idl_literal):
   """Map IDL literal to the corresponding cobalt value."""
   if idl_literal.is_null and not idl_type.is_interface_type:
@@ -237,6 +242,8 @@
       cobalt_type = 'ValueHandle'
     elif idl_type.is_dictionary:
       cobalt_type = get_interface_name(idl_type)
+    elif is_promise_type(idl_type):
+      cobalt_type = 'NativePromise'
 
     assert cobalt_type, 'Unsupported idl_type %s' % idl_type
 
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
index f29191b..2eec092 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
index 0357462..2db7ce9 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
index 6267442..c0d2e3a 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
index 7cacca4..c10480e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
index 86abb5a..840e07d 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
index 52cb989..4f6d720 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
index c3bb4a1..d4323fd 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
index fad5638..c8af6c7 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
index 4b1acd7..f6adfc1 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
index 68dbfbf..3167249 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
index 996883c..35fb74f 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
index 62b1da9..f33738c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
index 79f544d..f590af2 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
index aa4344c..60a6efb 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
index 1d50508..945e7da 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -37,6 +37,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
index d002e0c..203d729 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
index 271f2a3..7134234 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
index 95c9eb4..93a42bc 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
index 957f806..4132357 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
index 51f85c3..186f21e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
index 1db44c4..c609226 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
index e72b6f1..4bf4b5c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
index c10932c..3698732 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
index 8f262c9..5ec4a72 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
index ec38ee6..50b9278 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
index bdca6b5..7d61bf7 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
index c07d18c..2c6fe76 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
index 0e3b892..3aac1fe 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
index 9b4b430..e2e0a85 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
index 0aa039d..b7ba06f 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
index 32b2018..6b1689e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
index 8563497..7c5ff9a 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
index 2e1e598..bc382f7 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
index 9190584..3796736 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
index 9ce4df5..00b8359 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
index 8b2995e..adc219c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -42,6 +42,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
index 343d79b..d2c44c0 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
new file mode 100644
index 0000000..e37f1f9
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -0,0 +1,661 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/interface.cc.template
+
+#include "cobalt/bindings/testing/mozjs_promise_interface.h"
+
+#include "base/debug/trace_event.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/opaque_handle.h"
+#include "cobalt/script/script_value.h"
+
+#include "base/lazy_instance.h"
+#include "cobalt/script/exception_state.h"
+#include "cobalt/script/mozjs/callback_function_conversion.h"
+#include "cobalt/script/mozjs/conversion_helpers.h"
+#include "cobalt/script/mozjs/mozjs_callback_function.h"
+#include "cobalt/script/mozjs/mozjs_exception_state.h"
+#include "cobalt/script/mozjs/mozjs_global_environment.h"
+#include "cobalt/script/mozjs/mozjs_object_handle.h"
+#include "cobalt/script/mozjs/mozjs_property_enumerator.h"
+#include "cobalt/script/mozjs/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
+#include "cobalt/script/mozjs/proxy_handler.h"
+#include "cobalt/script/mozjs/type_traits.h"
+#include "cobalt/script/mozjs/wrapper_factory.h"
+#include "cobalt/script/mozjs/wrapper_private.h"
+#include "cobalt/script/property_enumerator.h"
+#include "cobalt/script/sequence.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+#include "third_party/mozjs/js/src/jsfriendapi.h"
+
+namespace {
+using cobalt::bindings::testing::PromiseInterface;
+using cobalt::bindings::testing::MozjsPromiseInterface;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::GlobalEnvironment;
+using cobalt::script::OpaqueHandle;
+using cobalt::script::OpaqueHandleHolder;
+using cobalt::script::ScriptValue;
+using cobalt::script::ValueHandle;
+using cobalt::script::Wrappable;
+
+using cobalt::script::CallbackFunction;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::ExceptionState;
+using cobalt::script::Wrappable;
+using cobalt::script::mozjs::FromJSValue;
+using cobalt::script::mozjs::InterfaceData;
+using cobalt::script::mozjs::MozjsCallbackFunction;
+using cobalt::script::mozjs::MozjsExceptionState;
+using cobalt::script::mozjs::MozjsGlobalEnvironment;
+using cobalt::script::mozjs::MozjsPropertyEnumerator;
+using cobalt::script::mozjs::MozjsUserObjectHolder;
+using cobalt::script::mozjs::ProxyHandler;
+using cobalt::script::mozjs::ToJSValue;
+using cobalt::script::mozjs::TypeTraits;
+using cobalt::script::mozjs::WrapperFactory;
+using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
+using cobalt::script::mozjs::kConversionFlagNullable;
+using cobalt::script::mozjs::kConversionFlagRestricted;
+using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
+using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
+using cobalt::script::mozjs::kNoConversionFlags;
+}  // namespace
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+namespace {
+
+class MozjsPromiseInterfaceHandler : public ProxyHandler {
+ public:
+  MozjsPromiseInterfaceHandler()
+      : ProxyHandler(indexed_property_hooks, named_property_hooks) {}
+
+ private:
+  static NamedPropertyHooks named_property_hooks;
+  static IndexedPropertyHooks indexed_property_hooks;
+};
+
+ProxyHandler::NamedPropertyHooks
+MozjsPromiseInterfaceHandler::named_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+ProxyHandler::IndexedPropertyHooks
+MozjsPromiseInterfaceHandler::indexed_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+
+static base::LazyInstance<MozjsPromiseInterfaceHandler>
+    proxy_handler;
+
+JSBool HasInstance(JSContext *context, JS::HandleObject type,
+                   JS::MutableHandleValue vp, JSBool *success) {
+  JS::RootedObject global_object(
+      context, JS_GetGlobalForObject(context, type));
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(
+      context, MozjsPromiseInterface::GetPrototype(context, global_object));
+
+  // |IsDelegate| walks the prototype chain of an object returning true if
+  // .prototype is found.
+  bool is_delegate;
+  if (!IsDelegate(context, prototype, vp, &is_delegate)) {
+    *success = false;
+    return false;
+  }
+
+  *success = is_delegate;
+  return true;
+}
+
+InterfaceData* CreateCachedInterfaceData() {
+  InterfaceData* interface_data = new InterfaceData();
+  memset(&interface_data->instance_class_definition, 0,
+         sizeof(interface_data->instance_class_definition));
+  memset(&interface_data->prototype_class_definition, 0,
+         sizeof(interface_data->prototype_class_definition));
+  memset(&interface_data->interface_object_class_definition, 0,
+         sizeof(interface_data->interface_object_class_definition));
+
+  JSClass* instance_class = &interface_data->instance_class_definition;
+  const int kGlobalFlags = 0;
+  instance_class->name = "PromiseInterface";
+  instance_class->flags = kGlobalFlags | JSCLASS_HAS_PRIVATE;
+  instance_class->addProperty = JS_PropertyStub;
+  instance_class->delProperty = JS_DeletePropertyStub;
+  instance_class->getProperty = JS_PropertyStub;
+  instance_class->setProperty = JS_StrictPropertyStub;
+  instance_class->enumerate = JS_EnumerateStub;
+  instance_class->resolve = JS_ResolveStub;
+  instance_class->convert = JS_ConvertStub;
+  // Function to be called before on object of this class is garbage collected.
+  instance_class->finalize = &WrapperPrivate::Finalizer;
+  // Called to trace objects that can be referenced from this object.
+  instance_class->trace = &WrapperPrivate::Trace;
+
+  JSClass* prototype_class = &interface_data->prototype_class_definition;
+  prototype_class->name = "PromiseInterfacePrototype";
+  prototype_class->flags = 0;
+  prototype_class->addProperty = JS_PropertyStub;
+  prototype_class->delProperty = JS_DeletePropertyStub;
+  prototype_class->getProperty = JS_PropertyStub;
+  prototype_class->setProperty = JS_StrictPropertyStub;
+  prototype_class->enumerate = JS_EnumerateStub;
+  prototype_class->resolve = JS_ResolveStub;
+  prototype_class->convert = JS_ConvertStub;
+
+  JSClass* interface_object_class =
+      &interface_data->interface_object_class_definition;
+  interface_object_class->name = "PromiseInterfaceConstructor";
+  interface_object_class->flags = 0;
+  interface_object_class->addProperty = JS_PropertyStub;
+  interface_object_class->delProperty = JS_DeletePropertyStub;
+  interface_object_class->getProperty = JS_PropertyStub;
+  interface_object_class->setProperty = JS_StrictPropertyStub;
+  interface_object_class->enumerate = JS_EnumerateStub;
+  interface_object_class->resolve = JS_ResolveStub;
+  interface_object_class->convert = JS_ConvertStub;
+  interface_object_class->hasInstance = &HasInstance;
+  return interface_data;
+}
+
+JSBool fcn_onSuccess(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  impl->OnSuccess();
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
+JSBool fcn_returnBooleanPromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnBooleanPromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+JSBool fcn_returnInterfacePromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnInterfacePromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+JSBool fcn_returnStringPromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnStringPromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+JSBool fcn_returnVoidPromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnVoidPromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+
+const JSPropertySpec prototype_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec prototype_functions[] = {
+  {
+      "onSuccess",
+      JSOP_WRAPPER(&fcn_onSuccess),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
+      "returnBooleanPromise",
+      JSOP_WRAPPER(&fcn_returnBooleanPromise),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
+      "returnInterfacePromise",
+      JSOP_WRAPPER(&fcn_returnInterfacePromise),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
+      "returnStringPromise",
+      JSOP_WRAPPER(&fcn_returnStringPromise),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
+      "returnVoidPromise",
+      JSOP_WRAPPER(&fcn_returnVoidPromise),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  JS_FS_END
+};
+
+const JSPropertySpec interface_object_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec interface_object_functions[] = {
+  JS_FS_END
+};
+
+const JSPropertySpec own_properties[] = {
+  JS_PS_END
+};
+
+void InitializePrototypeAndInterfaceObject(
+    InterfaceData* interface_data, JSContext* context,
+    JS::HandleObject global_object) {
+  DCHECK(!interface_data->prototype);
+  DCHECK(!interface_data->interface_object);
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  JS::RootedObject parent_prototype(
+      context, JS_GetObjectPrototype(context, global_object));
+  DCHECK(parent_prototype);
+
+  // Create the Prototype object.
+  interface_data->prototype = JS_NewObjectWithGivenProto(
+      context, &interface_data->prototype_class_definition, parent_prototype,
+      NULL);
+  bool success = JS_DefineProperties(
+      context, interface_data->prototype, prototype_properties);
+  DCHECK(success);
+  success = JS_DefineFunctions(
+      context, interface_data->prototype, prototype_functions);
+  DCHECK(success);
+
+  JS::RootedObject function_prototype(
+      context, JS_GetFunctionPrototype(context, global_object));
+  DCHECK(function_prototype);
+  // Create the Interface object.
+  interface_data->interface_object = JS_NewObjectWithGivenProto(
+      context, &interface_data->interface_object_class_definition,
+      function_prototype, NULL);
+
+  // Add the InterfaceObject.name property.
+  JS::RootedObject rooted_interface_object(
+      context, interface_data->interface_object);
+  JS::RootedValue name_value(context);
+  const char name[] =
+      "PromiseInterface";
+  name_value.setString(JS_NewStringCopyZ(context, name));
+  success =
+      JS_DefineProperty(context, rooted_interface_object, "name", name_value,
+                        JS_PropertyStub, JS_StrictPropertyStub,
+                        JSPROP_READONLY);
+  DCHECK(success);
+
+  // Define interface object properties (including constants).
+  success = JS_DefineProperties(context, rooted_interface_object,
+                                interface_object_properties);
+  DCHECK(success);
+  // Define interface object functions (static).
+  success = JS_DefineFunctions(context, rooted_interface_object,
+                               interface_object_functions);
+  DCHECK(success);
+
+
+  // Set the Prototype.constructor and Constructor.prototype properties.
+  DCHECK(interface_data->interface_object);
+  DCHECK(interface_data->prototype);
+  JS::RootedObject rooted_prototype(context, interface_data->prototype);
+  success = JS_LinkConstructorAndPrototype(
+      context,
+      rooted_interface_object,
+      rooted_prototype);
+  DCHECK(success);
+}
+
+InterfaceData* GetInterfaceData(JSContext* context) {
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  // Use the address of the properties definition for this interface as a
+  // unique key for looking up the InterfaceData for this interface.
+  intptr_t key = reinterpret_cast<intptr_t>(&own_properties);
+  InterfaceData* interface_data = global_environment->GetInterfaceData(key);
+  if (!interface_data) {
+    interface_data = CreateCachedInterfaceData();
+    DCHECK(interface_data);
+    global_environment->CacheInterfaceData(key, interface_data);
+    DCHECK_EQ(interface_data, global_environment->GetInterfaceData(key));
+  }
+  return interface_data;
+}
+
+}  // namespace
+
+// static
+JSObject* MozjsPromiseInterface::CreateProxy(
+    JSContext* context, const scoped_refptr<Wrappable>& wrappable) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  DCHECK(prototype);
+  JS::RootedObject new_object(context, JS_NewObjectWithGivenProto(
+      context, &interface_data->instance_class_definition, prototype, NULL));
+  DCHECK(new_object);
+  JS::RootedObject proxy(context,
+      ProxyHandler::NewProxy(context, new_object, prototype, NULL,
+                             proxy_handler.Pointer()));
+  WrapperPrivate::AddPrivateData(context, proxy, wrappable);
+  return proxy;
+}
+
+//static
+const JSClass* MozjsPromiseInterface::PrototypeClass(
+      JSContext* context) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  JSClass* proto_class = JS_GetClass(*prototype.address());
+  return proto_class;
+}
+
+// static
+JSObject* MozjsPromiseInterface::GetPrototype(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->prototype) {
+    // Create new prototype that has all the props and methods
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->prototype);
+  return interface_data->prototype;
+}
+
+// static
+JSObject* MozjsPromiseInterface::GetInterfaceObject(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->interface_object) {
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->interface_object);
+  return interface_data->interface_object;
+}
+
+
+namespace {
+}  // namespace
+
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.h
new file mode 100644
index 0000000..6c2e10f
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.h
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/interface.h.template
+
+#ifndef MozjsPromiseInterface_h
+#define MozjsPromiseInterface_h
+
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/bindings/testing/promise_interface.h"
+
+#include "third_party/mozjs/js/src/jsapi.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class MozjsPromiseInterface {
+ public:
+  static JSObject* CreateProxy(JSContext* context,
+      const scoped_refptr<script::Wrappable>& wrappable);
+  static const JSClass* PrototypeClass(JSContext* context);
+  static JSObject* GetPrototype(JSContext* context,
+                                JS::HandleObject global_object);
+  static JSObject* GetInterfaceObject(JSContext* context,
+                                      JS::HandleObject global_object);
+};
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
+
+#endif  // MozjsPromiseInterface_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
index 9560be3..20c4381 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
index 8051748..1efad36 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
index defc7f6..e20ac1c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
@@ -38,6 +38,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
index e97e764..c475463 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
index c74b794..8f8c7ba 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
index cb722e4..0901b3e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
index c8c9311..4d48366 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
@@ -36,6 +36,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
index 9fae514..8f4cb24 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -40,6 +40,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
index 2393d9a..07d98af 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -90,6 +90,7 @@
 #include "cobalt/bindings/testing/mozjs_numeric_types_test_interface.h"
 #include "cobalt/bindings/testing/mozjs_object_type_bindings_interface.h"
 #include "cobalt/bindings/testing/mozjs_operations_test_interface.h"
+#include "cobalt/bindings/testing/mozjs_promise_interface.h"
 #include "cobalt/bindings/testing/mozjs_put_forwards_interface.h"
 #include "cobalt/bindings/testing/mozjs_sequence_user.h"
 #include "cobalt/bindings/testing/mozjs_single_operation_interface.h"
@@ -110,6 +111,7 @@
 #include "cobalt/bindings/testing/numeric_types_test_interface.h"
 #include "cobalt/bindings/testing/object_type_bindings_interface.h"
 #include "cobalt/bindings/testing/operations_test_interface.h"
+#include "cobalt/bindings/testing/promise_interface.h"
 #include "cobalt/bindings/testing/put_forwards_interface.h"
 #include "cobalt/bindings/testing/sequence_user.h"
 #include "cobalt/bindings/testing/single_operation_interface.h"
@@ -132,6 +134,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
@@ -218,6 +221,7 @@
 using cobalt::bindings::testing::MozjsNumericTypesTestInterface;
 using cobalt::bindings::testing::MozjsObjectTypeBindingsInterface;
 using cobalt::bindings::testing::MozjsOperationsTestInterface;
+using cobalt::bindings::testing::MozjsPromiseInterface;
 using cobalt::bindings::testing::MozjsPutForwardsInterface;
 using cobalt::bindings::testing::MozjsSequenceUser;
 using cobalt::bindings::testing::MozjsSingleOperationInterface;
@@ -238,6 +242,7 @@
 using cobalt::bindings::testing::NumericTypesTestInterface;
 using cobalt::bindings::testing::ObjectTypeBindingsInterface;
 using cobalt::bindings::testing::OperationsTestInterface;
+using cobalt::bindings::testing::PromiseInterface;
 using cobalt::bindings::testing::PutForwardsInterface;
 using cobalt::bindings::testing::SequenceUser;
 using cobalt::bindings::testing::SingleOperationInterface;
@@ -653,6 +658,114 @@
   return !exception_state.is_exception_set();
 }
 
+JSBool fcn_setTimeout(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsWindow::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<Window>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  Window* impl =
+      wrapper_private->wrappable<Window>().get();
+  const size_t kMinArguments = 1;
+  if (args.length() < kMinArguments) {
+    exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+    return false;
+  }
+  // Non-optional arguments
+  TypeTraits<Window::TimerCallback >::ConversionType handler;
+  // Optional arguments
+  TypeTraits<int32_t >::ConversionType timeout;
+
+  DCHECK_LT(0, args.length());
+  JS::RootedValue non_optional_value0(
+      context, args[0]);
+  FromJSValue(context,
+              non_optional_value0,
+              kNoConversionFlags,
+              &exception_state, &handler);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+  size_t num_set_arguments = 1;
+  if (args.length() > 1) {
+    JS::RootedValue optional_value0(
+        context, args[1]);
+    FromJSValue(context,
+                optional_value0,
+                kNoConversionFlags,
+                &exception_state,
+                &timeout);
+    if (exception_state.is_exception_set()) {
+      return false;
+    }
+    ++num_set_arguments;
+  }
+  switch (num_set_arguments) {
+    case 1:
+      {
+          if (!exception_state.is_exception_set()) {
+            ToJSValue(context,
+                      impl->SetTimeout(handler),
+                      &result_value);
+          }
+          if (!exception_state.is_exception_set()) {
+            args.rval().set(result_value);
+          }
+          return !exception_state.is_exception_set();
+      }
+      break;
+    case 2:
+      {
+          if (!exception_state.is_exception_set()) {
+            ToJSValue(context,
+                      impl->SetTimeout(handler, timeout),
+                      &result_value);
+          }
+          if (!exception_state.is_exception_set()) {
+            args.rval().set(result_value);
+          }
+          return !exception_state.is_exception_set();
+      }
+      break;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
 JSBool fcn_windowOperation(
     JSContext* context, uint32_t argc, JS::Value *vp) {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -732,6 +845,13 @@
       NULL,
   },
   {
+      "setTimeout",
+      JSOP_WRAPPER(&fcn_setTimeout),
+      1,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
       "windowOperation",
       JSOP_WRAPPER(&fcn_windowOperation),
       0,
@@ -1098,6 +1218,10 @@
       base::Bind(MozjsOperationsTestInterface::CreateProxy),
       base::Bind(MozjsOperationsTestInterface::PrototypeClass));
   wrapper_factory->RegisterWrappableType(
+      PromiseInterface::PromiseInterfaceWrappableType(),
+      base::Bind(MozjsPromiseInterface::CreateProxy),
+      base::Bind(MozjsPromiseInterface::PrototypeClass));
+  wrapper_factory->RegisterWrappableType(
       PutForwardsInterface::PutForwardsInterfaceWrappableType(),
       base::Bind(MozjsPutForwardsInterface::CreateProxy),
       base::Bind(MozjsPutForwardsInterface::PrototypeClass));
@@ -1136,17 +1260,5 @@
 
 }
 
-// MSVS compiler does not need this explicit instantiation, and generates a
-// compiler error.
-#if !defined(_MSC_VER)
-// Explicitly instantiate the template function for template type Window
-// This is needed to prevent link errors when trying to resolve the template
-// instantiation.
-template
-void GlobalEnvironment::CreateGlobalObject<Window>(
-    const scoped_refptr<Window>& global_interface,
-    EnvironmentSettings* environment_settings);
-#endif
-
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
new file mode 100644
index 0000000..b918e34
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -0,0 +1,628 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/interface.cc.template
+
+#include "cobalt/bindings/testing/mozjs_promise_interface.h"
+
+#include "base/debug/trace_event.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/opaque_handle.h"
+#include "cobalt/script/script_value.h"
+
+#include "base/lazy_instance.h"
+#include "cobalt/script/exception_state.h"
+#include "cobalt/script/mozjs-45/callback_function_conversion.h"
+#include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "cobalt/script/mozjs-45/mozjs_callback_function.h"
+#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_global_environment.h"
+#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
+#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/proxy_handler.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/wrapper_factory.h"
+#include "cobalt/script/mozjs-45/wrapper_private.h"
+#include "cobalt/script/property_enumerator.h"
+#include "cobalt/script/sequence.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "third_party/mozjs-45/js/src/jsfriendapi.h"
+
+namespace {
+using cobalt::bindings::testing::PromiseInterface;
+using cobalt::bindings::testing::MozjsPromiseInterface;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::GlobalEnvironment;
+using cobalt::script::OpaqueHandle;
+using cobalt::script::OpaqueHandleHolder;
+using cobalt::script::ScriptValue;
+using cobalt::script::ValueHandle;
+using cobalt::script::Wrappable;
+
+using cobalt::script::CallbackFunction;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::ExceptionState;
+using cobalt::script::Wrappable;
+using cobalt::script::mozjs::FromJSValue;
+using cobalt::script::mozjs::InterfaceData;
+using cobalt::script::mozjs::MozjsCallbackFunction;
+using cobalt::script::mozjs::MozjsExceptionState;
+using cobalt::script::mozjs::MozjsGlobalEnvironment;
+using cobalt::script::mozjs::MozjsPropertyEnumerator;
+using cobalt::script::mozjs::MozjsUserObjectHolder;
+using cobalt::script::mozjs::ProxyHandler;
+using cobalt::script::mozjs::ToJSValue;
+using cobalt::script::mozjs::TypeTraits;
+using cobalt::script::mozjs::WrapperFactory;
+using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagNullable;
+using cobalt::script::mozjs::kConversionFlagRestricted;
+using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
+using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
+using cobalt::script::mozjs::kNoConversionFlags;
+}  // namespace
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+namespace {
+
+class MozjsPromiseInterfaceHandler : public ProxyHandler {
+ public:
+  MozjsPromiseInterfaceHandler()
+      : ProxyHandler(indexed_property_hooks, named_property_hooks) {}
+
+ private:
+  static NamedPropertyHooks named_property_hooks;
+  static IndexedPropertyHooks indexed_property_hooks;
+};
+
+ProxyHandler::NamedPropertyHooks
+MozjsPromiseInterfaceHandler::named_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+ProxyHandler::IndexedPropertyHooks
+MozjsPromiseInterfaceHandler::indexed_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+
+static base::LazyInstance<MozjsPromiseInterfaceHandler>
+    proxy_handler;
+
+bool HasInstance(JSContext *context, JS::HandleObject type,
+                   JS::MutableHandleValue vp, bool *success) {
+  JS::RootedObject global_object(
+      context, JS_GetGlobalForObject(context, type));
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(
+      context, MozjsPromiseInterface::GetPrototype(context, global_object));
+
+  // |IsDelegate| walks the prototype chain of an object returning true if
+  // .prototype is found.
+  bool is_delegate;
+  if (!IsDelegate(context, prototype, vp, &is_delegate)) {
+    *success = false;
+    return false;
+  }
+
+  *success = is_delegate;
+  return true;
+}
+
+const JSClass instance_class_definition = {
+    "PromiseInterface",
+    0 | JSCLASS_HAS_PRIVATE,
+    NULL,  // addProperty
+    NULL,  // delProperty
+    NULL,  // getProperty
+    NULL,  // setProperty
+    NULL,  // enumerate
+    NULL,  // resolve
+    NULL,  // mayResolve
+    &WrapperPrivate::Finalizer,  // finalize
+    NULL,  // call
+    NULL,  // hasInstance
+    NULL,  // construct
+    &WrapperPrivate::Trace,  // trace
+};
+
+const JSClass prototype_class_definition = {
+    "PromiseInterfacePrototype",
+};
+
+const JSClass interface_object_class_definition = {
+    "PromiseInterfaceConstructor",
+    0,
+    NULL,  // addProperty
+    NULL,  // delProperty
+    NULL,  // getProperty
+    NULL,  // setProperty
+    NULL,  // enumerate
+    NULL,  // resolve
+    NULL,  // mayResolve
+    NULL,  // finalize
+    NULL,  // call
+    &HasInstance,
+    NULL,
+};
+
+bool fcn_onSuccess(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  impl->OnSuccess();
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
+bool fcn_returnBooleanPromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnBooleanPromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+bool fcn_returnInterfacePromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnInterfacePromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+bool fcn_returnStringPromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnStringPromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+bool fcn_returnVoidPromise(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsPromiseInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<PromiseInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  PromiseInterface* impl =
+      wrapper_private->wrappable<PromiseInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->ReturnVoidPromise(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+
+
+const JSPropertySpec prototype_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec prototype_functions[] = {
+  JS_FNSPEC(
+      "onSuccess", fcn_onSuccess, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "returnBooleanPromise", fcn_returnBooleanPromise, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "returnInterfacePromise", fcn_returnInterfacePromise, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "returnStringPromise", fcn_returnStringPromise, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "returnVoidPromise", fcn_returnVoidPromise, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FS_END
+};
+
+const JSPropertySpec interface_object_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec interface_object_functions[] = {
+  JS_FS_END
+};
+
+const JSPropertySpec own_properties[] = {
+  JS_PS_END
+};
+
+void InitializePrototypeAndInterfaceObject(
+    InterfaceData* interface_data, JSContext* context,
+    JS::HandleObject global_object) {
+  DCHECK(!interface_data->prototype);
+  DCHECK(!interface_data->interface_object);
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  JS::RootedObject parent_prototype(
+      context, JS_GetObjectPrototype(context, global_object));
+  DCHECK(parent_prototype);
+
+  interface_data->prototype = JS_NewObjectWithGivenProto(
+    context, &prototype_class_definition, parent_prototype
+  );
+
+  JS::RootedObject rooted_prototype(context, interface_data->prototype);
+  bool success = JS_DefineProperties(
+      context,
+      rooted_prototype,
+      prototype_properties);
+
+  DCHECK(success);
+  success = JS_DefineFunctions(
+      context, rooted_prototype, prototype_functions);
+  DCHECK(success);
+
+  JS::RootedObject function_prototype(
+      context, JS_GetFunctionPrototype(context, global_object));
+  DCHECK(function_prototype);
+  // Create the Interface object.
+  interface_data->interface_object = JS_NewObjectWithGivenProto(
+      context, &interface_object_class_definition,
+      function_prototype);
+
+  // Add the InterfaceObject.name property.
+  JS::RootedObject rooted_interface_object(
+      context, interface_data->interface_object);
+  JS::RootedValue name_value(context);
+  const char name[] =
+      "PromiseInterface";
+  name_value.setString(JS_NewStringCopyZ(context, name));
+  success = JS_DefineProperty(
+      context, rooted_interface_object, "name", name_value, JSPROP_READONLY,
+      NULL, NULL);
+  DCHECK(success);
+
+  // Define interface object properties (including constants).
+  success = JS_DefineProperties(context, rooted_interface_object,
+                                interface_object_properties);
+  DCHECK(success);
+  // Define interface object functions (static).
+  success = JS_DefineFunctions(context, rooted_interface_object,
+                               interface_object_functions);
+  DCHECK(success);
+
+  // Set the Prototype.constructor and Constructor.prototype properties.
+  DCHECK(interface_data->interface_object);
+  DCHECK(interface_data->prototype);
+  success = JS_LinkConstructorAndPrototype(
+      context,
+      rooted_interface_object,
+      rooted_prototype);
+  DCHECK(success);
+}
+
+InterfaceData* GetInterfaceData(JSContext* context) {
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  // Use the address of the properties definition for this interface as a
+  // unique key for looking up the InterfaceData for this interface.
+  intptr_t key = reinterpret_cast<intptr_t>(&own_properties);
+  InterfaceData* interface_data = global_environment->GetInterfaceData(key);
+  if (!interface_data) {
+    interface_data = new InterfaceData();
+    DCHECK(interface_data);
+    global_environment->CacheInterfaceData(key, interface_data);
+    DCHECK_EQ(interface_data, global_environment->GetInterfaceData(key));
+  }
+  return interface_data;
+}
+
+}  // namespace
+
+// static
+JSObject* MozjsPromiseInterface::CreateProxy(
+    JSContext* context, const scoped_refptr<Wrappable>& wrappable) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  DCHECK(prototype);
+  JS::RootedObject new_object(
+      context,
+      JS_NewObjectWithGivenProto(
+          context, &instance_class_definition, prototype));
+  DCHECK(new_object);
+  JS::RootedObject proxy(context,
+      ProxyHandler::NewProxy(
+          context, proxy_handler.Pointer(), new_object, prototype));
+  WrapperPrivate::AddPrivateData(context, proxy, wrappable);
+  return proxy;
+}
+
+// static
+const JSClass* MozjsPromiseInterface::PrototypeClass(
+      JSContext* context) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  const JSClass* proto_class = JS_GetClass(prototype);
+  return proto_class;
+}
+
+// static
+JSObject* MozjsPromiseInterface::GetPrototype(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->prototype) {
+    // Create new prototype that has all the props and methods
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->prototype);
+  return interface_data->prototype;
+}
+
+// static
+JSObject* MozjsPromiseInterface::GetInterfaceObject(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->interface_object) {
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->interface_object);
+  return interface_data->interface_object;
+}
+
+
+namespace {
+}  // namespace
+
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.h
new file mode 100644
index 0000000..1b8d2f1
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.h
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/interface.h.template
+
+#ifndef MozjsPromiseInterface_h
+#define MozjsPromiseInterface_h
+
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/bindings/testing/promise_interface.h"
+
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class MozjsPromiseInterface {
+ public:
+  static JSObject* CreateProxy(JSContext* context,
+      const scoped_refptr<script::Wrappable>& wrappable);
+  static const JSClass* PrototypeClass(JSContext* context);
+  static JSObject* GetPrototype(JSContext* context,
+                                JS::HandleObject global_object);
+  static JSObject* GetInterfaceObject(JSContext* context,
+                                      JS::HandleObject global_object);
+};
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
+
+#endif  // MozjsPromiseInterface_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
index 094e325..a16151c 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -90,6 +90,7 @@
 #include "cobalt/bindings/testing/mozjs_numeric_types_test_interface.h"
 #include "cobalt/bindings/testing/mozjs_object_type_bindings_interface.h"
 #include "cobalt/bindings/testing/mozjs_operations_test_interface.h"
+#include "cobalt/bindings/testing/mozjs_promise_interface.h"
 #include "cobalt/bindings/testing/mozjs_put_forwards_interface.h"
 #include "cobalt/bindings/testing/mozjs_sequence_user.h"
 #include "cobalt/bindings/testing/mozjs_single_operation_interface.h"
@@ -110,6 +111,7 @@
 #include "cobalt/bindings/testing/numeric_types_test_interface.h"
 #include "cobalt/bindings/testing/object_type_bindings_interface.h"
 #include "cobalt/bindings/testing/operations_test_interface.h"
+#include "cobalt/bindings/testing/promise_interface.h"
 #include "cobalt/bindings/testing/put_forwards_interface.h"
 #include "cobalt/bindings/testing/sequence_user.h"
 #include "cobalt/bindings/testing/single_operation_interface.h"
@@ -217,6 +219,7 @@
 using cobalt::bindings::testing::MozjsNumericTypesTestInterface;
 using cobalt::bindings::testing::MozjsObjectTypeBindingsInterface;
 using cobalt::bindings::testing::MozjsOperationsTestInterface;
+using cobalt::bindings::testing::MozjsPromiseInterface;
 using cobalt::bindings::testing::MozjsPutForwardsInterface;
 using cobalt::bindings::testing::MozjsSequenceUser;
 using cobalt::bindings::testing::MozjsSingleOperationInterface;
@@ -237,6 +240,7 @@
 using cobalt::bindings::testing::NumericTypesTestInterface;
 using cobalt::bindings::testing::ObjectTypeBindingsInterface;
 using cobalt::bindings::testing::OperationsTestInterface;
+using cobalt::bindings::testing::PromiseInterface;
 using cobalt::bindings::testing::PutForwardsInterface;
 using cobalt::bindings::testing::SequenceUser;
 using cobalt::bindings::testing::SingleOperationInterface;
@@ -655,6 +659,114 @@
   return !exception_state.is_exception_set();
 }
 
+bool fcn_setTimeout(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsWindow::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<Window>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  Window* impl =
+      wrapper_private->wrappable<Window>().get();
+  const size_t kMinArguments = 1;
+  if (args.length() < kMinArguments) {
+    exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+    return false;
+  }
+  // Non-optional arguments
+  TypeTraits<Window::TimerCallback >::ConversionType handler;
+  // Optional arguments
+  TypeTraits<int32_t >::ConversionType timeout;
+
+  DCHECK_LT(0, args.length());
+  JS::RootedValue non_optional_value0(
+      context, args[0]);
+  FromJSValue(context,
+              non_optional_value0,
+              kNoConversionFlags,
+              &exception_state, &handler);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+  size_t num_set_arguments = 1;
+  if (args.length() > 1) {
+    JS::RootedValue optional_value0(
+        context, args[1]);
+    FromJSValue(context,
+                optional_value0,
+                kNoConversionFlags,
+                &exception_state,
+                &timeout);
+    if (exception_state.is_exception_set()) {
+      return false;
+    }
+    ++num_set_arguments;
+  }
+  switch (num_set_arguments) {
+    case 1:
+      {
+          if (!exception_state.is_exception_set()) {
+            ToJSValue(context,
+                      impl->SetTimeout(handler),
+                      &result_value);
+          }
+          if (!exception_state.is_exception_set()) {
+            args.rval().set(result_value);
+          }
+          return !exception_state.is_exception_set();
+      }
+      break;
+    case 2:
+      {
+          if (!exception_state.is_exception_set()) {
+            ToJSValue(context,
+                      impl->SetTimeout(handler, timeout),
+                      &result_value);
+          }
+          if (!exception_state.is_exception_set()) {
+            args.rval().set(result_value);
+          }
+          return !exception_state.is_exception_set();
+      }
+      break;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
 bool fcn_windowOperation(
     JSContext* context, uint32_t argc, JS::Value *vp) {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -731,6 +843,9 @@
       "getStackTrace", fcn_getStackTrace, NULL,
       0, JSPROP_ENUMERATE, NULL),
   JS_FNSPEC(
+      "setTimeout", fcn_setTimeout, NULL,
+      1, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
       "windowOperation", fcn_windowOperation, NULL,
       0, JSPROP_ENUMERATE, NULL),
   JS_FS_END
@@ -1100,6 +1215,10 @@
       base::Bind(MozjsOperationsTestInterface::CreateProxy),
       base::Bind(MozjsOperationsTestInterface::PrototypeClass));
   wrapper_factory->RegisterWrappableType(
+      PromiseInterface::PromiseInterfaceWrappableType(),
+      base::Bind(MozjsPromiseInterface::CreateProxy),
+      base::Bind(MozjsPromiseInterface::PrototypeClass));
+  wrapper_factory->RegisterWrappableType(
       PutForwardsInterface::PutForwardsInterfaceWrappableType(),
       base::Bind(MozjsPutForwardsInterface::CreateProxy),
       base::Bind(MozjsPutForwardsInterface::PrototypeClass));
@@ -1138,17 +1257,5 @@
 
 }
 
-// MSVS compiler does not need this explicit instantiation, and generates a
-// compiler error.
-#if !defined(_MSC_VER)
-// Explicitly instantiate the template function for template type Window
-// This is needed to prevent link errors when trying to resolve the template
-// instantiation.
-template
-void GlobalEnvironment::CreateGlobalObject<Window>(
-    const scoped_refptr<Window>& global_interface,
-    EnvironmentSettings* environment_settings);
-#endif
-
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template
index d740e9c..92af4f7 100644
--- a/src/cobalt/bindings/mozjs/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -37,6 +37,7 @@
 #include "cobalt/script/mozjs/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs/mozjs_user_object_holder.h"
 #include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
 #include "cobalt/script/mozjs/type_traits.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
diff --git a/src/cobalt/bindings/templates/interface-base.cc.template b/src/cobalt/bindings/templates/interface-base.cc.template
index ae80b8f..dfe5675 100644
--- a/src/cobalt/bindings/templates/interface-base.cc.template
+++ b/src/cobalt/bindings/templates/interface-base.cc.template
@@ -101,18 +101,6 @@
 {% endblock create_global_object_impl %}
 }
 
-// MSVS compiler does not need this explicit instantiation, and generates a
-// compiler error.
-#if !defined(_MSC_VER)
-// Explicitly instantiate the template function for template type {{impl_class}}
-// This is needed to prevent link errors when trying to resolve the template
-// instantiation.
-template
-void GlobalEnvironment::CreateGlobalObject<{{impl_class}}>(
-    const scoped_refptr<{{impl_class}}>& global_interface,
-    EnvironmentSettings* environment_settings);
-#endif
-
 }  // namespace script
 }  // namespace cobalt
 {% endif %}
diff --git a/src/cobalt/bindings/testing/bindings_test_base.h b/src/cobalt/bindings/testing/bindings_test_base.h
index 45c17f5..f6e42a2 100644
--- a/src/cobalt/bindings/testing/bindings_test_base.h
+++ b/src/cobalt/bindings/testing/bindings_test_base.h
@@ -85,6 +85,8 @@
 
   void CollectGarbage() { engine_->CollectGarbage(); }
 
+  Window* window() { return window_.get(); }
+
  protected:
   const scoped_ptr<script::EnvironmentSettings> environment_settings_;
   const scoped_ptr<script::JavaScriptEngine> engine_;
diff --git a/src/cobalt/bindings/testing/promise_interface.h b/src/cobalt/bindings/testing/promise_interface.h
new file mode 100644
index 0000000..5942a58
--- /dev/null
+++ b/src/cobalt/bindings/testing/promise_interface.h
@@ -0,0 +1,53 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BINDINGS_TESTING_PROMISE_INTERFACE_H_
+#define COBALT_BINDINGS_TESTING_PROMISE_INTERFACE_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/bindings/testing/arbitrary_interface.h"
+#include "cobalt/script/promise.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/wrappable.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class PromiseInterface : public script::Wrappable {
+ public:
+  typedef script::ScriptValue<script::Promise<void> > VoidPromiseValue;
+  typedef script::ScriptValue<script::Promise<bool> > BooleanPromiseValue;
+  typedef script::ScriptValue<script::Promise<std::string> > StringPromiseValue;
+  typedef script::ScriptValue<script::Promise<
+      scoped_refptr<script::Wrappable> > > InterfacePromiseValue;
+
+  MOCK_METHOD0(ReturnVoidPromise, const VoidPromiseValue*());
+  MOCK_METHOD0(ReturnBooleanPromise, const BooleanPromiseValue*());
+  MOCK_METHOD0(ReturnStringPromise, const StringPromiseValue*());
+  MOCK_METHOD0(ReturnInterfacePromise, const InterfacePromiseValue*());
+
+  MOCK_METHOD0(OnSuccess, void());
+
+  DEFINE_WRAPPABLE_TYPE(PromiseInterface);
+};
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
+
+#endif  // COBALT_BINDINGS_TESTING_PROMISE_INTERFACE_H_
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/cobalt/bindings/testing/promise_interface.idl
similarity index 66%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/cobalt/bindings/testing/promise_interface.idl
index b75f65f..eabdb5c 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/cobalt/bindings/testing/promise_interface.idl
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
+interface PromiseInterface {
+  Promise<void> returnVoidPromise();
+  Promise<boolean> returnBooleanPromise();
+  Promise<DOMString> returnStringPromise();
+  Promise<ArbitraryInterface> returnInterfacePromise();
 
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
-}
+  // Helper function used in promise bindings tests to report success.
+  void onSuccess();
+};
diff --git a/src/cobalt/bindings/testing/promise_test.cc b/src/cobalt/bindings/testing/promise_test.cc
new file mode 100644
index 0000000..71cc0b1
--- /dev/null
+++ b/src/cobalt/bindings/testing/promise_test.cc
@@ -0,0 +1,190 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/bindings/testing/bindings_test_base.h"
+#include "cobalt/bindings/testing/exception_object_interface.h"
+#include "cobalt/bindings/testing/promise_interface.h"
+#include "cobalt/bindings/testing/script_object_owner.h"
+#include "cobalt/script/exception_message.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ContainsRegex;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::_;
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+namespace {
+// Simple implementation of window.setTimeout. window.setTimeout is needed for
+// the Promise polyfill used in SpiderMonkey 24.
+int32_t SetTimeoutFunction(const Window::TimerCallbackArg& timer_callback,
+                           int32_t /* timeout */) {
+  // Just execute immediately. This is sufficient for the tests below.
+  Window::TimerCallbackArg::StrongReference reference(timer_callback);
+  reference.value().Run();
+  return 1;
+}
+
+class PromiseTest : public InterfaceBindingsTest<PromiseInterface> {
+ protected:
+  void SetUp() {
+    window()->SetSetTimeoutHandler(base::Bind(&SetTimeoutFunction));
+  }
+};
+}  // namespace
+
+TEST_F(PromiseTest, ResolveVoidPromise) {
+  typedef PromiseInterface::VoidPromiseValue VoidPromiseValue;
+  scoped_ptr<VoidPromiseValue> promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<void>();
+  VoidPromiseValue::StrongReference reference(*promise);
+  EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get()));
+
+  EXPECT_TRUE(
+      EvaluateScript("var promise = test.returnVoidPromise();\n"
+                     "promise.then(function() { test.onSuccess(); })\n"));
+
+  EXPECT_CALL(test_mock(), OnSuccess());
+  reference.value().Resolve();
+}
+
+TEST_F(PromiseTest, RejectVoidPromise) {
+  typedef PromiseInterface::VoidPromiseValue VoidPromiseValue;
+  scoped_ptr<VoidPromiseValue> promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<void>();
+  VoidPromiseValue::StrongReference reference(*promise);
+  EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get()));
+
+  EXPECT_TRUE(
+      EvaluateScript("var promise = test.returnVoidPromise();\n"
+                     "promise.catch(function() { test.onSuccess(); })\n"));
+
+  EXPECT_CALL(test_mock(), OnSuccess());
+  reference.value().Reject();
+}
+
+TEST_F(PromiseTest, RejectWithExceptionObject) {
+  typedef PromiseInterface::VoidPromiseValue VoidPromiseValue;
+  scoped_ptr<VoidPromiseValue> promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<void>();
+  VoidPromiseValue::StrongReference reference(*promise);
+  EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get()));
+
+  EXPECT_TRUE(
+      EvaluateScript("var promise = test.returnVoidPromise();\n"
+                     "var onReject = function(error) {\n"
+                     "  if (error.message == 'apple') {\n"
+                     "    test.onSuccess();\n"
+                     "  }\n"
+                     "};\n"
+                     "promise.catch(onReject)\n"));
+  EXPECT_CALL(test_mock(), OnSuccess());
+  scoped_refptr<ExceptionObjectInterface> exception_object(
+      new ExceptionObjectInterface());
+  EXPECT_CALL(*exception_object, message()).WillOnce(Return("apple"));
+  reference.value().Reject(exception_object);
+}
+
+TEST_F(PromiseTest, RejectWithSimpleException) {
+  typedef PromiseInterface::VoidPromiseValue VoidPromiseValue;
+  scoped_ptr<VoidPromiseValue> promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<void>();
+  VoidPromiseValue::StrongReference reference(*promise);
+  EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get()));
+
+  EXPECT_TRUE(
+      EvaluateScript("var promise = test.returnVoidPromise();\n"
+                     "var onReject = function(error) {\n"
+                     "  if (error.name == 'TypeError') {\n"
+                     "    test.onSuccess();\n"
+                     "  }\n"
+                     "};\n"
+                     "promise.catch(onReject)\n"));
+  EXPECT_CALL(test_mock(), OnSuccess());
+  reference.value().Reject(script::kTypeError);
+}
+
+TEST_F(PromiseTest, BooleanPromise) {
+  typedef PromiseInterface::BooleanPromiseValue BooleanPromiseValue;
+  scoped_ptr<BooleanPromiseValue> promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<bool>();
+  BooleanPromiseValue::StrongReference reference(*promise);
+  EXPECT_CALL(test_mock(), ReturnBooleanPromise())
+      .WillOnce(Return(promise.get()));
+
+  EXPECT_TRUE(
+      EvaluateScript("var promise = test.returnBooleanPromise();\n"
+                     "var onFulfill = function(result) {\n"
+                     "  if (result === true) {\n"
+                     "    test.onSuccess();\n"
+                     "  }\n"
+                     "};\n"
+                     "promise.then(onFulfill)\n"));
+  EXPECT_CALL(test_mock(), OnSuccess());
+  reference.value().Resolve(true);
+}
+
+TEST_F(PromiseTest, StringPromise) {
+  typedef PromiseInterface::StringPromiseValue StringPromiseValue;
+  scoped_ptr<StringPromiseValue> promise =
+      global_environment_->script_value_factory()
+          ->CreateBasicPromise<std::string>();
+  StringPromiseValue::StrongReference reference(*promise);
+  EXPECT_CALL(test_mock(), ReturnStringPromise())
+      .WillOnce(Return(promise.get()));
+
+  EXPECT_TRUE(
+      EvaluateScript("var promise = test.returnStringPromise();\n"
+                     "var onFulfill = function(result) {\n"
+                     "  if (result == 'banana') {\n"
+                     "    test.onSuccess();\n"
+                     "  }\n"
+                     "};\n"
+                     "promise.then(onFulfill)\n"));
+  EXPECT_CALL(test_mock(), OnSuccess());
+  reference.value().Resolve("banana");
+}
+
+TEST_F(PromiseTest, InterfacePromise) {
+  typedef PromiseInterface::InterfacePromiseValue InterfacePromiseValue;
+  scoped_ptr<InterfacePromiseValue> promise =
+      global_environment_->script_value_factory()
+          ->CreateInterfacePromise<scoped_refptr<ArbitraryInterface> >();
+  InterfacePromiseValue::StrongReference reference(*promise);
+  EXPECT_CALL(test_mock(), ReturnInterfacePromise())
+      .WillOnce(Return(promise.get()));
+
+  EXPECT_TRUE(
+      EvaluateScript("var promise = test.returnInterfacePromise();\n"
+                     "var onFulfill = function(result) {\n"
+                     "  result.arbitraryFunction();\n"
+                     "  test.onSuccess();\n"
+                     "};\n"
+                     "promise.then(onFulfill)\n"));
+
+  scoped_refptr<ArbitraryInterface> result = new ArbitraryInterface();
+  EXPECT_CALL(*result.get(), ArbitraryFunction());
+  EXPECT_CALL(test_mock(), OnSuccess());
+  reference.value().Resolve(result);
+}
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp
index 96130d0..d005089 100644
--- a/src/cobalt/bindings/testing/testing.gyp
+++ b/src/cobalt/bindings/testing/testing.gyp
@@ -62,6 +62,7 @@
         'numeric_types_test_interface.idl',
         'object_type_bindings_interface.idl',
         'operations_test_interface.idl',
+        'promise_interface.idl',
         'put_forwards_interface.idl',
         'sequence_user.idl',
         'single_operation_interface.idl',
@@ -164,6 +165,7 @@
         'object_type_bindings_test.cc',
         'operations_bindings_test.cc',
         'optional_arguments_bindings_test.cc',
+        'promise_test.cc',
         'put_forwards_test.cc',
         'sequence_bindings_test.cc',
         'stack_trace_test.cc',
diff --git a/src/cobalt/bindings/testing/window.h b/src/cobalt/bindings/testing/window.h
index 89fe9e1..fd37054 100644
--- a/src/cobalt/bindings/testing/window.h
+++ b/src/cobalt/bindings/testing/window.h
@@ -20,6 +20,7 @@
 
 #include "cobalt/bindings/testing/global_interface_parent.h"
 #include "cobalt/bindings/testing/single_operation_interface.h"
+#include "cobalt/script/callback_function.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/global_environment.h"
 
@@ -29,7 +30,11 @@
 
 class Window : public GlobalInterfaceParent {
  public:
+  typedef script::CallbackFunction<void()> TimerCallback;
+  typedef script::ScriptValue<TimerCallback> TimerCallbackArg;
   typedef script::ScriptValue<SingleOperationInterface> TestEventHandler;
+  typedef base::Callback<int32_t(const TimerCallbackArg&, int32_t)>
+      SetTimeoutHandler;
 
   virtual void WindowOperation() {}
   virtual std::string window_property() { return ""; }
@@ -44,7 +49,26 @@
   void set_on_event(const TestEventHandler&) {}
   const TestEventHandler* on_event() { return NULL; }
 
+  // Stub implementation of window.setTimeout, which is needed for some tests.
+  int SetTimeout(const TimerCallbackArg& handler) {
+    return SetTimeout(handler, 0);
+  }
+
+  int32_t SetTimeout(const TimerCallbackArg& handler, int32_t timeout) {
+    if (!set_timeout_handler_.is_null()) {
+      return set_timeout_handler_.Run(handler, timeout);
+    }
+    return 0;
+  }
+
+  void SetSetTimeoutHandler(const SetTimeoutHandler& handler) {
+    set_timeout_handler_ = handler;
+  }
+
   DEFINE_WRAPPABLE_TYPE(Window);
+
+ private:
+  SetTimeoutHandler set_timeout_handler_;
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/testing/window.idl b/src/cobalt/bindings/testing/window.idl
index 6a0eff0..2cbfe51 100644
--- a/src/cobalt/bindings/testing/window.idl
+++ b/src/cobalt/bindings/testing/window.idl
@@ -20,8 +20,12 @@
 
   // Exercise a similar setup to EventHandler.
   attribute TestEventHandler onEvent;
+
+  // This is needed for the Promise polyfill.
+  long setTimeout(TimerCallback handler, optional long timeout);
 };
 
 // Exercise a similar setup to EventHandler.
 typedef SingleOperationInterface? TestEventHandler;
 
+callback TimerCallback = void();
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 4f3380c..a3d141a 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -62,6 +62,7 @@
         'COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO=<(image_cache_capacity_multiplier_when_playing_video)',
       ],
       'dependencies': [
+        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/accessibility/accessibility.gyp:accessibility',
         '<(DEPTH)/cobalt/account/account.gyp:account',
         '<(DEPTH)/cobalt/audio/audio.gyp:audio',
@@ -180,6 +181,7 @@
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/network/network.gyp:network',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/storage/storage.gyp:storage',
         '<(DEPTH)/cobalt/storage/storage.gyp:storage_upgrade_copy_test_data',
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index d161cf2..7572715 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -193,6 +193,12 @@
         '../speech/speech_recognition_result.idl',
         '../speech/speech_recognition_result_list.idl',
 
+        '../speech/speech_synthesis.idl',
+        '../speech/speech_synthesis_error_event.idl',
+        '../speech/speech_synthesis_event.idl',
+        '../speech/speech_synthesis_utterance.idl',
+        '../speech/speech_synthesis_voice.idl',
+
         '../web_animations/animatable.idl',
         '../web_animations/animation.idl',
         '../web_animations/animation_effect_read_only.idl',
@@ -242,6 +248,7 @@
         '../dom/non_element_parent_node.idl',
         '../dom/parent_node.idl',
         '../dom/performance__high_resolution_time.idl',
+        '../dom/speech_synthesis_getter.idl',
         '../dom/url_utils.idl',
         '../dom/window__animation_timing.idl',
         '../dom/window_cssom.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index be257e9..32c9309 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -19,11 +19,13 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/debug/trace_event.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "base/stl_util.h"
 #include "base/string_number_conversions.h"
 #include "base/string_split.h"
+#include "base/time.h"
 #include "cobalt/base/cobalt_paths.h"
 #include "cobalt/base/source_location.h"
 #include "cobalt/base/tokens.h"
@@ -36,11 +38,67 @@
 #include "cobalt/h5vcc/h5vcc.h"
 #include "cobalt/input/input_device_manager_fuzzer.h"
 #include "nb/memory_scope.h"
+#include "starboard/atomic.h"
+#include "starboard/configuration.h"
+#include "starboard/system.h"
+#include "starboard/time.h"
+
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+#include "starboard/ps4/core_dump_handler.h"
+#endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 
 namespace cobalt {
+
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+namespace timestamp {
+// This is a temporary workaround.
+extern SbAtomic64 g_last_render_timestamp;
+}  // namespace timestamp
+
+namespace {
+struct NonTrivialGlobalVariables {
+  NonTrivialGlobalVariables();
+
+  SbAtomic64* last_render_timestamp;
+};
+
+NonTrivialGlobalVariables::NonTrivialGlobalVariables() {
+  last_render_timestamp = &cobalt::timestamp::g_last_render_timestamp;
+  SbAtomicNoBarrier_Exchange64(last_render_timestamp,
+                               static_cast<SbAtomic64>(SbTimeGetNow()));
+}
+
+base::LazyInstance<NonTrivialGlobalVariables> non_trivial_global_variables =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+#endif
+
 namespace browser {
 namespace {
 
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+// Timeout for last render.
+const int kLastRenderTimeoutSeconds = 15;
+
+// Polling interval for timeout_polling_thread_.
+const int kRenderTimeOutPollingDelaySeconds = 1;
+
+// Minimum number of continuous times the timeout expirations. This is used to
+// prevent unintended behavior in situations such as when returning from
+// suspended state. Note that the timeout response trigger will be delayed
+// after the actual timeout expiration by this value times the polling delay.
+const int kMinimumContinuousRenderTimeoutExpirations = 2;
+
+// Name for timeout_polling_thread_.
+const char* kTimeoutPollingThreadName = "TimeoutPolling";
+
+// This specifies the percentage of calls to OnRenderTimeout() that result in a
+// call to OnError().
+const int kRenderTimeoutErrorPercentage = 99;
+
+#endif
+
 // This constant defines the maximum rate at which the layout engine will
 // refresh over time.  Since there is little benefit in performing a layout
 // faster than the display's refresh rate, we set this to 60Hz.
@@ -188,9 +246,29 @@
           h5vcc_url_handler_(this, system_window)),
       web_module_options_(options.web_module_options),
       has_resumed_(true, false),
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+      timeout_polling_thread_(kTimeoutPollingThreadName),
+      render_timeout_count_(0),
+#endif
       will_quit_(false),
       suspended_(false),
       system_window_(system_window) {
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+  SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
+  on_error_triggered_count_ = 0;
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+  recovery_mechanism_triggered_count_ = 0;
+  timeout_response_trigger_count_ = 0;
+#endif  // defined(COBALT_CHECK_RENDER_TIMEOUT)
+#endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+  timeout_polling_thread_.Start();
+  timeout_polling_thread_.message_loop()->PostDelayedTask(
+      FROM_HERE, base::Bind(&BrowserModule::OnPollForRenderTimeout,
+                            base::Unretained(this), url),
+      base::TimeDelta::FromSeconds(kRenderTimeOutPollingDelaySeconds));
+#endif
   TRACE_EVENT0("cobalt::browser", "BrowserModule::BrowserModule()");
   // All allocations for media will be tracked by "Media" memory scope.
   {
@@ -260,6 +338,9 @@
 
 BrowserModule::~BrowserModule() {
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+  SbCoreDumpUnregisterHandler(BrowserModule::CoreDumpHandler, this);
+#endif
 }
 
 void BrowserModule::Navigate(const GURL& url) {
@@ -280,6 +361,22 @@
       base::SourceLocation("[object BrowserModule]", 1, 1));
 }
 
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+// static
+void BrowserModule::CoreDumpHandler(void* browser_module_as_void) {
+  BrowserModule* browser_module =
+      static_cast<BrowserModule*>(browser_module_as_void);
+  SbCoreDumpLogInteger("BrowserModule.on_error_triggered_count_",
+                       browser_module->on_error_triggered_count_);
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+  SbCoreDumpLogInteger("BrowserModule.recovery_mechanism_triggered_count_",
+                       browser_module->recovery_mechanism_triggered_count_);
+  SbCoreDumpLogInteger("BrowserModule.timeout_response_trigger_count_",
+                       browser_module->timeout_response_trigger_count_);
+#endif  // defined(COBALT_CHECK_RENDER_TIMEOUT)
+}
+#endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+
 void BrowserModule::NavigateInternal(const GURL& url) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::NavigateInternal()");
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
@@ -543,6 +640,9 @@
 
 void BrowserModule::OnError(const GURL& url, const std::string& error) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnError()");
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+  on_error_triggered_count_++;
+#endif
   LOG(ERROR) << error;
   std::string url_string = "h5vcc://network-failure";
 
@@ -798,5 +898,52 @@
 }
 #endif  // OS_STARBOARD
 
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+void BrowserModule::OnPollForRenderTimeout(const GURL& url) {
+  SbTime last_render_timestamp = static_cast<SbTime>(SbAtomicAcquire_Load64(
+      non_trivial_global_variables.Get().last_render_timestamp));
+  base::Time last_render =
+      base::Time::FromSbTime(last_render_timestamp);
+  bool timeout_expiration =
+      base::Time::Now() -
+          base::TimeDelta::FromSeconds(kLastRenderTimeoutSeconds) >
+      last_render;
+  bool timeout_response_trigger = false;
+  if (timeout_expiration) {
+    // The timeout only triggers if the timeout expiration has been detected
+    // without interruption at least kMinimumContinuousRenderTimeoutExpirations
+    // times.
+    ++render_timeout_count_;
+    timeout_response_trigger =
+        render_timeout_count_ >= kMinimumContinuousRenderTimeoutExpirations;
+  } else {
+    render_timeout_count_ = 0;
+  }
+
+  if (timeout_response_trigger) {
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+    timeout_response_trigger_count_++;
+#endif
+    SbAtomicNoBarrier_Exchange64(
+        non_trivial_global_variables.Get().last_render_timestamp,
+        static_cast<SbAtomic64>(kSbTimeMax));
+    if (SbSystemGetRandomUInt64() <
+        kRenderTimeoutErrorPercentage * (UINT64_MAX / 100)) {
+      OnError(url, std::string("Rendering Timeout"));
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+      recovery_mechanism_triggered_count_++;
+#endif
+    } else {
+      SB_DLOG(INFO) << "Received OnRenderTimeout, ignoring by random chance.";
+    }
+  } else {
+    timeout_polling_thread_.message_loop()->PostDelayedTask(
+        FROM_HERE, base::Bind(&BrowserModule::OnPollForRenderTimeout,
+                              base::Unretained(this), url),
+        base::TimeDelta::FromSeconds(kRenderTimeOutPollingDelaySeconds));
+  }
+}
+#endif
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index bb48058..7acc494 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -21,6 +21,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
 #include "cobalt/account/account_manager.h"
 #include "cobalt/base/message_queue.h"
 #include "cobalt/browser/h5vcc_url_handler.h"
@@ -45,6 +46,7 @@
 #include "cobalt/browser/trace_manager.h"
 #include "cobalt/debug/debug_server.h"
 #endif  // ENABLE_DEBUG_CONSOLE
+#include "starboard/configuration.h"
 
 namespace cobalt {
 namespace browser {
@@ -121,6 +123,15 @@
   void Resume();
 
  private:
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+  static void CoreDumpHandler(void* browser_module_as_void);
+  int on_error_triggered_count_;
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+  int recovery_mechanism_triggered_count_;
+  int timeout_response_trigger_count_;
+#endif  // defined(COBALT_CHECK_RENDER_TIMEOUT)
+#endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+
   // Recreates web module with the given URL.
   void NavigateInternal(const GURL& url);
 
@@ -210,6 +221,11 @@
   // Process all messages queued into the |render_tree_submission_queue_|.
   void ProcessRenderTreeSubmissionQueue();
 
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+  // Poll for render timeout. Called from timeout_polling_thread_.
+  void OnPollForRenderTimeout(const GURL& url);
+#endif
+
   // 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
@@ -326,6 +342,14 @@
   // Reset when the browser is paused, signalled to resume.
   base::WaitableEvent has_resumed_;
 
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+  base::Thread timeout_polling_thread_;
+
+  // Counts the number of continuous render timeout expirations. This value is
+  // updated and used from OnPollForRenderTimeout.
+  int render_timeout_count_;
+#endif
+
   // Set when the application is about to quit. May be set from a thread other
   // than the one hosting this object, and read from another.
   bool will_quit_;
diff --git a/src/cobalt/browser/testdata/speech-synthesis-demo/index.html b/src/cobalt/browser/testdata/speech-synthesis-demo/index.html
new file mode 100644
index 0000000..5eb715f
--- /dev/null
+++ b/src/cobalt/browser/testdata/speech-synthesis-demo/index.html
@@ -0,0 +1,211 @@
+<!DOCTYPE html>
+<!--
+ | Test the SpeechSynthesis interface.
+ -->
+<html>
+<head>
+  <style>
+    body {
+      background-color: #FFFFFF;
+    }
+  </style>
+  <script>
+    var on_voices_changed_event_count = 0;
+    function log_info(message) {
+      console.log(message);
+      document.getElementById('info').innerHTML += message + '.\n';
+    }
+    function log_error(message) {
+      console.log(message);
+      document.getElementById('error').innerHTML += message + '.\n';
+    }
+    function assert(condition, message) {
+      if (!condition) { log_error('ASSERT: ' + message + '.'); }
+    }
+    function SaySomething(voice) {
+      var unheard_utterance_text = 'You Should Not Hear This.';
+      var utterance_text = 'Cobalt can talk.';
+      var final_utterance_text = 'It really can!';
+
+      var utterance = new SpeechSynthesisUtterance(unheard_utterance_text);
+      assert(utterance, 'new SpeechSynthesisUtterance failed');
+      assert(typeof utterance != undefined,
+          'new SpeechSynthesisUtterance undefined');
+      assert(utterance.text === unheard_utterance_text,
+          'SpeechSynthesisUtterance doesn\'t get text from constructor');
+
+      assert(utterance.onstart !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define start event handler');
+      assert(utterance.onend !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define end event handler');
+      assert(utterance.onpause !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define pause event handler');
+      assert(utterance.onresume !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define resume event handler');
+      assert(utterance.onmark !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define mark event handler');
+      utterance.onmark = function(e) { log_info('SpeechSynthesisUtterance onmark called for: ' + e.utterance.text); }
+      assert(utterance.onboundary !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define boundary event handler');
+      utterance.onboundary = function(e) { log_info('SpeechSynthesisUtterance onboundary called for: ' + e.utterance.text); }
+
+      assert(utterance.voice !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define voice attribute');
+      assert(typeof utterance.voice !== 'SpeechSynthesisVoice',
+          'SpeechSynthesisUtterance voice isn\'t of type SpeechSynthesisVoice');
+      utterance.voice = voice;
+
+      assert(utterance.volume !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define volume attribute');
+      utterance.volume = 1.0;
+
+      assert(utterance.rate !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define rate attribute');
+      utterance.rate = 1.0;
+
+      assert(utterance.pitch !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define pitch attribute');
+      utterance.pitch = 1.0;
+
+      assert(utterance.onerror !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define error event handler');
+      utterance.onerror = function(e) { log_info('SpeechSynthesisUtterance onerror called as expected with: ' + e.error); }
+
+      assert(utterance.lang !== undefined,
+          'SpeechSynthesisUtterance doesn\'t define lang attribute');
+      utterance.lang = "Martian is not a language";
+
+      utterance.onstart = function(e) { log_error('SpeechSynthesisUtterance onstart called for: ' + e.utterance.text); }
+      utterance.onend = function(e) { log_error('SpeechSynthesisUtterance onend called for: ' + e.utterance.text); }
+      utterance.onpause = function(e) { log_error('SpeechSynthesisUtterance onpause called for: ' + e.utterance.text); }
+      utterance.onresume = function(e) { log_error('SpeechSynthesisUtterance onresume called for: ' + e.utterance.text); }
+
+      assert(speechSynthesis.speak !== undefined,
+          'SpeechSynthesis doesn\'t define speak');
+      speechSynthesis.speak(utterance);
+
+      utterance.lang = voice.lang;
+
+      assert(speechSynthesis.pause !== undefined,
+          'SpeechSynthesis doesn\'t define pause');
+      speechSynthesis.pause();
+
+      // These will be canceled below. They will not be heard.
+      speechSynthesis.speak(utterance);
+      speechSynthesis.speak(utterance);
+      speechSynthesis.speak(utterance);
+
+      utterance.onerror = function(e) { log_error('SpeechSynthesisUtterance onerror called as with: ' + e.error); }
+
+      assert(speechSynthesis.cancel !== undefined,
+          'SpeechSynthesis doesn\'t define cancel');
+      speechSynthesis.cancel();
+      assert(speechSynthesis.resume !== undefined,
+          'SpeechSynthesis doesn\'t define resume');
+      speechSynthesis.resume();
+      speechSynthesis.pause();
+
+      utterance.text = utterance_text;
+      utterance.onstart = function(e) { log_info('SpeechSynthesisUtterance onstart called for: ' + e.utterance.text); }
+      utterance.onend = function(e) { log_info('SpeechSynthesisUtterance onend called for: ' + e.utterance.text); }
+
+      assert(utterance.text === utterance_text,
+          'SpeechSynthesisUtterance text doesn\'t get text updated');
+      speechSynthesis.speak(utterance);
+
+      utterance.text = final_utterance_text;
+      utterance.onstart = function(e) { log_info('SpeechSynthesisUtterance onstart called again for: ' + e.utterance.text); }
+      utterance.onend = function(e) { log_info('SpeechSynthesisUtterance onend called again for: ' + e.utterance.text); }
+
+      speechSynthesis.resume();
+      log_info('You should hear \"' + utterance_text + '\" being spoken (once), with onstart and onend calls');
+
+      speechSynthesis.speak(utterance);
+      log_info('You should hear \"' + final_utterance_text + '\" being spoken (once), with onstart and onend calls');
+    }
+    function onVoicesChanged() {
+      if (on_voices_changed_event_count) {
+        console.log('Note: onVoicesChanged called more than once.');
+        return;
+      }
+      ++on_voices_changed_event_count;
+
+      assert(speechSynthesis.getVoices !== undefined,
+          'SpeechSynthesis doesn\'t define getVoices');
+
+      var voices = speechSynthesis.getVoices();
+      assert(voices, 'SpeechSynthesis.getVoices() returned nothing');
+      assert(typeof voices !== undefined,
+          'SpeechSynthesis.getVoices() returned undefined type');
+
+      document.getElementById('voice_count').innerHTML = voices.length;
+      assert(voices.length > 0,
+          'SpeechSynthesis.getVoices() returned no voices');
+
+      var voice = voices[0];
+      assert(voice, 'First voice element is nothing');
+      assert(typeof voice !== undefined,
+          'First voice element is of undefined type');
+
+      assert(voice.name !== undefined, 'Voice does not have a name attribute');
+      assert(voice.lang !== undefined,
+          'Voice does not have a language attribute');
+      assert(voice.lang.length > 0,
+          'Voice has an empty language attribute string');
+      assert(voice.voiceURI !== undefined,
+          'Voice does not have a URI attribute');
+      assert(voice.localService !== undefined,
+          'Voice does not have a localservice attribute');
+      assert(voice.default !== undefined,
+          'Voice does not have a default attribute');
+      var voice_data = 'name=\'' + voice.name + '\' ' +
+                       'lang=\'' + voice.lang + '\' ' +
+                       'URI=\'' + voice.voiceURI + '\' ' +
+                       'local=' + voice.localService + ' ' +
+                       'default=' + voice.default;
+
+      document.getElementById('voice_data').innerHTML = voice_data;
+      SaySomething(voice);
+    }
+    window.onload = function() {
+
+      document.getElementById('lang').innerHTML = navigator.language;
+
+      assert(window.speechSynthesis !== undefined,
+          'window.SpeechSynthesis doesn\'t exist');
+      assert(speechSynthesis, 'SpeechSynthesis doesn\'t exist');
+      assert(typeof speechSynthesis !== undefined,
+          'SpeechSynthesis is of undefined type');
+      assert(speechSynthesis === window.speechSynthesis,
+          'SpeechSynthesis doesn\'t match window.SpeechSynthesis');
+
+      assert(speechSynthesis.onvoiceschanged !== undefined,
+          'SpeechSynthesis doesn\'t define voiceschanged event handler');
+      speechSynthesis.onvoiceschanged = onVoicesChanged;
+
+      assert(speechSynthesis.pending !== undefined,
+          'SpeechSynthesis does not define pending attribute');
+      assert(speechSynthesis.speaking !== undefined,
+          'SpeechSynthesis does not define speaking attribute');
+      assert(speechSynthesis.paused !== undefined,
+          'SpeechSynthesis does not define paused attribute');
+
+      window.setTimeout(function() {
+        if (!on_voices_changed_event_count) {
+          log_error('ERROR: onVoicesChanged never called!');
+        }
+      }, 333);
+    }
+  </script>
+</head>
+<body>
+  <div>Speech Synthesis Test
+    <div>Document Language: <span id='lang'>-</div>
+    <div>Voice Count: <span id='voice_count'>-</div>
+    <div>Voice Data: <span id='voice_data'>-</div>
+    <span id='error' style='white-space: pre; color:#FFFFFF; background-color:#FF0000'></span>
+    <span id='info' style='white-space: pre; background-color:#00FF00'></span>
+    <div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 8733995..28b10ee 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-32627
\ No newline at end of file
+34413
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index c0ce39c..e500d8c 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -115,6 +115,9 @@
     # moved to starboard.
     'starboard_path%': 'starboard/<(target_arch)',
 
+    # List of platform-specific targets that get compiled into cobalt.
+    'cobalt_platform_dependencies%': [],
+
     # The source of EGL and GLES headers and libraries.
     # Valid values (case and everything sensitive!):
     #   'none'   - No EGL + GLES implementation is available on this platform.
@@ -132,6 +135,9 @@
     #   "cobalt/renderer/egl_and_gles/egl_and_gles_<gl_type>.gyp not found"
     'gl_type%': 'system_gles2',
 
+    # Temporary indicator for Tizen - should eventually move to feature defines.
+    'tizen_os%': 0,
+
     # Cache parameters
 
     # The following set of parameters define how much memory is reserved for
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index ddf3740..698d9b7 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -47,6 +47,7 @@
   """Returns the name of the hosting OS."""
   host_os_names = {
       'linux2': 'linux',
+      'linux3': 'linux',
       'win32': 'win',
   }
 
diff --git a/src/cobalt/build/ninja/ninja-linux32.armv7l b/src/cobalt/build/ninja/ninja-linux32.armv7l
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/cobalt/build/ninja/ninja-linux32.armv7l
diff --git a/src/cobalt/debug/debug.gyp b/src/cobalt/debug/debug.gyp
index 9d3596e..9fa32c3 100644
--- a/src/cobalt/debug/debug.gyp
+++ b/src/cobalt/debug/debug.gyp
@@ -58,6 +58,7 @@
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/script/script.gyp:script',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/net/net.gyp:http_server',
       ],
       'conditions': [
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index a80be3f..7985a1a 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -73,6 +73,7 @@
         '<(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)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
diff --git a/src/cobalt/dom/mutation_observer_task_manager.cc b/src/cobalt/dom/mutation_observer_task_manager.cc
index 3f1aae8..01de24d 100644
--- a/src/cobalt/dom/mutation_observer_task_manager.cc
+++ b/src/cobalt/dom/mutation_observer_task_manager.cc
@@ -67,12 +67,19 @@
   //    microtask subtask to run these steps: [HTML]
   //
   // Subtask steps are implemented in MutationObserver::Notify.
-  for (MutationObserverSet::iterator it = observers_.begin();
-       it != observers_.end(); ++it) {
-    MutationObserver* observer = *it;
-    if (!observer->Notify()) {
-      DLOG(ERROR) << "Exception when notifying mutation observer.";
+
+  // Calling Notify could eventually add/remove observers from the set, so make
+  // a copy of it.
+  MutationObserverSet observers_copy = observers_;
+  while (!observers_copy.empty()) {
+    MutationObserver* observer = *observers_copy.begin();
+    // Check that this observer was not removed as a side effect of Notify().
+    if (observers_.find(observer) != observers_.end()) {
+      if (!observer->Notify()) {
+        DLOG(ERROR) << "Exception when notifying mutation observer.";
+      }
     }
+    observers_copy.erase(observers_copy.begin());
   }
 }
 
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/cobalt/dom/speech_synthesis_getter.idl
similarity index 65%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/cobalt/dom/speech_synthesis_getter.idl
index b75f65f..4032352 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/cobalt/dom/speech_synthesis_getter.idl
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
 
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
-}
+[NoInterfaceObject]
+interface SpeechSynthesisGetter {
+  readonly attribute SpeechSynthesis speechSynthesis;
+};
+Window implements SpeechSynthesisGetter;
diff --git a/src/cobalt/dom/testing/mock_event_listener.h b/src/cobalt/dom/testing/mock_event_listener.h
index 4ae9d20..108f4ee 100644
--- a/src/cobalt/dom/testing/mock_event_listener.h
+++ b/src/cobalt/dom/testing/mock_event_listener.h
@@ -148,6 +148,8 @@
 
   void RegisterOwner(script::Wrappable*) OVERRIDE {}
   void DeregisterOwner(script::Wrappable*) OVERRIDE {}
+  void PreventGarbageCollection() OVERRIDE {}
+  void AllowGarbageCollection() OVERRIDE {}
   const EventListener* GetScriptValue(void) const OVERRIDE {
     return mock_listener_;
   }
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index df5ff80..7add6f2 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -120,6 +120,7 @@
       ALLOW_THIS_IN_INITIALIZER_LIST(animation_frame_request_callback_list_(
           new AnimationFrameRequestCallbackList(this))),
       crypto_(new Crypto()),
+      speech_synthesis_(new speech::SpeechSynthesis(navigator_)),
       ALLOW_THIS_IN_INITIALIZER_LIST(local_storage_(
           new Storage(this, Storage::kLocalStorage, local_storage_database))),
       ALLOW_THIS_IN_INITIALIZER_LIST(
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index fe011e0..77c67c3 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -51,6 +51,7 @@
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/execution_state.h"
 #include "cobalt/script/script_runner.h"
+#include "cobalt/speech/speech_synthesis.h"
 #include "cobalt/system_window/system_window.h"
 #include "googleurl/src/gurl.h"
 #include "starboard/window.h"
@@ -227,6 +228,12 @@
   //   https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
   const scoped_refptr<Performance>& performance() const;
 
+  // Web API: SpeechSynthesisGetter (implements)
+  //   https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+  scoped_refptr<speech::SpeechSynthesis> speech_synthesis() const {
+    return speech_synthesis_;
+  }
+
   // Custom, not in any spec.
   //
   const scoped_refptr<Console>& console() const;
@@ -291,6 +298,7 @@
       animation_frame_request_callback_list_;
 
   scoped_refptr<Crypto> crypto_;
+  scoped_refptr<speech::SpeechSynthesis> speech_synthesis_;
 
   scoped_refptr<Storage> local_storage_;
   scoped_refptr<Storage> session_storage_;
diff --git a/src/cobalt/dom_parser/dom_parser.gyp b/src/cobalt/dom_parser/dom_parser.gyp
index 379fe54..f64e8d9 100644
--- a/src/cobalt/dom_parser/dom_parser.gyp
+++ b/src/cobalt/dom_parser/dom_parser.gyp
@@ -38,6 +38,7 @@
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/loader/loader.gyp:loader',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/third_party/libxml/libxml.gyp:libxml',
       ],
     },
@@ -52,6 +53,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
diff --git a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
index 91dc02c..af0e85b 100644
--- a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
@@ -116,10 +116,12 @@
   }
 
   if (!html_parser_context_) {
+#if !defined(USE_SYSTEM_LIBXML)
     // Suppress emitting a <p> element at the root level. This is needed to
     // prevent a <p> tag being added to text at the root level, for example
     // when used for setting an element's innerHTML.
     htmlEmitImpliedRootLevelParagraph(0);
+#endif
 
     html_parser_context_ =
         htmlCreatePushParserCtxt(&html_sax_handler, this, current_chunk.c_str(),
diff --git a/src/cobalt/h5vcc/h5vcc.gyp b/src/cobalt/h5vcc/h5vcc.gyp
index 40d8db1..6c5c7b6 100644
--- a/src/cobalt/h5vcc/h5vcc.gyp
+++ b/src/cobalt/h5vcc/h5vcc.gyp
@@ -60,6 +60,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/build/cobalt_build_id.gyp:cobalt_build_id',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
       ],
       'include_dirs': [
         # For cobalt_build_id.h
diff --git a/src/cobalt/h5vcc/h5vcc_account_manager.cc b/src/cobalt/h5vcc/h5vcc_account_manager.cc
index e2796cb..0bdfffd 100644
--- a/src/cobalt/h5vcc/h5vcc_account_manager.cc
+++ b/src/cobalt/h5vcc/h5vcc_account_manager.cc
@@ -15,14 +15,14 @@
 #include "cobalt/h5vcc/h5vcc_account_manager.h"
 
 #include "base/memory/scoped_ptr.h"
-#include "starboard/ps4/application_linking.h"
 #include "starboard/user.h"
 
 namespace cobalt {
 namespace h5vcc {
 
 H5vccAccountManager::H5vccAccountManager()
-    : thread_("AccountManager"), owning_message_loop_(MessageLoop::current()) {
+    : thread_("AccountManager"), owning_message_loop_(MessageLoop::current()),
+      user_authorizer_(account::UserAuthorizer::Create()) {
   thread_.Start();
 }
 
@@ -71,55 +71,48 @@
   SbUser current_user = SbUserGetCurrent();
   DCHECK(SbUserIsValid(current_user));
 
-  static size_t kBufferSize = SbUserMaxAuthenticationTokenSizeInBytes();
-  scoped_array<char> token_buffer(new char[kBufferSize]);
-  SbUserApplicationTokenResults token_results;
-  token_results.token_buffer_size = kBufferSize;
-  token_results.token_buffer = token_buffer.get();
+  scoped_ptr<account::AccessToken> access_token(NULL);
 
-  bool got_valid_token = false;
   switch (operation) {
     case kPairing:
-      got_valid_token =
-          SbUserRequestApplicationLinking(current_user, &token_results);
-      DLOG_IF(INFO, !got_valid_token) << "Application linking request failed.";
+      access_token = user_authorizer_->AuthorizeUser(current_user);
+      DLOG_IF(INFO, !access_token) << "User authorization request failed.";
       break;
     case kUnpairing:
-      if (SbUserRequestApplicationUnlinking(current_user)) {
-        got_valid_token = false;
+      if (user_authorizer_->DeauthorizeUser(current_user)) {
         break;
       }
       // The user canceled the flow, or there was some error. Fall into the next
       // case to get an access token if available and return that.
-      DLOG(INFO) << "Application unlinking request failed. Try to get token.";
+      DLOG(INFO) << "User deauthorization request failed. Try to get token.";
     case kGetToken:
-      got_valid_token =
-          SbUserRequestAuthenticationToken(current_user, &token_results);
-      DLOG_IF(INFO, !got_valid_token) << "Authentication token request failed.";
+      access_token = user_authorizer_->RefreshAuthorization(current_user);
+      DLOG_IF(INFO, !access_token) << "Authorization refresh request failed.";
       break;
   }
 
+  std::string token_value;
   uint64_t expiration_in_seconds = 0;
-  if (got_valid_token) {
-    SbTime expires_in = token_results.expiry - SbTimeGetNow();
-    // If this token's expiry is in the past, then we didn't get a valid token.
-    if (expires_in < 0) {
-      DLOG(WARNING) << "Authentication token expires in the past.";
-      got_valid_token = false;
-    } else {
-      expiration_in_seconds = expires_in / kSbTimeSecond;
+  if (access_token) {
+    token_value = access_token->token_value;
+    if (access_token->expiry) {
+      base::TimeDelta expires_in =
+          access_token->expiry.value() - base::Time::Now();
+      // If this token's expiry is in the past, then it's not a valid token.
+      base::TimeDelta zero;
+      if (expires_in < zero) {
+        DLOG(WARNING) << "User authorization expires in the past.";
+        token_value.clear();
+      } else {
+        expiration_in_seconds = expires_in.InSeconds();
+      }
     }
   }
-  // If we did not get a valid token to return to the caller, set the token
-  // buffer to an empty string.
-  if (!got_valid_token) {
-    token_buffer[0] = '\0';
-  }
 
   owning_message_loop_->PostTask(
       FROM_HERE,
       base::Bind(&H5vccAccountManager::SendResult, this,
-                 base::Passed(&token_callback), std::string(token_buffer.get()),
+                 base::Passed(&token_callback), token_value,
                  expiration_in_seconds));
 }
 
diff --git a/src/cobalt/h5vcc/h5vcc_account_manager.h b/src/cobalt/h5vcc/h5vcc_account_manager.h
index 8d904ca..9885bb7 100644
--- a/src/cobalt/h5vcc/h5vcc_account_manager.h
+++ b/src/cobalt/h5vcc/h5vcc_account_manager.h
@@ -18,9 +18,11 @@
 #include <queue>
 #include <string>
 
+#include "base/memory/scoped_ptr.h"
 #include "base/message_loop.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
+#include "cobalt/account/user_authorizer.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/wrappable.h"
@@ -76,6 +78,9 @@
   // fired on this loop as well.
   MessageLoop* owning_message_loop_;
 
+  // The platform-specific user authorizer for getting access tokens.
+  scoped_ptr<account::UserAuthorizer> user_authorizer_;
+
   friend class scoped_refptr<H5vccAccountManager>;
   DISALLOW_COPY_AND_ASSIGN(H5vccAccountManager);
 };
diff --git a/src/cobalt/input/input.gyp b/src/cobalt/input/input.gyp
index 7c6c3e5..56ca050 100644
--- a/src/cobalt/input/input.gyp
+++ b/src/cobalt/input/input.gyp
@@ -34,6 +34,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
       ],
       'conditions': [
diff --git a/src/cobalt/layout/layout.gyp b/src/cobalt/layout/layout.gyp
index c239402..e1b9469 100644
--- a/src/cobalt/layout/layout.gyp
+++ b/src/cobalt/layout/layout.gyp
@@ -91,6 +91,7 @@
         '<(DEPTH)/cobalt/loader/loader.gyp:loader',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:animations',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
       ],
     },
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index 0e6b5ea..25da0b1 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -22,10 +22,17 @@
 namespace cobalt {
 namespace loader {
 namespace image {
+namespace {
 
 const int kPixelSize = 4;
 const int kLoopInfinite = 0;
 
+inline uint32_t DivideBy255(uint32_t value) {
+  return (value + 1 + (value >> 8)) >> 8;
+}
+
+}  // namespace
+
 AnimatedWebPImage::AnimatedWebPImage(
     const math::Size& size, bool is_opaque,
     render_tree::ResourceProvider* resource_provider)
@@ -37,6 +44,7 @@
       frame_count_(0),
       loop_count_(kLoopInfinite),
       background_color_(0),
+      should_dispose_previous_frame_(false),
       current_frame_index_(0),
       next_frame_index_(0),
       resource_provider_(resource_provider) {}
@@ -62,16 +70,17 @@
 
     loop_count_ = WebPDemuxGetI(demux_, WEBP_FF_LOOP_COUNT);
     background_color_ = WebPDemuxGetI(demux_, WEBP_FF_BACKGROUND_COLOR);
-    image_buffer_ = AllocateImageData();
-    DCHECK(image_buffer_);
-    FillImageBufferWithBackgroundColor();
+    if (size_.width() > 0 && size_.height() > 0) {
+      image_buffer_.resize(size_.width() * size_.height() * kPixelSize);
+      FillImageBufferWithBackgroundColor();
 
-    current_frame_time_ = base::TimeTicks::Now();
-    current_frame_index_ = 0;
-    thread_.Start();
-    thread_.message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+      current_frame_time_ = base::TimeTicks::Now();
+      current_frame_index_ = 0;
+      thread_.Start();
+      thread_.message_loop()->PostTask(
+          FROM_HERE,
+          base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+    }
   }
   frame_count_ = new_frame_count;
 }
@@ -82,67 +91,74 @@
 
   UpdateTimelineInfo();
 
-  // Decode the frames from current frame to next frame and blend the results
-  // if necessary.
+  // Decode the frames from current frame to next frame and blend the results.
   for (int frame_index = current_frame_index_ + 1;
        frame_index <= next_frame_index_; ++frame_index) {
+    // Dispose previous frame if necessary.
+    if (should_dispose_previous_frame_) {
+      FillImageBufferWithBackgroundColor();
+    }
+
+    // Decode the current frame.
     WebPIterator webp_iterator;
     WebPDemuxGetFrame(demux_, frame_index, &webp_iterator);
     if (!webp_iterator.complete) {
       break;
     }
-
     WEBPImageDecoder webp_image_decoder(resource_provider_);
     webp_image_decoder.DecodeChunk(webp_iterator.fragment.bytes,
                                    webp_iterator.fragment.size);
-    WebPDemuxReleaseIterator(&webp_iterator);
     if (!webp_image_decoder.FinishWithSuccess()) {
       LOG(ERROR) << "Failed to decode WebP image frame.";
       break;
     }
 
-    scoped_ptr<render_tree::ImageData> next_frame_data =
-        webp_image_decoder.RetrieveImageData();
-    DCHECK(next_frame_data);
-
-    uint8_t* image_buffer_memory = image_buffer_->GetMemory();
-    uint8_t* next_frame_memory = next_frame_data->GetMemory();
-    // Alpha-blending: Given that each of the R, G, B and A channels is 8-bit,
-    // and the RGB channels are not premultiplied by alpha, the formula for
-    // blending 'dst' onto 'src' is:
-    // blend.A = src.A + dst.A * (1 - src.A / 255)
-    // if blend.A = 0 then
-    //   blend.RGB = 0
-    // else
-    //   blend.RGB = (src.RGB * src.A +
-    //                dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
-    //   https://developers.google.com/speed/webp/docs/riff_container#animation
-    for (int y = 0; y < webp_iterator.height; ++y) {
-      for (int x = 0; x < webp_iterator.width; ++x) {
+    // Alpha blend the current frame on top of previous frame.
+    {
+      TRACE_EVENT0("cobalt::loader::image", "Blending");
+      DCHECK(!image_buffer_.empty());
+      uint8_t* image_buffer_memory = &image_buffer_[0];
+      uint8_t* next_frame_memory = webp_image_decoder.GetOriginalMemory();
+      // Alpha-blending: Given that each of the R, G, B and A channels is 8-bit,
+      // and the RGB channels are not premultiplied by alpha, the formula for
+      // blending 'dst' onto 'src' is:
+      // blend.A = src.A + dst.A * (1 - src.A / 255)
+      // if blend.A = 0 then
+      //   blend.RGB = 0
+      // else
+      //   blend.RGB = (src.RGB * src.A +
+      //                dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
+      //   https://developers.google.com/speed/webp/docs/riff_container#animation
+      for (int y = 0; y < webp_iterator.height; ++y) {
         uint8_t* src =
-            next_frame_memory + (y * webp_iterator.width + x) * kPixelSize;
+            next_frame_memory + (y * webp_iterator.width) * kPixelSize;
         uint8_t* dst = image_buffer_memory +
-                       ((y + webp_iterator.y_offset) * size_.width() + x +
+                       ((y + webp_iterator.y_offset) * size_.width() +
                         webp_iterator.x_offset) *
                            kPixelSize;
-        uint8_t blend[4];
-        blend[3] = static_cast<uint8_t>(src[3] + dst[3] * (1 - src[3] / 255.0));
-        if (blend[3] == 0) {
-          blend[0] = blend[1] = blend[2] = 0;
-        } else {
-          for (int i = 0; i < 3; ++i) {
-            blend[i] = static_cast<uint8_t>(
-                (src[i] * src[3] + dst[i] * dst[3] * (1 - src[3] / 255.0)) /
-                blend[3]);
+        for (int x = 0; x < webp_iterator.width; ++x) {
+          uint32_t dst_factor = dst[3] * (255 - src[3]);
+          dst[3] = static_cast<uint8_t>(src[3] + DivideBy255(dst_factor));
+          if (dst[3] == 0) {
+            dst[0] = dst[1] = dst[2] = 0;
+          } else {
+            for (int i = 0; i < 3; ++i) {
+              dst[i] = static_cast<uint8_t>(
+                  (src[i] * src[3] + DivideBy255(dst[i] * dst_factor)) /
+                  dst[3]);
+            }
           }
+
+          src += kPixelSize;
+          dst += kPixelSize;
         }
-        SbMemoryCopy(dst, &blend, kPixelSize);
       }
     }
 
-    if (webp_iterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
-      FillImageBufferWithBackgroundColor();
-    }
+    // Record the dispose method for current frame.
+    should_dispose_previous_frame_ =
+        webp_iterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND;
+    WebPDemuxReleaseIterator(&webp_iterator);
   }
 
   // Generate the next frame render tree image if necessary.
@@ -150,7 +166,8 @@
     current_frame_index_ = next_frame_index_;
     scoped_ptr<render_tree::ImageData> next_frame_data = AllocateImageData();
     DCHECK(next_frame_data);
-    uint8_t* image_buffer_memory = image_buffer_->GetMemory();
+    DCHECK(!image_buffer_.empty());
+    uint8_t* image_buffer_memory = &image_buffer_[0];
     uint8_t* next_frame_memory = next_frame_data->GetMemory();
     SbMemoryCopy(next_frame_memory, image_buffer_memory,
                  size_.width() * size_.height() * kPixelSize);
@@ -208,12 +225,11 @@
 }
 
 void AnimatedWebPImage::FillImageBufferWithBackgroundColor() {
-  uint8_t* image_buffer_memory = image_buffer_->GetMemory();
   for (int y = 0; y < size_.height(); ++y) {
     for (int x = 0; x < size_.width(); ++x) {
-      uint32_t* src = reinterpret_cast<uint32_t*>(
-          image_buffer_memory + (y * size_.width() + x) * kPixelSize);
-      *src = background_color_;
+      *reinterpret_cast<uint32_t*>(
+          &image_buffer_[(y * size_.width() + x) * kPixelSize]) =
+          background_color_;
     }
   }
 }
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index e32f624..3f93216 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -77,14 +77,17 @@
   // looping infinitely.
   int loop_count_;
   uint32_t background_color_;
+  bool should_dispose_previous_frame_;
   int current_frame_index_;
   int next_frame_index_;
   render_tree::ResourceProvider* resource_provider_;
 
   base::TimeTicks current_frame_time_;
   base::TimeTicks next_frame_time_;
+  // The original encoded data.
   std::vector<uint8> data_buffer_;
-  scoped_ptr<render_tree::ImageData> image_buffer_;
+  // The alpha-blended frame.
+  std::vector<uint8> image_buffer_;
   scoped_refptr<render_tree::Image> current_frame_image_;
   base::Closure decoded_callback_;
   base::Lock lock_;
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index fd4fcbc..9c2b351 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -83,7 +83,8 @@
     : resource_provider_(resource_provider),
       success_callback_(success_callback),
       error_callback_(error_callback),
-      state_(kWaitingForHeader) {
+      state_(kWaitingForHeader),
+      is_deletion_pending_(false) {
   TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::ImageDecoder()");
   signature_cache_.position = 0;
 
@@ -129,6 +130,14 @@
 void ImageDecoder::DecodeChunk(const char* data, size_t size) {
   TRACE_EVENT1("cobalt::loader::image_decoder", "ImageDecoder::DecodeChunk",
                "size", size);
+  // If there's a deletion pending, then just clear out the decoder and return.
+  // There's no point in doing any additional processing that'll get thrown
+  // away without ever being used.
+  if (base::subtle::Acquire_Load(&is_deletion_pending_)) {
+    decoder_.reset();
+    return;
+  }
+
   if (size == 0) {
     DLOG(WARNING) << "Decoder received 0 bytes.";
     return;
@@ -139,6 +148,14 @@
 
 void ImageDecoder::Finish() {
   TRACE_EVENT0("cobalt::loader::image_decoder", "ImageDecoder::Finish");
+  // If there's a deletion pending, then just clear out the decoder and return.
+  // There's no point in doing any additional processing that'll get thrown
+  // away without ever being used.
+  if (base::subtle::Acquire_Load(&is_deletion_pending_)) {
+    decoder_.reset();
+    return;
+  }
+
   switch (state_) {
     case kDecoding:
       DCHECK(decoder_);
@@ -196,7 +213,7 @@
 bool ImageDecoder::Suspend() {
   TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::Suspend()");
   if (state_ == kDecoding) {
-    DCHECK(decoder_);
+    DCHECK(decoder_ || base::subtle::Acquire_Load(&is_deletion_pending_));
     decoder_.reset();
   }
   state_ = kSuspended;
@@ -220,6 +237,10 @@
   }
 }
 
+void ImageDecoder::SetDeletionPending() {
+  base::subtle::Acquire_Store(&is_deletion_pending_, true);
+}
+
 void ImageDecoder::DecodeChunkInternal(const uint8* input_bytes, size_t size) {
   TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::DecodeChunkInternal()");
   switch (state_) {
diff --git a/src/cobalt/loader/image/image_decoder.h b/src/cobalt/loader/image/image_decoder.h
index a3f0168..c4b03a3 100644
--- a/src/cobalt/loader/image/image_decoder.h
+++ b/src/cobalt/loader/image/image_decoder.h
@@ -17,6 +17,7 @@
 
 #include <string>
 
+#include "base/atomicops.h"
 #include "base/callback.h"
 #include "cobalt/loader/decoder.h"
 #include "cobalt/loader/image/image.h"
@@ -50,6 +51,11 @@
   bool Suspend() OVERRIDE;
   void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE;
 
+  // Called when this ImageDecoder's deletion has been posted to a message loop.
+  // This prevents any additional decoding from occuring prior to the decoder
+  // being deleted.
+  void SetDeletionPending();
+
   // Call this function to use the StubImageDecoder which produces a small image
   // without decoding.
   static void UseStubImageDecoder();
@@ -84,6 +90,9 @@
   State state_;
   std::string error_message_;
   std::string mime_type_;
+  // Whether or not there is a pending task deleting this decoder on a message
+  // loop.
+  base::subtle::Atomic32 is_deletion_pending_;
 };
 
 }  // namespace image
diff --git a/src/cobalt/loader/image/threaded_image_decoder_proxy.cc b/src/cobalt/loader/image/threaded_image_decoder_proxy.cc
index 0eb7958..e4c26ac 100644
--- a/src/cobalt/loader/image/threaded_image_decoder_proxy.cc
+++ b/src/cobalt/loader/image/threaded_image_decoder_proxy.cc
@@ -77,6 +77,9 @@
 // loop, where it will be deleted after any pending tasks involving it are
 // done.
 ThreadedImageDecoderProxy::~ThreadedImageDecoderProxy() {
+  // Notify the ImageDecoder that there's a pending deletion to ensure that no
+  // additional work is done decoding the image.
+  image_decoder_->SetDeletionPending();
   load_message_loop_->DeleteSoon(FROM_HERE, image_decoder_.release());
 }
 
diff --git a/src/cobalt/loader/image/webp_image_decoder.cc b/src/cobalt/loader/image/webp_image_decoder.cc
index 6f36315..f2ae873 100644
--- a/src/cobalt/loader/image/webp_image_decoder.cc
+++ b/src/cobalt/loader/image/webp_image_decoder.cc
@@ -27,8 +27,7 @@
     render_tree::ResourceProvider* resource_provider)
     : ImageDataDecoder(resource_provider),
       internal_decoder_(NULL),
-      has_animation_(false),
-      data_buffer_(new std::vector<uint8>()) {
+      has_animation_(false) {
   TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::WEBPImageDecoder()");
   // Initialize the configuration as empty.
   WebPInitDecoderConfig(&config_);
@@ -48,6 +47,10 @@
   DeleteInternalDecoder();
 }
 
+uint8_t* WEBPImageDecoder::GetOriginalMemory() {
+  return config_.output.u.RGBA.rgba;
+}
+
 size_t WEBPImageDecoder::DecodeChunkInternal(const uint8* data,
                                              size_t input_byte) {
   TRACE_EVENT0("cobalt::loader::image",
@@ -58,9 +61,6 @@
     }
 
     if (!config_.input.has_animation) {
-      // For static images, we don't need to store the encoded data.
-      data_buffer_.reset();
-
       if (!AllocateImageData(
               math::Size(config_.input.width, config_.input.height),
               !!config_.input.has_alpha)) {
@@ -89,7 +89,6 @@
         DCHECK(config_.output.u.RGBA.rgba);
         SbMemoryCopy(image_data()->GetMemory(), config_.output.u.RGBA.rgba,
                      config_.output.u.RGBA.size);
-        DeleteInternalDecoder();
         set_state(kDone);
       } else if (status != VP8_STATUS_SUSPENDED) {
         DLOG(ERROR) << "WebPIAppend error, status code: " << status;
diff --git a/src/cobalt/loader/image/webp_image_decoder.h b/src/cobalt/loader/image/webp_image_decoder.h
index 9bafde2..f6b9c16 100644
--- a/src/cobalt/loader/image/webp_image_decoder.h
+++ b/src/cobalt/loader/image/webp_image_decoder.h
@@ -34,6 +34,9 @@
   // From ImageDataDecoder
   std::string GetTypeString() const OVERRIDE { return "WEBPImageDecoder"; }
 
+  // Returns a pointer to the original decoded image memory.
+  uint8_t* GetOriginalMemory();
+
  private:
   // From ImageDataDecoder
   size_t DecodeChunkInternal(const uint8* data, size_t input_byte) OVERRIDE;
@@ -51,7 +54,6 @@
   WebPDecoderConfig config_;
   bool has_animation_;
   scoped_refptr<AnimatedWebPImage> animated_webp_image_;
-  scoped_ptr<std::vector<uint8> > data_buffer_;
 };
 
 }  // namespace image
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc
index 3193a12..08fd0a3 100644
--- a/src/cobalt/loader/net_fetcher.cc
+++ b/src/cobalt/loader/net_fetcher.cc
@@ -64,10 +64,6 @@
   // was successful *and* we get a 2xx response back.
   // We also accept a response code of -1 for URLs where response code is not
   // meaningful, like "data:"
-  // TODO: 304s are unexpected since we don't enable the HTTP cache,
-  // meaning we don't add the If-Modified-Since header to our request.
-  // However, it's unclear what would happen if we did, so DCHECK.
-  DCHECK_NE(response_code, 304) << "Unsupported status code";
   return response_code == -1 || response_code / 100 == 2;
 }
 }  // namespace
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index b88274b..cd17293 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -155,6 +155,8 @@
   ++ticket_;
 #if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
   SbPlayerSetPause(player_, true);
+#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+  SbPlayerSetPlaybackRate(player_, 0.f);
 #endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
   seek_pending_ = true;
 }
@@ -183,6 +185,8 @@
   seek_pending_ = false;
 #if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
   SbPlayerSetPause(player_, playback_rate_ == 0.0);
+#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+  SbPlayerSetPlaybackRate(player_, playback_rate_);
 #endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
 }
 
@@ -330,38 +334,19 @@
   DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
 #endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
 
-#if SB_API_VERSION <= 3
-
   player_ = SbPlayerCreate(
       window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
       &audio_header, &StarboardPlayer::DeallocateSampleCB,
       &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
-#if SB_API_VERSION == 3
-      ,
-      ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider()  // provider
-#endif  // SB_API_VERSION == 3
-      );
-
-#else  //  SB_API_VERSION <= 3
-
-  player_ = SbPlayerCreate(
-      window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
-      &audio_header,
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-      NULL,
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-      &StarboardPlayer::DeallocateSampleCB, &StarboardPlayer::DecoderStatusCB,
-      &StarboardPlayer::PlayerStatusCB,
 #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-      output_mode_,
+      ,
+      output_mode_
 #endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
 #if SB_API_VERSION >= 3
-      ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider(),  // provider
+      ,
+      ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider()  // provider
 #endif  // SB_API_VERSION >= 3
-      this);
-
-#endif  //  SB_API_VERSION <= 3
-
+      );
   DCHECK(SbPlayerIsValid(player_));
 
 #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.cc b/src/cobalt/renderer/backend/egl/graphics_context.cc
index 1b06ad9..574ddd4 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_context.cc
@@ -415,7 +415,10 @@
 
   surface->increment_swap_count();
   if (surface->IsWindowRenderTarget() && surface->swap_count() <= 2) {
+    surface->set_cleared_on_swap(true);
     SecurityClear();
+  } else {
+    surface->set_cleared_on_swap(false);
   }
 }
 
diff --git a/src/cobalt/renderer/backend/egl/render_target.h b/src/cobalt/renderer/backend/egl/render_target.h
index 04e627b..d947408 100644
--- a/src/cobalt/renderer/backend/egl/render_target.h
+++ b/src/cobalt/renderer/backend/egl/render_target.h
@@ -29,6 +29,7 @@
       : swap_count_(0)
       , has_been_made_current_(false)
       , content_preserved_on_swap_(false)
+      , content_cleared_on_swap_(false)
       , is_surface_bad_(false)
   {}
 
@@ -43,9 +44,11 @@
 
   virtual bool IsWindowRenderTarget() const { return false; }
 
-  // Returns whether the render target contents are preserved after the
-  // target has been displayed via eglSwapBuffers().
-  bool IsContentPreservedOnSwap() const { return content_preserved_on_swap_; }
+  // Returns whether the render target contents were preserved after the
+  // target was displayed via eglSwapBuffers().
+  bool ContentWasPreservedAfterSwap() const {
+    return content_preserved_on_swap_ && !content_cleared_on_swap_;
+  }
 
   int64 swap_count() { return swap_count_; }
   void increment_swap_count() { ++swap_count_; }
@@ -56,12 +59,15 @@
   bool is_surface_bad() const { return is_surface_bad_; }
   void set_surface_bad() { is_surface_bad_ = true; }
 
+  void set_cleared_on_swap(bool cleared) { content_cleared_on_swap_ = cleared; }
+
  protected:
   virtual ~RenderTargetEGL() {}
 
   int64 swap_count_;
   bool has_been_made_current_;
   bool content_preserved_on_swap_;
+  bool content_cleared_on_swap_;
   bool is_surface_bad_;
 };
 
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.h b/src/cobalt/renderer/rasterizer/egl/draw_object.h
index 9b055a1..4924230 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.h
@@ -18,6 +18,7 @@
 #include "cobalt/base/type_id.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect.h"
+#include "cobalt/render_tree/color_rgba.h"
 #include "cobalt/renderer/rasterizer/egl/graphics_state.h"
 #include "cobalt/renderer/rasterizer/egl/shader_program_manager.h"
 
@@ -42,12 +43,31 @@
 
   virtual ~DrawObject() {}
 
-  // Issue GL commands to rasterize the object.
-  virtual void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
-      ShaderProgramManager* program_manager) = 0;
-  virtual void ExecuteRasterizeOffscreen(GraphicsState* graphics_state,
+  // This stage is used to render to offscreen targets. It specifically runs
+  // before vertex data is updated for onscreen rendering in order to allow
+  // this stage to modify that data. For example, this stage may rasterize to
+  // an offscreen atlas, so the location within the atlas might impact the
+  // texture coordinates used for the onscreen rasterize stage.
+  //
+  // If this stage needs to use vertex data of its own, then a new stage,
+  // ExecuteOffscreenUpdateVertexBuffer, should be created. This stage should
+  // be handled similarly to the current ExecuteOnscreenUpdateVertexBuffer.
+  virtual void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) {}
-  virtual void ExecuteRasterizeNormal(GraphicsState* graphics_state,
+
+  // This stage is used to update the vertex buffer for the onscreen rasterize
+  // stage. Vertex data is handled by the GraphicsState to minimize the number
+  // of vertex buffers needed. Once this stage is executed, the rasterizer will
+  // then notify the GraphicsState to send all vertex data from all draw
+  // objects to the GPU.
+  virtual void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) = 0;
+
+  // This stage is used to render to the main render target. Although it can be
+  // used to rasterize to any number of render targets, it is best to use a
+  // different stage for offscreen rendering in order to minimize the cost of
+  // switching render targets.
+  virtual void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) = 0;
 
  protected:
@@ -56,6 +76,9 @@
   // Return a uint32_t suitable to be transferred as 4 unsigned bytes
   // representing color to a GL shader.
   static uint32_t GetGLRGBA(float r, float g, float b, float a);
+  static uint32_t GetGLRGBA(const render_tree::ColorRGBA& color) {
+    return GetGLRGBA(color.r(), color.g(), color.b(), color.a());
+  }
 
   BaseState base_state_;
 };
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
index 96e7d78..a3f91c29 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
@@ -20,19 +20,25 @@
 namespace egl {
 
 void DrawObjectManager::AddOpaqueDraw(scoped_ptr<DrawObject> object,
-                                      DrawType type) {
-  draw_objects_[type].push_back(object.release());
+                                      OnscreenType onscreen_type,
+                                      OffscreenType offscreen_type) {
+  if (offscreen_type != kOffscreenNone) {
+    offscreen_order_[offscreen_type].push_back(object.get());
+  }
+
+  draw_objects_[onscreen_type].push_back(object.release());
 }
 
 void DrawObjectManager::AddTransparentDraw(scoped_ptr<DrawObject> object,
-                                           DrawType type,
+                                           OnscreenType onscreen_type,
+                                           OffscreenType offscreen_type,
                                            const math::RectF& bounds) {
   // Try to sort the transparent object next to another object of its type.
   // However, this can only be done as long as its bounds do not overlap with
   // the other object while swapping draw order.
   size_t position = transparent_object_info_.size();
   while (position > 0) {
-    if (transparent_object_info_[position - 1].type <= type) {
+    if (transparent_object_info_[position - 1].type <= onscreen_type) {
       break;
     }
     if (transparent_object_info_[position - 1].bounds.Intersects(bounds)) {
@@ -41,48 +47,66 @@
     --position;
   }
 
+  if (offscreen_type != kOffscreenNone) {
+    offscreen_order_[offscreen_type].push_back(object.get());
+  }
+
   transparent_object_info_.insert(
       transparent_object_info_.begin() + position,
-      TransparentObjectInfo(type, bounds));
-  draw_objects_[kDrawTransparent].insert(
-      draw_objects_[kDrawTransparent].begin() + position,
+      TransparentObjectInfo(onscreen_type, bounds));
+  draw_objects_[kOnscreenTransparent].insert(
+      draw_objects_[kOnscreenTransparent].begin() + position,
       object.release());
 }
 
-void DrawObjectManager::ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+void DrawObjectManager::ExecuteOffscreenRasterize(GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  for (int type = 0; type < kDrawCount; ++type) {
-    for (size_t index = 0; index < draw_objects_[type].size(); ++index) {
-      draw_objects_[type][index]->ExecuteUpdateVertexBuffer(graphics_state,
+  graphics_state->DisableDepthTest();
+  for (int type = 0; type < kOffscreenCount; ++type) {
+    for (size_t index = 0; index < offscreen_order_[type].size(); ++index) {
+      offscreen_order_[type][index]->ExecuteOffscreenRasterize(graphics_state,
           program_manager);
     }
   }
 }
 
-void DrawObjectManager::ExecuteRasterizeOffscreen(GraphicsState* graphics_state,
+void DrawObjectManager::ExecuteOnscreenUpdateVertexBuffer(
+    GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  for (int type = 0; type < kDrawCount; ++type) {
+  for (int type = 0; type < kOnscreenCount; ++type) {
     for (size_t index = 0; index < draw_objects_[type].size(); ++index) {
-      draw_objects_[type][index]->ExecuteRasterizeOffscreen(graphics_state,
-          program_manager);
+      draw_objects_[type][index]->ExecuteOnscreenUpdateVertexBuffer(
+          graphics_state, program_manager);
     }
   }
 }
 
-void DrawObjectManager::ExecuteRasterizeNormal(GraphicsState* graphics_state,
+void DrawObjectManager::ExecuteOnscreenRasterize(GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  for (int type = 0; type < kDrawCount; ++type) {
-    if (type == kDrawTransparent) {
+  graphics_state->EnableDepthTest();
+
+  for (int type = 0; type < kOnscreenCount; ++type) {
+    if (type == kOnscreenTransparent) {
       graphics_state->EnableBlend();
       graphics_state->DisableDepthWrite();
+
+      // Transparent objects must be drawn in the order they were added to
+      // maintain correctness.
+      for (size_t index = 0; index < draw_objects_[type].size(); ++index) {
+        draw_objects_[type][index]->ExecuteOnscreenRasterize(graphics_state,
+            program_manager);
+      }
     } else {
       graphics_state->DisableBlend();
       graphics_state->EnableDepthWrite();
-    }
 
-    for (size_t index = 0; index < draw_objects_[type].size(); ++index) {
-      draw_objects_[type][index]->ExecuteRasterizeNormal(graphics_state,
-          program_manager);
+      // Opaque objects can be drawn in front-to-back order to take advantage
+      // of z-culling.
+      for (size_t index = draw_objects_[type].size(); index > 0;) {
+        --index;
+        draw_objects_[type][index]->ExecuteOnscreenRasterize(graphics_state,
+            program_manager);
+      }
     }
   }
 }
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
index d6df2ed..7c71e9d 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
@@ -33,37 +33,60 @@
 // objects to minimize GPU state changes.
 class DrawObjectManager {
  public:
-  enum DrawType {
-    kDrawRectTexture = 0,
-    kDrawRectColorTexture,
-    kDrawPolyColor,
-    kDrawTransparent,
-    kDrawCount,
+  enum OnscreenType {
+    kOnscreenRectTexture = 0,
+    kOnscreenRectColorTexture,
+    kOnscreenPolyColor,
+    kOnscreenTransparent,
+    kOnscreenCount,
   };
 
-  void AddOpaqueDraw(scoped_ptr<DrawObject> object, DrawType type);
-  void AddTransparentDraw(scoped_ptr<DrawObject> object, DrawType type,
+  // Order offscreen rendering by descending expected pixel area. This helps
+  // make better use of the offscreen texture atlas as smaller requests can
+  // fill in gaps created by the larger requests.
+  enum OffscreenType {
+    kOffscreenSkiaFilter = 0,
+    kOffscreenSkiaShadow,
+    kOffscreenSkiaMultiPlaneImage,
+    kOffscreenSkiaRectRounded,
+    kOffscreenSkiaRectBrush,
+    kOffscreenSkiaRectBorder,
+    kOffscreenSkiaText,
+    kOffscreenCount,
+    kOffscreenNone,     // ExecuteOffscreenRasterize will not be run for these.
+  };
+
+  void AddOpaqueDraw(scoped_ptr<DrawObject> object,
+                     OnscreenType onscreen_type,
+                     OffscreenType offscreen_type);
+  void AddTransparentDraw(scoped_ptr<DrawObject> object,
+                          OnscreenType onscreen_type,
+                          OffscreenType offscreen_type,
                           const math::RectF& bounds);
 
-  void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+  void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager);
-  void ExecuteRasterizeOffscreen(GraphicsState* graphics_state,
+  void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager);
-  void ExecuteRasterizeNormal(GraphicsState* graphics_state,
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager);
 
  private:
   struct TransparentObjectInfo {
-    TransparentObjectInfo(DrawType object_type,
+    TransparentObjectInfo(OnscreenType onscreen_type,
                           const math::RectF& object_bounds)
         : bounds(object_bounds),
-          type(object_type) {}
+          type(onscreen_type) {}
     math::RectF bounds;
-    DrawType type;
+    OnscreenType type;
   };
 
-  ScopedVector<DrawObject> draw_objects_[kDrawCount];
+  ScopedVector<DrawObject> draw_objects_[kOnscreenCount];
   std::vector<TransparentObjectInfo> transparent_object_info_;
+
+  // Manage execution order of objects in the draw_objects_ vectors. This does
+  // not manage destruction of objects.
+  std::vector<DrawObject*> offscreen_order_[kOffscreenCount];
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
index e484793..93b6c95 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
@@ -29,8 +29,7 @@
     const BaseState& base_state, const math::RectF& rect,
     const render_tree::ColorRGBA& color)
     : DrawObject(base_state) {
-  float alpha = base_state_.opacity * color.a();
-  uint32_t color32 = GetGLRGBA(color.r(), color.g(), color.b(), alpha);
+  uint32_t color32 = GetGLRGBA(color * base_state_.opacity);
   attributes_.reserve(4);
   AddVertex(rect.x(), rect.y(), color32);
   AddVertex(rect.x(), rect.bottom(), color32);
@@ -40,7 +39,7 @@
                                     sizeof(VertexAttributes));
 }
 
-void DrawPolyColor::ExecuteUpdateVertexBuffer(
+void DrawPolyColor::ExecuteOnscreenUpdateVertexBuffer(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
   vertex_buffer_ = graphics_state->AllocateVertexData(
@@ -49,7 +48,7 @@
                attributes_.size() * sizeof(VertexAttributes));
 }
 
-void DrawPolyColor::ExecuteRasterizeNormal(
+void DrawPolyColor::ExecuteOnscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
   ShaderProgram<ShaderVertexColor,
@@ -72,7 +71,7 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, color));
   graphics_state->VertexAttribFinish();
-  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
+  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
 }
 
 void DrawPolyColor::AddVertex(float x, float y, uint32_t color) {
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
index b02c854..769299c 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
@@ -34,9 +34,9 @@
                 const math::RectF& rect,
                 const render_tree::ColorRGBA& color);
 
-  void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+  void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
-  void ExecuteRasterizeNormal(GraphicsState* graphics_state,
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
 
  private:
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
index 05d6411..d3f48c9 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
@@ -43,12 +43,33 @@
       rect_(rect),
       texture_(texture),
       vertex_buffer_(NULL) {
-  base_state_.opacity *= color.a();
-  color_ = GetGLRGBA(color.r(), color.g(), color.b(), base_state_.opacity);
+  color_ = GetGLRGBA(color * base_state_.opacity);
   graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
 }
 
-void DrawRectColorTexture::ExecuteUpdateVertexBuffer(
+DrawRectColorTexture::DrawRectColorTexture(GraphicsState* graphics_state,
+    const BaseState& base_state,
+    const math::RectF& rect, const render_tree::ColorRGBA& color,
+    const GenerateTextureFunction& generate_texture)
+    : DrawObject(base_state),
+      texcoord_transform_(math::Matrix3F::Identity()),
+      rect_(rect),
+      texture_(NULL),
+      generate_texture_(generate_texture),
+      vertex_buffer_(NULL) {
+  color_ = GetGLRGBA(color * base_state_.opacity);
+  graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
+}
+
+void DrawRectColorTexture::ExecuteOffscreenRasterize(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  if (!generate_texture_.is_null()) {
+    generate_texture_.Run(&texture_, &texcoord_transform_);
+  }
+}
+
+void DrawRectColorTexture::ExecuteOnscreenUpdateVertexBuffer(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
   VertexAttributes attributes[4] = {
@@ -73,7 +94,7 @@
   SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
 }
 
-void DrawRectColorTexture::ExecuteRasterizeNormal(
+void DrawRectColorTexture::ExecuteOnscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
   ShaderProgram<ShaderVertexColorTexcoord,
@@ -100,9 +121,9 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, texcoord));
   graphics_state->VertexAttribFinish();
-  graphics_state->ActiveTexture(
-      program->GetFragmentShader().u_texture_texunit());
-  GL_CALL(glBindTexture(texture_->GetTarget(), texture_->gl_handle()));
+  graphics_state->ActiveBindTexture(
+      program->GetFragmentShader().u_texture_texunit(),
+      texture_->GetTarget(), texture_->gl_handle());
   GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
 }
 
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
index 1334396..d459196 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
@@ -15,6 +15,7 @@
 #ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_COLOR_TEXTURE_H_
 #define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_COLOR_TEXTURE_H_
 
+#include "base/callback.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/color_rgba.h"
@@ -29,6 +30,10 @@
 // Handles drawing a textured rectangle modulated by a given color.
 class DrawRectColorTexture : public DrawObject {
  public:
+  typedef base::Callback<void(const backend::TextureEGL** out_texture,
+                              math::Matrix3F* out_texcoord_transform)>
+      GenerateTextureFunction;
+
   DrawRectColorTexture(GraphicsState* graphics_state,
                        const BaseState& base_state,
                        const math::RectF& rect,
@@ -36,9 +41,17 @@
                        const backend::TextureEGL* texture,
                        const math::Matrix3F& texcoord_transform);
 
-  void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+  DrawRectColorTexture(GraphicsState* graphics_state,
+                       const BaseState& base_state,
+                       const math::RectF& rect,
+                       const render_tree::ColorRGBA& color,
+                       const GenerateTextureFunction& generate_texture);
+
+  void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
-  void ExecuteRasterizeNormal(GraphicsState* graphics_state,
+  void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) OVERRIDE;
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
 
  private:
@@ -46,6 +59,7 @@
   math::RectF rect_;
   uint32_t color_;
   const backend::TextureEGL* texture_;
+  GenerateTextureFunction generate_texture_;
 
   uint8_t* vertex_buffer_;
 };
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
index 9e0b567..f4099d8 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
@@ -47,10 +47,9 @@
 DrawRectTexture::DrawRectTexture(GraphicsState* graphics_state,
     const BaseState& base_state,
     const math::RectF& rect,
-    const GenerateTextureFunction& generate_texture,
-    const math::Matrix3F& texcoord_transform)
+    const GenerateTextureFunction& generate_texture)
     : DrawObject(base_state),
-      texcoord_transform_(texcoord_transform),
+      texcoord_transform_(math::Matrix3F::Identity()),
       rect_(rect),
       texture_(NULL),
       generate_texture_(generate_texture),
@@ -58,7 +57,15 @@
   graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
 }
 
-void DrawRectTexture::ExecuteUpdateVertexBuffer(
+void DrawRectTexture::ExecuteOffscreenRasterize(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  if (!generate_texture_.is_null()) {
+    generate_texture_.Run(&texture_, &texcoord_transform_);
+  }
+}
+
+void DrawRectTexture::ExecuteOnscreenUpdateVertexBuffer(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
   VertexAttributes attributes[4] = {
@@ -83,15 +90,7 @@
   SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
 }
 
-void DrawRectTexture::ExecuteRasterizeOffscreen(
-    GraphicsState* graphics_state,
-    ShaderProgramManager* program_manager) {
-  if (!generate_texture_.is_null()) {
-    texture_ = generate_texture_.Run();
-  }
-}
-
-void DrawRectTexture::ExecuteRasterizeNormal(
+void DrawRectTexture::ExecuteOnscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
   ShaderProgram<ShaderVertexTexcoord,
@@ -114,9 +113,9 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, texcoord));
   graphics_state->VertexAttribFinish();
-  graphics_state->ActiveTexture(
-      program->GetFragmentShader().u_texture_texunit());
-  GL_CALL(glBindTexture(texture_->GetTarget(), texture_->gl_handle()));
+  graphics_state->ActiveBindTexture(
+      program->GetFragmentShader().u_texture_texunit(),
+      texture_->GetTarget(), texture_->gl_handle());
   GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
 }
 
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
index c021ff0..767c3eb 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
@@ -29,7 +29,8 @@
 // Handles drawing a textured rectangle.
 class DrawRectTexture : public DrawObject {
  public:
-  typedef base::Callback<backend::TextureEGL*(void)>
+  typedef base::Callback<void(const backend::TextureEGL** out_texture,
+                              math::Matrix3F* out_texcoord_transform)>
       GenerateTextureFunction;
 
   DrawRectTexture(GraphicsState* graphics_state,
@@ -41,14 +42,13 @@
   DrawRectTexture(GraphicsState* graphics_state,
                   const BaseState& base_state,
                   const math::RectF& rect,
-                  const GenerateTextureFunction& generate_texture,
-                  const math::Matrix3F& texcoord_transform);
+                  const GenerateTextureFunction& generate_texture);
 
-  void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+  void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
-  void ExecuteRasterizeOffscreen(GraphicsState* graphics_state,
+  void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
-  void ExecuteRasterizeNormal(GraphicsState* graphics_state,
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
 
  private:
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
index 4be6178..904b6ee 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
@@ -30,6 +30,10 @@
       vertex_data_buffer_updated_(false) {
   GL_CALL(glGenBuffers(kNumFramesBuffered, &vertex_data_buffer_handle_[0]));
   memset(clip_adjustment_, 0, sizeof(clip_adjustment_));
+  SetDirty();
+  blend_enabled_ = false;
+  depth_test_enabled_ = false;
+  depth_write_enabled_ = true;
   Reset();
 }
 
@@ -47,7 +51,14 @@
   if (vertex_data_reserved_ > vertex_data_buffer_.capacity()) {
     vertex_data_buffer_.reserve(vertex_data_reserved_);
   }
+
+  // Reset to default GL state. Assume the current state is dirty, so just
+  // set the cached state. The actual GL state will be updated to match the
+  // cached state when needed.
   SetDirty();
+  blend_enabled_ = false;
+  depth_test_enabled_ = false;
+  depth_write_enabled_ = true;
 }
 
 void GraphicsState::EndFrame() {
@@ -56,6 +67,16 @@
   vertex_data_allocated_ = 0;
   vertex_data_buffer_updated_ = false;
   frame_index_ = (frame_index_ + 1) % kNumFramesBuffered;
+
+  // Force default GL state. The current state may be marked dirty, so don't
+  // rely on any functions which check the cached state before issuing GL calls.
+  GL_CALL(glDisable(GL_BLEND));
+  GL_CALL(glDisable(GL_DEPTH_TEST));
+  GL_CALL(glDepthMask(GL_TRUE));
+
+  // Since the GL state was changed without going through the cache, mark it
+  // as dirty.
+  SetDirty();
 }
 
 void GraphicsState::Clear() {
@@ -89,19 +110,25 @@
 }
 
 void GraphicsState::Viewport(int x, int y, int width, int height) {
-  viewport_ = math::Rect(x, y, width, height);
   // Incoming origin is top-left, but GL origin is bottom-left, so flip
   // vertically.
-  GL_CALL(glViewport(x, render_target_size_.height() - y - height,
-                     width, height));
+  if (state_dirty_ || viewport_.x() != x || viewport_.y() != y ||
+      viewport_.width() != width || viewport_.height() != height) {
+    viewport_.SetRect(x, y, width, height);
+    GL_CALL(glViewport(x, render_target_size_.height() - y - height,
+                       width, height));
+  }
 }
 
 void GraphicsState::Scissor(int x, int y, int width, int height) {
   // Incoming origin is top-left, but GL origin is bottom-left, so flip
   // vertically.
-  scissor_ = math::Rect(x, y, width, height);
-  GL_CALL(glScissor(x, render_target_size_.height() - y - height,
-                    width, height));
+  if (state_dirty_ || scissor_.x() != x || scissor_.y() != y ||
+      scissor_.width() != width || scissor_.height() != height) {
+    scissor_.SetRect(x, y, width, height);
+    GL_CALL(glScissor(x, render_target_size_.height() - y - height,
+                      width, height));
+  }
 }
 
 void GraphicsState::EnableBlend() {
@@ -146,10 +173,24 @@
   }
 }
 
-void GraphicsState::ActiveTexture(GLenum texture_unit) {
-  if (texture_unit_ != texture_unit) {
-    texture_unit_ = texture_unit;
-    GL_CALL(glActiveTexture(texture_unit));
+void GraphicsState::ActiveBindTexture(GLenum texture_unit, GLenum target,
+                                      GLuint texture) {
+  int texunit_index = texture_unit - GL_TEXTURE0;
+
+  // Update only if it doesn't match the current state.
+  if (texunit_index >= kNumTextureUnitsCached ||
+      texunit_target_[texunit_index] != target ||
+      texunit_texture_[texunit_index] != texture) {
+    if (texture_unit_ != texture_unit) {
+      texture_unit_ = texture_unit;
+      GL_CALL(glActiveTexture(texture_unit));
+    }
+    GL_CALL(glBindTexture(target, texture));
+
+    if (texunit_index < kNumTextureUnitsCached) {
+      texunit_target_[texunit_index] = target;
+      texunit_texture_[texunit_index] = texture;
+    }
   }
 }
 
@@ -291,22 +332,28 @@
 
   array_buffer_handle_ = 0;
   texture_unit_ = 0;
+  memset(&texunit_target_, 0, sizeof(texunit_target_));
+  memset(&texunit_texture_, 0, sizeof(texunit_texture_));
   enabled_vertex_attrib_array_mask_ = 0;
   disable_vertex_attrib_array_mask_ = 0;
   clip_adjustment_dirty_ = true;
 
-  blend_enabled_ = true;
-  DisableBlend();
+  if (blend_enabled_) {
+    GL_CALL(glEnable(GL_BLEND));
+  } else {
+    GL_CALL(glDisable(GL_BLEND));
+  }
   GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
 
-  depth_test_enabled_ = false;
-  EnableDepthTest();
+  if (depth_test_enabled_) {
+    GL_CALL(glEnable(GL_DEPTH_TEST));
+  } else {
+    GL_CALL(glDisable(GL_DEPTH_TEST));
+  }
+  GL_CALL(glDepthMask(depth_write_enabled_ ? GL_TRUE : GL_FALSE));
   GL_CALL(glDepthFunc(GL_LESS));
   GL_CALL(glDepthRangef(0.0f, 1.0f));
 
-  depth_write_enabled_ = false;
-  EnableDepthWrite();
-
   GL_CALL(glDisable(GL_DITHER));
   GL_CALL(glDisable(GL_CULL_FACE));
   GL_CALL(glDisable(GL_STENCIL_TEST));
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.h b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
index b08af12..489b926 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.h
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
@@ -74,8 +74,9 @@
   void EnableDepthWrite();
   void DisableDepthWrite();
 
-  // Make the specified texture unit active.
-  void ActiveTexture(GLenum texture_unit);
+  // Bind a texture to a given texture unit. Combines glActiveTexture and
+  // glBindTexture.
+  void ActiveBindTexture(GLenum texture_unit, GLenum target, GLuint texture);
 
   // Set the clip adjustment to be used with vertex shaders. This transforms
   // the vertex coordinates from view space to clip space.
@@ -138,6 +139,10 @@
   bool depth_test_enabled_;
   bool depth_write_enabled_;
 
+  static const int kNumTextureUnitsCached = 8;
+  GLenum texunit_target_[kNumTextureUnitsCached];
+  GLuint texunit_texture_[kNumTextureUnitsCached];
+
   static const int kNumFramesBuffered = 3;
   int frame_index_;
 
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index e73ce16..59958a5 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -16,9 +16,9 @@
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
-#include <vector>
 
 #include "base/debug/trace_event.h"
+#include "base/memory/scoped_vector.h"
 #include "base/threading/thread_checker.h"
 #include "cobalt/math/size.h"
 #include "cobalt/renderer/backend/egl/framebuffer.h"
@@ -29,6 +29,7 @@
 #include "cobalt/renderer/frame_rate_throttler.h"
 #include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
 #include "cobalt/renderer/rasterizer/egl/graphics_state.h"
+#include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
 #include "cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h"
 #include "cobalt/renderer/rasterizer/egl/shader_program_manager.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
@@ -45,6 +46,26 @@
 namespace rasterizer {
 namespace egl {
 
+namespace {
+
+int32_t NextPowerOf2(int32_t num) {
+  // Return the smallest power of 2 that is greater than or equal to num.
+  // This flips on all bits <= num, then num+1 will be the next power of 2.
+  --num;
+  num |= num >> 1;
+  num |= num >> 2;
+  num |= num >> 4;
+  num |= num >> 8;
+  num |= num >> 16;
+  return num + 1;
+}
+
+bool IsPowerOf2(int32_t num) {
+  return (num & (num - 1)) == 0;
+}
+
+}  // namespace
+
 class HardwareRasterizer::Impl {
  public:
   explicit Impl(backend::GraphicsContext* graphics_context,
@@ -57,19 +78,36 @@
               const scoped_refptr<backend::RenderTarget>& render_target,
               const Options& options);
 
-  backend::TextureEGL* SubmitToFallbackRasterizer(
+  void SubmitToFallbackRasterizer(
       const scoped_refptr<render_tree::Node>& render_tree,
-      const math::RectF& viewport);
+      const math::RectF& viewport,
+      const backend::TextureEGL** out_texture,
+      math::Matrix3F* out_texcoord_transform);
 
   render_tree::ResourceProvider* GetResourceProvider() {
     return fallback_rasterizer_->GetResourceProvider();
   }
 
  private:
-  GrContext* GetGrContext() {
+  // Use an atlas for offscreen targets.
+  struct OffscreenAtlas {
+    explicit OffscreenAtlas(const math::Size& size) : allocator(size) {}
+    RectAllocator allocator;
+    scoped_ptr<backend::FramebufferEGL> framebuffer;
+    SkAutoTUnref<SkSurface> skia_surface;
+  };
+
+  GrContext* GetFallbackContext() {
     return fallback_rasterizer_->GetGrContext();
   }
 
+  void RasterizeTree(const scoped_refptr<render_tree::Node>& render_tree);
+
+  void AllocateOffscreenTarget(const math::SizeF& size,
+      OffscreenAtlas** out_atlas, math::RectF* out_target_rect);
+
+  OffscreenAtlas* AddOffscreenAtlas(const math::Size& size);
+
   scoped_ptr<skia::HardwareRasterizer> fallback_rasterizer_;
   scoped_ptr<GraphicsState> graphics_state_;
   scoped_ptr<ShaderProgramManager> shader_program_manager_;
@@ -78,14 +116,17 @@
   FrameRateThrottler frame_rate_throttler_;
   base::ThreadChecker thread_checker_;
 
-  static const size_t kMinOffscreenTargets = 3;
-  struct OffscreenTarget {
-    scoped_ptr<backend::FramebufferEGL> framebuffer;
-    SkAutoTUnref<SkSurface> skia_surface;
-  };
-  typedef std::vector<OffscreenTarget*> OffscreenTargetList;
-  OffscreenTargetList used_offscreen_targets_;
-  OffscreenTargetList unused_offscreen_targets_;
+  typedef ScopedVector<OffscreenAtlas> OffscreenAtlasList;
+  OffscreenAtlasList offscreen_atlases_;
+
+  // Size of the smallest offscreen target atlas that can hold all offscreen
+  // targets requested this frame.
+  math::Size offscreen_atlas_size_;
+
+  // Align offscreen targets to a particular size to more efficiently use the
+  // offscreen target atlas. Use a power of 2 for the alignment so that a bit
+  // mask can be used for the alignment calculation.
+  math::Size offscreen_target_size_mask_;
 };
 
 HardwareRasterizer::Impl::Impl(
@@ -100,7 +141,9 @@
           surface_cache_size_in_bytes)),
       graphics_context_(
           base::polymorphic_downcast<backend::GraphicsContextEGL*>(
-              graphics_context)) {
+              graphics_context)),
+      offscreen_atlas_size_(0, 0),
+      offscreen_target_size_mask_(0, 0) {
   backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
       graphics_context_);
 
@@ -113,10 +156,7 @@
       graphics_context_);
 
   GL_CALL(glFinish());
-  while (!unused_offscreen_targets_.empty()) {
-    delete unused_offscreen_targets_.back();
-    unused_offscreen_targets_.pop_back();
-  }
+  offscreen_atlases_.clear();
 
   shader_program_manager_.reset();
   graphics_state_.reset();
@@ -139,129 +179,227 @@
   const math::Size& target_size = render_target->GetSize();
   graphics_state_->SetClipAdjustment(target_size);
   graphics_state_->Viewport(0, 0, target_size.width(), target_size.height());
-  graphics_state_->Scissor(0, 0, target_size.width(), target_size.height());
 
-  {
-    DrawObjectManager draw_object_manager;
-    RenderTreeNodeVisitor::FallbackRasterizeFunction fallback_rasterize =
-        base::Bind(&HardwareRasterizer::Impl::SubmitToFallbackRasterizer,
-                   base::Unretained(this));
-    RenderTreeNodeVisitor visitor(graphics_state_.get(),
-                                  &draw_object_manager,
-                                  &fallback_rasterize);
-
-    {
-      TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
-      render_tree->Accept(&visitor);
-    }
-
-    graphics_state_->BeginFrame();
-
-    {
-      TRACE_EVENT0("cobalt::renderer", "UpdateVertexBuffer");
-      draw_object_manager.ExecuteUpdateVertexBuffer(graphics_state_.get(),
-          shader_program_manager_.get());
-      graphics_state_->UpdateVertexData();
-    }
-
-    {
-      TRACE_EVENT0("cobalt::renderer", "RasterizeOffscreen");
-
-      // Reset the skia graphics context since the egl rasterizer dirtied it.
-      GetGrContext()->resetContext();
-
-      draw_object_manager.ExecuteRasterizeOffscreen(graphics_state_.get(),
-          shader_program_manager_.get());
-      GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
-
-      // Reset the egl graphics state since skia dirtied it.
-      graphics_state_->SetDirty();
-    }
-
-    graphics_state_->Clear();
-
-    {
-      TRACE_EVENT0("cobalt::renderer", "RasterizeNormal");
-      draw_object_manager.ExecuteRasterizeNormal(graphics_state_.get(),
-          shader_program_manager_.get());
-    }
-
-    graphics_state_->EndFrame();
+  // Update only the dirty pixels if the render target contents are preserved
+  // between frames.
+  if (options.dirty && render_target_egl->ContentWasPreservedAfterSwap()) {
+    graphics_state_->Scissor(options.dirty->x(), options.dirty->y(),
+        options.dirty->width(), options.dirty->height());
+  } else {
+    graphics_state_->Scissor(0, 0, target_size.width(), target_size.height());
   }
 
-  // Update the offscreen target cache. Keep an extra frame's worth of targets
-  // around.
-  while (unused_offscreen_targets_.size() > kMinOffscreenTargets &&
-         unused_offscreen_targets_.size() > used_offscreen_targets_.size()) {
-    delete unused_offscreen_targets_.back();
-    unused_offscreen_targets_.pop_back();
+  // Set initial characteristics for offscreen target handling.
+  if (offscreen_atlas_size_.IsEmpty()) {
+    if (target_size.width() >= 64 && target_size.height() >= 64) {
+      offscreen_atlas_size_.SetSize(
+          NextPowerOf2(target_size.width() / 16),
+          NextPowerOf2(target_size.height() / 16));
+      offscreen_target_size_mask_.SetSize(
+          NextPowerOf2(target_size.width() / 64) - 1,
+          NextPowerOf2(target_size.height() / 64) - 1);
+    } else {
+      offscreen_atlas_size_.SetSize(16, 16);
+      offscreen_target_size_mask_.SetSize(0, 0);
+    }
   }
 
-  // Add most recently used offscreen targets to the front.
-  unused_offscreen_targets_.insert(unused_offscreen_targets_.begin(),
-                                   used_offscreen_targets_.begin(),
-                                   used_offscreen_targets_.end());
-  used_offscreen_targets_.clear();
+  // Keep only the largest offscreen target atlas.
+  while (offscreen_atlases_.size() > 1) {
+    offscreen_atlases_.erase(offscreen_atlases_.begin());
+  }
+
+  if (!offscreen_atlases_.empty()) {
+    offscreen_atlases_.back()->allocator.Reset();
+    offscreen_atlases_.back()->skia_surface->getCanvas()->clear(
+        SK_ColorTRANSPARENT);
+  }
+
+  RasterizeTree(render_tree);
 
   frame_rate_throttler_.EndInterval();
   graphics_context_->SwapBuffers(render_target_egl);
   frame_rate_throttler_.BeginInterval();
 }
 
-backend::TextureEGL* HardwareRasterizer::Impl::SubmitToFallbackRasterizer(
+void HardwareRasterizer::Impl::SubmitToFallbackRasterizer(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const math::RectF& viewport) {
+    const math::RectF& viewport,
+    const backend::TextureEGL** out_texture,
+    math::Matrix3F* out_texcoord_transform) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("cobalt::renderer", "SubmitToFallbackRasterizer");
 
-  // Get an offscreen target for rendering.
-  math::Size viewport_size(viewport.width(), viewport.height());
-  OffscreenTarget* offscreen_target = NULL;
-  for (OffscreenTargetList::iterator iter = unused_offscreen_targets_.begin();
-       iter != unused_offscreen_targets_.end(); ++iter) {
-    if ((*iter)->framebuffer->GetSize() == viewport_size) {
-      offscreen_target = *iter;
-      unused_offscreen_targets_.erase(iter);
-      break;
-    }
-  }
+  math::RectF target_rect(0, 0, 0, 0);
+  OffscreenAtlas* atlas = NULL;
+  AllocateOffscreenTarget(viewport.size(), &atlas, &target_rect);
 
-  if (offscreen_target == NULL) {
-    offscreen_target = new OffscreenTarget;
+  backend::FramebufferEGL* framebuffer = atlas->framebuffer.get();
+  SkCanvas* canvas = atlas->skia_surface->getCanvas();
 
-    // Create a new framebuffer.
-    offscreen_target->framebuffer.reset(new backend::FramebufferEGL(
-        graphics_context_, viewport_size, GL_RGBA, GL_NONE));
-
-    // Wrap the framebuffer as a skia surface.
-    GrBackendRenderTargetDesc skia_desc;
-    skia_desc.fWidth = viewport_size.width();
-    skia_desc.fHeight = viewport_size.height();
-    skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
-    skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-    skia_desc.fSampleCnt = 0;
-    skia_desc.fStencilBits = 0;
-    skia_desc.fRenderTargetHandle = offscreen_target->framebuffer->gl_handle();
-
-    SkAutoTUnref<GrRenderTarget> skia_render_target(
-        GetGrContext()->wrapBackendRenderTarget(skia_desc));
-    SkSurfaceProps skia_surface_props(
-        SkSurfaceProps::kUseDistanceFieldFonts_Flag,
-        SkSurfaceProps::kLegacyFontHost_InitType);
-    offscreen_target->skia_surface.reset(SkSurface::NewRenderTargetDirect(
-        skia_render_target, &skia_surface_props));
-  }
-  used_offscreen_targets_.push_back(offscreen_target);
-
-  backend::FramebufferEGL* framebuffer = offscreen_target->framebuffer.get();
-  SkCanvas* canvas = offscreen_target->skia_surface->getCanvas();
-
+  // Use skia to rasterize to the allocated offscreen target.
   canvas->save();
-  canvas->clear(SkColorSetARGB(0, 0, 0, 0));
-  canvas->translate(viewport.x(), viewport.y());
+  canvas->clipRect(SkRect::MakeXYWH(
+      target_rect.x(), target_rect.y(),
+      viewport.width() + 0.5f, viewport.height() + 0.5f));
+  canvas->translate(viewport.x() + target_rect.x(),
+                    viewport.y() + target_rect.y());
   fallback_rasterizer_->SubmitOffscreen(render_tree, canvas);
   canvas->restore();
 
-  return framebuffer->GetColorTexture();
+  float scale_x = 1.0f / framebuffer->GetSize().width();
+  float scale_y = 1.0f / framebuffer->GetSize().height();
+  *out_texcoord_transform = math::Matrix3F::FromValues(
+      viewport.width() * scale_x, 0, target_rect.x() * scale_x,
+      0, viewport.height() * scale_y, target_rect.y() * scale_y,
+      0, 0, 1);
+  *out_texture = framebuffer->GetColorTexture();
+}
+
+void HardwareRasterizer::Impl::RasterizeTree(
+    const scoped_refptr<render_tree::Node>& render_tree) {
+  DrawObjectManager draw_object_manager;
+  RenderTreeNodeVisitor::FallbackRasterizeFunction fallback_rasterize =
+      base::Bind(&HardwareRasterizer::Impl::SubmitToFallbackRasterizer,
+                 base::Unretained(this));
+  RenderTreeNodeVisitor visitor(graphics_state_.get(),
+                                &draw_object_manager,
+                                &fallback_rasterize);
+
+  // Traverse the render tree to populate the draw object manager.
+  {
+    TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
+    render_tree->Accept(&visitor);
+  }
+
+  graphics_state_->BeginFrame();
+
+  // Rasterize to offscreen targets using skia.
+  {
+    TRACE_EVENT0("cobalt::renderer", "OffscreenRasterize");
+
+    // Reset the skia graphics context since the egl rasterizer dirtied it.
+    GetFallbackContext()->resetContext();
+    draw_object_manager.ExecuteOffscreenRasterize(graphics_state_.get(),
+        shader_program_manager_.get());
+
+    {
+      TRACE_EVENT0("cobalt::renderer", "Skia Flush");
+      for (OffscreenAtlasList::iterator iter = offscreen_atlases_.begin();
+           iter != offscreen_atlases_.end(); ++iter) {
+        (*iter)->skia_surface->getCanvas()->flush();
+      }
+    }
+
+    // Reset the egl graphics state since skia dirtied it.
+    graphics_state_->SetDirty();
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+  }
+
+  graphics_state_->Clear();
+
+  {
+    TRACE_EVENT0("cobalt::renderer", "OnscreenUpdateVertexBuffer");
+    draw_object_manager.ExecuteOnscreenUpdateVertexBuffer(graphics_state_.get(),
+        shader_program_manager_.get());
+    graphics_state_->UpdateVertexData();
+  }
+
+  {
+    TRACE_EVENT0("cobalt::renderer", "OnscreenRasterize");
+    draw_object_manager.ExecuteOnscreenRasterize(graphics_state_.get(),
+        shader_program_manager_.get());
+  }
+
+  graphics_state_->EndFrame();
+}
+
+void HardwareRasterizer::Impl::AllocateOffscreenTarget(
+    const math::SizeF& size,
+    OffscreenAtlas** out_atlas, math::RectF* out_target_rect) {
+  // Get an offscreen target for rendering. Align up the requested target size
+  // to improve usage of the atlas (since more requests will have the same
+  // aligned width or height).
+  DCHECK(IsPowerOf2(offscreen_target_size_mask_.width() + 1));
+  DCHECK(IsPowerOf2(offscreen_target_size_mask_.height() + 1));
+  math::Size target_size(
+      (static_cast<int>(size.width() + 0.5f) +
+          offscreen_target_size_mask_.width()) &
+          ~offscreen_target_size_mask_.width(),
+      (static_cast<int>(size.height() + 0.5f) +
+          offscreen_target_size_mask_.height()) &
+          ~offscreen_target_size_mask_.height());
+  math::Rect target_rect(0, 0, 0, 0);
+  OffscreenAtlas* atlas = NULL;
+
+  // See if there's room in the most recently created offscreen target atlas.
+  // Don't search any other atlases since we want to quickly find an offscreen
+  // atlas size large enough to hold all offscreen targets needed in a frame.
+  if (!offscreen_atlases_.empty()) {
+    atlas = offscreen_atlases_.back();
+    target_rect = atlas->allocator.Allocate(target_size);
+  }
+
+  if (target_rect.IsEmpty()) {
+    // Create a new offscreen atlas, bigger than the previous, so that
+    // eventually only one offscreen atlas is needed per frame.
+    bool grew = false;
+    if (offscreen_atlas_size_.width() < target_size.width()) {
+      offscreen_atlas_size_.set_width(NextPowerOf2(target_size.width()));
+      grew = true;
+    }
+    if (offscreen_atlas_size_.height() < target_size.height()) {
+      offscreen_atlas_size_.set_height(NextPowerOf2(target_size.height()));
+      grew = true;
+    }
+    if (!grew) {
+      // Grow the offscreen atlas while keeping it square-ish.
+      if (offscreen_atlas_size_.width() <= offscreen_atlas_size_.height()) {
+        offscreen_atlas_size_.set_width(offscreen_atlas_size_.width() * 2);
+      } else {
+        offscreen_atlas_size_.set_height(offscreen_atlas_size_.height() * 2);
+      }
+    }
+
+    atlas = AddOffscreenAtlas(offscreen_atlas_size_);
+    target_rect = atlas->allocator.Allocate(target_size);
+  }
+  DCHECK(!target_rect.IsEmpty());
+
+  *out_atlas = atlas;
+  *out_target_rect = target_rect;
+}
+
+HardwareRasterizer::Impl::OffscreenAtlas*
+    HardwareRasterizer::Impl::AddOffscreenAtlas(const math::Size& size) {
+  OffscreenAtlas* atlas = new OffscreenAtlas(offscreen_atlas_size_);
+  offscreen_atlases_.push_back(atlas);
+
+  // Create a new framebuffer.
+  atlas->framebuffer.reset(new backend::FramebufferEGL(
+      graphics_context_, offscreen_atlas_size_, GL_RGBA, GL_NONE));
+
+  // Wrap the framebuffer as a skia surface.
+  GrBackendRenderTargetDesc skia_desc;
+  skia_desc.fWidth = offscreen_atlas_size_.width();
+  skia_desc.fHeight = offscreen_atlas_size_.height();
+  skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
+  skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+  skia_desc.fSampleCnt = 0;
+  skia_desc.fStencilBits = 0;
+  skia_desc.fRenderTargetHandle = atlas->framebuffer->gl_handle();
+
+  SkAutoTUnref<GrRenderTarget> skia_render_target(
+      GetFallbackContext()->wrapBackendRenderTarget(skia_desc));
+  SkSurfaceProps skia_surface_props(
+      SkSurfaceProps::kUseDistanceFieldFonts_Flag,
+      SkSurfaceProps::kLegacyFontHost_InitType);
+  atlas->skia_surface.reset(SkSurface::NewRenderTargetDirect(
+      skia_render_target, &skia_surface_props));
+
+  atlas->skia_surface->getCanvas()->clear(SK_ColorTRANSPARENT);
+
+  return atlas;
 }
 
 HardwareRasterizer::HardwareRasterizer(
diff --git a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
index 22d8429..ebb05ff 100644
--- a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
@@ -51,6 +51,8 @@
         'graphics_state.cc',
         'hardware_rasterizer.cc',
         'hardware_rasterizer.h',
+        'rect_allocator.h',
+        'rect_allocator.cc',
         'render_tree_node_visitor.h',
         'render_tree_node_visitor.cc',
         'shader_base.h',
diff --git a/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc b/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
new file mode 100644
index 0000000..2ebb99e
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
@@ -0,0 +1,137 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
+
+#include <algorithm>
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+namespace {
+// Sort by descending area then width and height. This will place smaller
+// blocks at the end of the free list. Allocations will use the block with
+// the smallest area of sufficient dimensions in order to minimize waste.
+  bool FirstRectIsBigger(const math::Rect& a, const math::Rect& b) {
+  const float area_a = a.width() * a.height();
+  const float area_b = b.width() * b.height();
+  return (area_a > area_b) ||
+         (area_a == area_b && (a.width() > b.width() ||
+                               a.height() > b.height()));
+}
+}  // namespace
+
+RectAllocator::RectAllocator() {
+  Reset(math::Size(0, 0));
+}
+
+RectAllocator::RectAllocator(const math::Size& total_size) {
+  Reset(total_size);
+}
+
+void RectAllocator::Reset(const math::Size& total_size) {
+  total_size_ = total_size;
+
+  unused_.clear();
+  unused_.push_back(
+      math::Rect(0, 0, total_size_.width(), total_size_.height()));
+}
+
+math::Rect RectAllocator::Allocate(const math::Size& alloc_size) {
+  math::Rect allocation(0, 0, alloc_size.width(), alloc_size.height());
+
+  // Find the first block that is too small for the requested allocation.
+  MemoryList::iterator pos = std::upper_bound(
+      unused_.begin(), unused_.end(), allocation, FirstRectIsBigger);
+
+  // All blocks before "pos" will have sufficient area, but a sequential search
+  // must be used to find a block of sufficient width and height.
+  while (pos != unused_.begin()) {
+    --pos;
+    if (pos->width() >= allocation.width() &&
+        pos->height() >= allocation.height()) {
+      // Found an unused block big enough for the allocation.
+      math::Rect memory = *pos;
+      unused_.erase(pos);
+
+      // Use top-left of memory block for the allocation, and return the
+      // bottom and right portions as unused blocks. The split can be done
+      // in one of two ways:
+      // +-------+-------+      +-------+-------+
+      // | Alloc | Right |      | Alloc |       |
+      // +-------+-------+  or  +-------+ Right +
+      // |    Bottom     |      |Bottom |       |
+      // +---------------+      +-------+-------+
+      allocation.set_x(memory.x());
+      allocation.set_y(memory.y());
+
+      if (memory.width() != allocation.width()) {
+        if (memory.height() != allocation.height()) {
+          // Return bottom and right of memory block as unused.
+          // Preserve the largest block.
+          const float remaining_width = memory.width() - allocation.width();
+          const float remaining_height = memory.height() - allocation.height();
+          if (memory.width() * remaining_height >=
+              remaining_width * memory.height()) {
+            // Bottom portion is bigger.
+            math::Rect right(memory.x() + allocation.width(), memory.y(),
+                             remaining_width, allocation.height());
+            memory.set_y(memory.y() + allocation.height());
+            memory.set_height(remaining_height);
+            AddUnused(memory);
+            AddUnused(right);
+          } else {
+            // Right portion is bigger.
+            math::Rect bottom(memory.x(), memory.y() + allocation.height(),
+                              allocation.width(), remaining_height);
+            memory.set_x(memory.x() + allocation.width());
+            memory.set_width(remaining_width);
+            AddUnused(memory);
+            AddUnused(bottom);
+          }
+        } else {
+          // Return right of memory block as unused.
+          memory.set_x(memory.x() + allocation.width());
+          memory.set_width(memory.width() - allocation.width());
+          AddUnused(memory);
+        }
+      } else if (memory.height() != allocation.height()) {
+        // Return bottom of memory block as unused.
+        memory.set_y(memory.y() + allocation.height());
+        memory.set_height(memory.height() - allocation.height());
+        AddUnused(memory);
+      }
+
+      return allocation;
+    }
+  }
+
+  // Not enough unused space for the allocation.
+  return math::Rect(0, 0, 0, 0);
+}
+
+void RectAllocator::AddUnused(const math::Rect& memory) {
+  DCHECK_LE(memory.right(), total_size_.width());
+  DCHECK_LE(memory.bottom(), total_size_.height());
+  MemoryList::iterator pos = std::lower_bound(
+      unused_.begin(), unused_.end(), memory, FirstRectIsBigger);
+  unused_.insert(pos, memory);
+}
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/rect_allocator.h b/src/cobalt/renderer/rasterizer/egl/rect_allocator.h
new file mode 100644
index 0000000..19e982d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/rect_allocator.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_RECT_ALLOCATOR_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_RECT_ALLOCATOR_H_
+
+#include <vector>
+
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Manage 2D allocations from a virtual 2D space. This maintains a sorted list
+// of unused memory from which allocations are made using a best-fit search.
+// If an allocation does not exactly fit a memory block, then the unused
+// portions are added to the unused memory list. Upon deallocation, unused
+// memory blocks are not merged, so fragmentation will occur quickly.
+class RectAllocator {
+ public:
+  RectAllocator();
+  explicit RectAllocator(const math::Size& total_size);
+
+  const math::Size& GetTotalSize() const { return total_size_; }
+
+  // Remove all allocations, and optionally reset the total managed memory.
+  void Reset() { Reset(total_size_); }
+  void Reset(const math::Size& total_size);
+
+  math::Rect Allocate(const math::Size& alloc_size);
+  void Deallocate(const math::Rect& allocation) { AddUnused(allocation); }
+
+ private:
+  void AddUnused(const math::Rect& memory);
+
+  math::Size total_size_;     // Total memory space managed by the allocator.
+
+  typedef std::vector<math::Rect> MemoryList;
+  MemoryList unused_;
+};
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_EGL_RECT_ALLOCATOR_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 290da46..bc07839 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -15,6 +15,7 @@
 #include "cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h"
 
 #include <algorithm>
+#include <cmath>
 
 #include "base/debug/trace_event.h"
 #include "cobalt/base/polymorphic_downcast.h"
@@ -46,18 +47,6 @@
          matrix(0, 1) == 0 && matrix(1, 0) == 0;
 }
 
-int32_t NextPowerOf2(int32_t num) {
-  // Return the smallest power of 2 that is greater than or equal to num.
-  // This flips on all bits <= num, then num+1 will be the next power of 2.
-  --num;
-  num |= num >> 1;
-  num |= num >> 2;
-  num |= num >> 4;
-  num |= num >> 8;
-  num |= num >> 16;
-  return num + 1;
-}
-
 }  // namespace
 
 RenderTreeNodeVisitor::RenderTreeNodeVisitor(GraphicsState* graphics_state,
@@ -145,7 +134,20 @@
     }
   }
 
-  NOTIMPLEMENTED();
+  // Handle opacity-only filter.
+  if (data.opacity_filter &&
+      !data.viewport_filter &&
+      !data.blur_filter &&
+      !data.map_to_mesh_filter) {
+    float old_opacity = draw_state_.opacity;
+    draw_state_.opacity *= data.opacity_filter->opacity();
+    data.source->Accept(this);
+    draw_state_.opacity = old_opacity;
+    return;
+  }
+
+  // Use the fallback rasterizer to handle everything else.
+  FallbackRasterize(filter_node, DrawObjectManager::kOffscreenSkiaFilter);
 }
 
 void RenderTreeNodeVisitor::Visit(render_tree::ImageNode* image_node) {
@@ -193,19 +195,21 @@
       scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
           draw_state_, data.destination_rect,
           hardware_image->GetTextureEGL(), texcoord_transform));
-      AddOpaqueDraw(draw.Pass(), DrawObjectManager::kDrawRectTexture);
+      AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenRectTexture,
+          DrawObjectManager::kOffscreenNone);
     } else {
       scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
           draw_state_, data.destination_rect,
-          // Treat alpha as premultiplied in the texture.
-          render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, draw_state_.opacity),
+          render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
           hardware_image->GetTextureEGL(), texcoord_transform));
-      AddTransparentDraw(draw.Pass(), DrawObjectManager::kDrawRectColorTexture,
-                         image_node->GetBounds());
+      AddTransparentDraw(draw.Pass(),
+          DrawObjectManager::kOnscreenRectColorTexture,
+          DrawObjectManager::kOffscreenNone, image_node->GetBounds());
     }
   } else if (skia_image->GetTypeId() ==
              base::GetTypeId<skia::MultiPlaneImage>()) {
-    NOTIMPLEMENTED();
+    FallbackRasterize(image_node,
+                      DrawObjectManager::kOffscreenSkiaMultiPlaneImage);
   } else {
     NOTREACHED();
   }
@@ -217,7 +221,18 @@
     return;
   }
 
-  NOTIMPLEMENTED();
+  const render_tree::PunchThroughVideoNode::Builder& data = video_node->data();
+  math::RectF mapped_rect = draw_state_.transform.MapRect(data.rect);
+  data.set_bounds_cb.Run(
+      math::Rect(static_cast<int>(mapped_rect.x()),
+                 static_cast<int>(mapped_rect.y()),
+                 static_cast<int>(mapped_rect.width()),
+                 static_cast<int>(mapped_rect.height())));
+
+  scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
+      draw_state_, data.rect, render_tree::ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f)));
+  AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
+      DrawObjectManager::kOffscreenNone);
 }
 
 void RenderTreeNodeVisitor::Visit(render_tree::RectNode* rect_node) {
@@ -226,33 +241,49 @@
   }
 
   const render_tree::RectNode::Builder& data = rect_node->data();
-
   const scoped_ptr<render_tree::Brush>& brush = data.background_brush;
-  math::RectF content_rect(data.rect);
-  if (data.border) {
-    content_rect.Inset(data.border->left.width,
-                       data.border->top.width,
-                       data.border->right.width,
-                       data.border->bottom.width);
-  }
 
-  if (data.rounded_corners ||
-      (brush && brush->GetTypeId() !=
-          base::GetTypeId<render_tree::SolidColorBrush>())) {
-    NOTIMPLEMENTED();
+  // Only solid color brushes are supported at this time.
+  const bool brush_supported = !brush || brush->GetTypeId() ==
+      base::GetTypeId<render_tree::SolidColorBrush>();
+
+  // Borders are not supported natively by this rasterizer at this time. The
+  // difficulty lies in getting anti-aliased borders and minimizing state
+  // switches (due to anti-aliased borders requiring transparency). However,
+  // by using the fallback rasterizer, both can be accomplished -- sort to
+  // minimize state switches while rendering anti-aliased borders to the
+  // offscreen target, then use a single shader to render those.
+  const bool border_supported = !data.border;
+
+  if (data.rounded_corners) {
+    FallbackRasterize(rect_node, DrawObjectManager::kOffscreenSkiaRectRounded);
+  } else if (!brush_supported) {
+    FallbackRasterize(rect_node, DrawObjectManager::kOffscreenSkiaRectBrush);
+  } else if (!border_supported) {
+    FallbackRasterize(rect_node, DrawObjectManager::kOffscreenSkiaRectBorder);
   } else {
+    DCHECK(!data.border);
+    const math::RectF& content_rect(data.rect);
+
     // Handle drawing the content.
     if (brush) {
-      render_tree::SolidColorBrush* solid_brush =
-          base::polymorphic_downcast<render_tree::SolidColorBrush*>
+      const render_tree::SolidColorBrush* solid_brush =
+          base::polymorphic_downcast<const render_tree::SolidColorBrush*>
               (brush.get());
+      const render_tree::ColorRGBA& brush_color(solid_brush->color());
+      render_tree::ColorRGBA content_color(
+          brush_color.r() * brush_color.a(),
+          brush_color.g() * brush_color.a(),
+          brush_color.b() * brush_color.a(),
+          brush_color.a());
       scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
-          draw_state_, content_rect, solid_brush->color()));
-      if (draw_state_.opacity * solid_brush->color().a() == 1.0f) {
-        AddOpaqueDraw(draw.Pass(), DrawObjectManager::kDrawPolyColor);
+          draw_state_, content_rect, content_color));
+      if (draw_state_.opacity * content_color.a() == 1.0f) {
+        AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
+            DrawObjectManager::kOffscreenNone);
       } else {
-        AddTransparentDraw(draw.Pass(), DrawObjectManager::kDrawPolyColor,
-                           rect_node->GetBounds());
+        AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
+            DrawObjectManager::kOffscreenNone, rect_node->GetBounds());
       }
     }
   }
@@ -263,7 +294,7 @@
     return;
   }
 
-  NOTIMPLEMENTED();
+  FallbackRasterize(shadow_node, DrawObjectManager::kOffscreenSkiaShadow);
 }
 
 void RenderTreeNodeVisitor::Visit(render_tree::TextNode* text_node) {
@@ -271,41 +302,45 @@
     return;
   }
 
-  FallbackRasterize(text_node);
+  FallbackRasterize(text_node, DrawObjectManager::kOffscreenSkiaText);
 }
 
-void RenderTreeNodeVisitor::FallbackRasterize(render_tree::Node* node) {
+void RenderTreeNodeVisitor::FallbackRasterize(render_tree::Node* node,
+    DrawObjectManager::OffscreenType offscreen_type) {
+  DCHECK_NE(offscreen_type, DrawObjectManager::kOffscreenNone);
+
   // Use fallback_rasterize_ to render to an offscreen target. Add a small
   // buffer to allow anti-aliased edges (e.g. rendered text).
   const int kBorderWidth = 1;
-
   math::RectF node_bounds(node->GetBounds());
-
-  // Use power-of-2 texture sizes to ensure good performance on most GPUs.
-  // Some nodes, like TextNode, may have an internal offset for rendering,
-  // so the offscreen target must accommodate that.
   math::RectF viewport(kBorderWidth, kBorderWidth,
-      NextPowerOf2(node_bounds.right() + 2 * kBorderWidth),
-      NextPowerOf2(node_bounds.bottom() + 2 * kBorderWidth));
-
-  // Adjust the draw rect to accomodate the extra border.
-  math::RectF draw_rect(-kBorderWidth, -kBorderWidth,
       node_bounds.right() + 2 * kBorderWidth,
       node_bounds.bottom() + 2 * kBorderWidth);
 
-  // Use the texcoord transform matrix to handle the change in offscreen
-  // target size to the next power of 2.
-  math::Matrix3F texcoord_transform(math::Matrix3F::FromValues(
-      draw_rect.width() / viewport.width(), 0, 0,
-      0, draw_rect.height() / viewport.height(), 0,
-      0, 0, 1));
+  // Adjust the draw rect to accomodate the extra border and align texels with
+  // pixels. Perform the smallest fractional pixel shift for alignment.
+  float trans_x = std::floor(draw_state_.transform(0, 2) + 0.5f) -
+      draw_state_.transform(0, 2);
+  float trans_y = std::floor(draw_state_.transform(1, 2) + 0.5f) -
+      draw_state_.transform(1, 2);
+  math::RectF draw_rect(-kBorderWidth + trans_x, -kBorderWidth + trans_y,
+      viewport.width(), viewport.height());
 
-  scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
-      draw_state_, draw_rect, base::Bind(*fallback_rasterize_,
-          scoped_refptr<render_tree::Node>(node), viewport),
-      texcoord_transform));
-  AddTransparentDraw(draw.Pass(), DrawObjectManager::kDrawRectTexture,
-                     draw_rect);
+  if (draw_state_.opacity == 1.0f) {
+    scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
+        draw_state_, draw_rect, base::Bind(*fallback_rasterize_,
+            scoped_refptr<render_tree::Node>(node), viewport)));
+    AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenRectTexture,
+        offscreen_type, draw_rect);
+  } else {
+    scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
+        draw_state_, draw_rect, render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+        base::Bind(*fallback_rasterize_,
+            scoped_refptr<render_tree::Node>(node), viewport)));
+    AddTransparentDraw(draw.Pass(),
+        DrawObjectManager::kOnscreenRectColorTexture, offscreen_type,
+        draw_rect);
+  }
 }
 
 bool RenderTreeNodeVisitor::IsVisible(const math::RectF& bounds) {
@@ -315,15 +350,19 @@
 }
 
 void RenderTreeNodeVisitor::AddOpaqueDraw(scoped_ptr<DrawObject> object,
-    DrawObjectManager::DrawType type) {
-  draw_object_manager_->AddOpaqueDraw(object.Pass(), type);
+    DrawObjectManager::OnscreenType onscreen_type,
+    DrawObjectManager::OffscreenType offscreen_type) {
+  draw_object_manager_->AddOpaqueDraw(object.Pass(), onscreen_type,
+      offscreen_type);
   draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
 }
 
 void RenderTreeNodeVisitor::AddTransparentDraw(scoped_ptr<DrawObject> object,
-    DrawObjectManager::DrawType type, const math::RectF& local_bounds) {
-  draw_object_manager_->AddTransparentDraw(object.Pass(), type,
-      draw_state_.transform.MapRect(local_bounds));
+    DrawObjectManager::OnscreenType onscreen_type,
+    DrawObjectManager::OffscreenType offscreen_type,
+    const math::RectF& local_bounds) {
+  draw_object_manager_->AddTransparentDraw(object.Pass(), onscreen_type,
+      offscreen_type, draw_state_.transform.MapRect(local_bounds));
   draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
 }
 
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
index 9176951..704b4bb 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
@@ -19,6 +19,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
+#include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/animations/animate_node.h"
 #include "cobalt/render_tree/composition_node.h"
@@ -45,9 +46,11 @@
 // DrawObjects which must then be processed using calls to ExecuteDraw.
 class RenderTreeNodeVisitor : public render_tree::NodeVisitor {
  public:
-  typedef base::Callback<backend::TextureEGL*(
+  typedef base::Callback<void(
       const scoped_refptr<render_tree::Node>& render_tree,
-      const math::RectF& viewport)>
+      const math::RectF& viewport,
+      const backend::TextureEGL** out_texture,
+      math::Matrix3F* out_texcoord_transform)>
       FallbackRasterizeFunction;
 
   RenderTreeNodeVisitor(GraphicsState* graphics_state,
@@ -68,12 +71,15 @@
   void Visit(render_tree::TextNode* text_node) OVERRIDE;
 
  private:
-  void FallbackRasterize(render_tree::Node* node);
+  void FallbackRasterize(render_tree::Node* node,
+                         DrawObjectManager::OffscreenType offscreen_type);
   bool IsVisible(const math::RectF& bounds);
   void AddOpaqueDraw(scoped_ptr<DrawObject> object,
-                     DrawObjectManager::DrawType type);
+                     DrawObjectManager::OnscreenType onscreen_type,
+                     DrawObjectManager::OffscreenType offscreen_type);
   void AddTransparentDraw(scoped_ptr<DrawObject> object,
-                          DrawObjectManager::DrawType type,
+                          DrawObjectManager::OnscreenType onscreen_type,
+                          DrawObjectManager::OffscreenType offscreen_type,
                           const math::RectF& local_bounds);
 
   GraphicsState* graphics_state_;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index e92f194..d3c5415 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -456,8 +456,7 @@
   } else if (options.dirty) {
     // Only a portion of the display is dirty. Reuse the previous frame
     // if possible.
-    if (render_target_egl->IsContentPreservedOnSwap() &&
-        render_target_egl->swap_count() >= 3) {
+    if (render_target_egl->ContentWasPreservedAfterSwap()) {
       canvas->clipRect(CobaltRectFToSkiaRect(*options.dirty));
     }
   }
@@ -519,11 +518,6 @@
         surface_cache_ ? &surface_cache_.value() : NULL);
     render_tree->Accept(&visitor);
   }
-
-  {
-    TRACE_EVENT0("cobalt::renderer", "Skia Flush");
-    canvas->flush();
-  }
 }
 
 render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
diff --git a/src/cobalt/script/fake_global_environment.h b/src/cobalt/script/fake_global_environment.h
index ba90180..ca8c3fa 100644
--- a/src/cobalt/script/fake_global_environment.h
+++ b/src/cobalt/script/fake_global_environment.h
@@ -51,6 +51,7 @@
   void SetReportEvalCallback(const base::Closure& /*report_eval*/) OVERRIDE {}
   void Bind(const std::string& /*identifier*/,
             const scoped_refptr<Wrappable>& /*impl*/) OVERRIDE {}
+  ScriptValueFactory* script_value_factory() { return NULL; }
 };
 
 }  // namespace script
diff --git a/src/cobalt/script/global_environment.h b/src/cobalt/script/global_environment.h
index 86ebb87..794cc22 100644
--- a/src/cobalt/script/global_environment.h
+++ b/src/cobalt/script/global_environment.h
@@ -21,6 +21,7 @@
 #include "base/optional.h"
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
+#include "cobalt/script/script_value_factory.h"
 #include "cobalt/script/stack_frame.h"
 #include "cobalt/script/wrappable.h"
 
@@ -104,6 +105,11 @@
   virtual void Bind(const std::string& identifier,
                     const scoped_refptr<Wrappable>& impl) = 0;
 
+  // Get the ScriptValueFactory for this global environment. The
+  // GlobalEnvironment instance retains ownership of the ScriptValueFactory and
+  // should live longer than any ScriptValueFactory pointer.
+  virtual ScriptValueFactory* script_value_factory() = 0;
+
  protected:
   virtual ~GlobalEnvironment() {}
   friend class base::RefCounted<GlobalEnvironment>;
diff --git a/src/cobalt/script/mozjs/mozjs.gyp b/src/cobalt/script/mozjs/mozjs.gyp
index 8896c72..aa3d0c0 100644
--- a/src/cobalt/script/mozjs/mozjs.gyp
+++ b/src/cobalt/script/mozjs/mozjs.gyp
@@ -25,9 +25,11 @@
         'mozjs_exception_state.cc',
         'mozjs_global_environment.cc',
         'mozjs_property_enumerator.cc',
+        'mozjs_script_value_factory.cc',
         'mozjs_source_code.cc',
         'mozjs_trace_logging.cc',
         'opaque_root_tracker.cc',
+        'promise_wrapper.cc',
         'proxy_handler.cc',
         'referenced_object_map.cc',
         'util/algorithm_helpers.cc',
@@ -86,10 +88,11 @@
       'target_name': 'mozjs_engine_test',
       'type': '<(gtest_target_type)',
       'sources': [
+        'native_promise_test.cc',
         '<(DEPTH)/third_party/mozjs/test/jscustomallocator_test.cc',
-        'mozjs_trace_logging.cc',
       ],
       'dependencies': [
+        'engine',
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
         '<(DEPTH)/testing/gtest.gyp:gtest',
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.cc b/src/cobalt/script/mozjs/mozjs_exception_state.cc
index 045158f..e2fefbc 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.cc
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.cc
@@ -20,6 +20,7 @@
 #include "base/string_number_conversions.h"
 #include "base/string_util.h"
 #include "cobalt/script/mozjs/conversion_helpers.h"
+#include "third_party/mozjs/js/src/jsexn.h"
 
 namespace cobalt {
 namespace script {
@@ -98,6 +99,18 @@
   is_exception_set_ = true;
 }
 
+JSObject* MozjsExceptionState::CreateErrorObject(JSContext* context,
+                                                 SimpleExceptionType type) {
+  JSExnType mozjs_type = ConvertToMozjsExceptionType(type);
+  JS::RootedObject error_prototype(context);
+  if (!JS_GetClassPrototype(context, GetExceptionProtoKey(mozjs_type),
+                            error_prototype.address())) {
+    DLOG(ERROR) << "Failed to get Error prototype.";
+    return NULL;
+  }
+  return JS_NewObject(context, NULL, error_prototype, NULL);
+}
+
 }  // namespace mozjs
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.h b/src/cobalt/script/mozjs/mozjs_exception_state.h
index 54233b3..86303a9 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.h
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.h
@@ -35,6 +35,9 @@
 
   bool is_exception_set() const { return is_exception_set_; }
 
+  static JSObject* CreateErrorObject(JSContext* context,
+                                     SimpleExceptionType type);
+
  private:
   bool is_exception_set_;
   JSContext* context_;
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc
index 2372ba7..a6bc89f 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -22,6 +22,7 @@
 #include "cobalt/script/mozjs/conversion_helpers.h"
 #include "cobalt/script/mozjs/embedded_resources.h"
 #include "cobalt/script/mozjs/mozjs_exception_state.h"
+#include "cobalt/script/mozjs/mozjs_script_value_factory.h"
 #include "cobalt/script/mozjs/mozjs_source_code.h"
 #include "cobalt/script/mozjs/mozjs_wrapper_handle.h"
 #include "cobalt/script/mozjs/proxy_handler.h"
@@ -159,6 +160,7 @@
   JS_SetErrorReporter(context_, &MozjsGlobalEnvironment::ReportErrorHandler);
 
   wrapper_factory_.reset(new WrapperFactory(context_));
+  script_value_factory_.reset(new MozjsScriptValueFactory(this));
   referenced_objects_.reset(new ReferencedObjectMap(context_));
   opaque_root_tracker_.reset(new OpaqueRootTracker(
       context_, referenced_objects_.get(), wrapper_factory_.get()));
@@ -355,6 +357,11 @@
   DCHECK(success);
 }
 
+ScriptValueFactory* MozjsGlobalEnvironment::script_value_factory() {
+  DCHECK(script_value_factory_);
+  return script_value_factory_.get();
+}
+
 void MozjsGlobalEnvironment::EvaluateAutomatics() {
   TRACK_MEMORY_SCOPE("Javascript");
   std::string source(
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.h b/src/cobalt/script/mozjs/mozjs_global_environment.h
index 6b6b3a9..628631a 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.h
@@ -36,6 +36,7 @@
 namespace script {
 namespace mozjs {
 
+class MozjsScriptValueFactory;
 class ReferencedObjectMap;
 class WeakHandle;
 
@@ -77,6 +78,8 @@
   void Bind(const std::string& identifier,
             const scoped_refptr<Wrappable>& impl) OVERRIDE;
 
+  ScriptValueFactory* script_value_factory() OVERRIDE;
+
   // Evaluates any automatically included Javascript for the environment.
   void EvaluateAutomatics();
 
@@ -167,6 +170,7 @@
   STLValueDeleter<CachedInterfaceData> cached_interface_data_deleter_;
   ContextDestructor context_destructor_;
   scoped_ptr<WrapperFactory> wrapper_factory_;
+  scoped_ptr<MozjsScriptValueFactory> script_value_factory_;
   scoped_ptr<OpaqueRootTracker::OpaqueRootState> opaque_root_state_;
   JS::Heap<JSObject*> global_object_proxy_;
   EnvironmentSettings* environment_settings_;
diff --git a/src/cobalt/script/mozjs/mozjs_script_value_factory.cc b/src/cobalt/script/mozjs/mozjs_script_value_factory.cc
new file mode 100644
index 0000000..c97dda4
--- /dev/null
+++ b/src/cobalt/script/mozjs/mozjs_script_value_factory.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/script/mozjs/mozjs_script_value_factory.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+MozjsScriptValueFactory::MozjsScriptValueFactory(
+    MozjsGlobalEnvironment* global_environment)
+    : global_environment_(global_environment) {}
+}  // namespace mozjs
+
+// Implementation of template function declared in the base class.
+template <typename T>
+scoped_ptr<ScriptValue<Promise<T> > > ScriptValueFactory::CreatePromise() {
+  mozjs::MozjsScriptValueFactory* mozjs_this =
+      base::polymorphic_downcast<mozjs::MozjsScriptValueFactory*>(this);
+  return mozjs_this->CreatePromise<T>();
+}
+
+}  // namespace script
+}  // namespace cobalt
+
+// Explicit template instantiations must go after the template function
+// implementation.
+#include "cobalt/script/script_value_factory_instantiations.h"
diff --git a/src/cobalt/script/mozjs/mozjs_script_value_factory.h b/src/cobalt/script/mozjs/mozjs_script_value_factory.h
new file mode 100644
index 0000000..d6b28a1
--- /dev/null
+++ b/src/cobalt/script/mozjs/mozjs_script_value_factory.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_MOZJS_SCRIPT_VALUE_FACTORY_H_
+#define COBALT_SCRIPT_MOZJS_MOZJS_SCRIPT_VALUE_FACTORY_H_
+
+#include "cobalt/script/mozjs/mozjs_global_environment.h"
+#include "cobalt/script/mozjs/native_promise.h"
+#include "cobalt/script/mozjs/promise_wrapper.h"
+#include "cobalt/script/script_value_factory.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+class MozjsScriptValueFactory : public ScriptValueFactory {
+ public:
+  explicit MozjsScriptValueFactory(MozjsGlobalEnvironment* global_environment);
+  ~MozjsScriptValueFactory() OVERRIDE {}
+
+  template <typename T>
+  scoped_ptr<ScriptValue<Promise<T> > > CreatePromise() {
+    typedef ScriptValue<Promise<T> > ScriptPromiseType;
+    typedef MozjsUserObjectHolder<NativePromise<T> > MozjsPromiseHolderType;
+
+    JSContext* context = global_environment_->context();
+    JS::RootedObject global_object(context,
+                                   global_environment_->global_object());
+    JSAutoRequest auto_request(context);
+    JSAutoCompartment auto_compartment(context, global_object);
+
+    JS::RootedObject promise_wrapper(
+        context, PromiseWrapper::Create(context, global_object));
+    DCHECK(promise_wrapper);
+    scoped_ptr<ScriptPromiseType> promise(new MozjsPromiseHolderType(
+        promise_wrapper, context, global_environment_->wrapper_factory()));
+    return promise.Pass();
+  }
+
+ private:
+  MozjsGlobalEnvironment* global_environment_;
+};
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+#endif  // COBALT_SCRIPT_MOZJS_MOZJS_SCRIPT_VALUE_FACTORY_H_
diff --git a/src/cobalt/script/mozjs/mozjs_user_object_holder.h b/src/cobalt/script/mozjs/mozjs_user_object_holder.h
index ea4f40e..3799221 100644
--- a/src/cobalt/script/mozjs/mozjs_user_object_holder.h
+++ b/src/cobalt/script/mozjs/mozjs_user_object_holder.h
@@ -23,9 +23,8 @@
 #include "cobalt/script/mozjs/wrapper_factory.h"
 #include "cobalt/script/mozjs/wrapper_private.h"
 #include "cobalt/script/script_value.h"
-#include "third_party/mozjs/js/src/jsapi.h"
-
 #include "nb/memory_scope.h"
+#include "third_party/mozjs/js/src/jsapi.h"
 
 namespace cobalt {
 namespace script {
@@ -43,19 +42,29 @@
  public:
   typedef ScriptValue<typename MozjsUserObjectType::BaseType> BaseClass;
 
-  MozjsUserObjectHolder() : context_(NULL), wrapper_factory_(NULL) {}
+  MozjsUserObjectHolder()
+      : context_(NULL),
+        wrapper_factory_(NULL),
+        prevent_garbage_collection_count_(0) {}
 
   MozjsUserObjectHolder(JS::HandleObject object, JSContext* context,
                         WrapperFactory* wrapper_factory)
       : handle_(MozjsUserObjectType(context, object)),
         context_(context),
-        wrapper_factory_(wrapper_factory) {}
+        wrapper_factory_(wrapper_factory),
+        prevent_garbage_collection_count_(0) {}
 
   MozjsUserObjectHolder(JS::HandleValue value, JSContext* context,
                         WrapperFactory* wrapper_factory)
       : handle_(MozjsUserObjectType(context, value)),
         context_(context),
-        wrapper_factory_(wrapper_factory) {}
+        wrapper_factory_(wrapper_factory),
+        prevent_garbage_collection_count_(0) {}
+
+  ~MozjsUserObjectHolder() {
+    DCHECK_EQ(prevent_garbage_collection_count_, 0);
+    DCHECK(!persistent_root_);
+  }
 
   void RegisterOwner(Wrappable* owner) OVERRIDE {
     JSAutoRequest auto_request(context_);
@@ -86,6 +95,25 @@
     }
   }
 
+  void PreventGarbageCollection() OVERRIDE {
+    if (prevent_garbage_collection_count_++ == 0 && handle_) {
+      JSAutoRequest auto_request(context_);
+      persistent_root_ = handle_->value();
+      JSBool result = JS_AddNamedValueRoot(
+          context_, &persistent_root_.value(),
+          "MozjsUserObjectHolder::PreventGarbageCollection");
+      DCHECK(result);
+    }
+  }
+
+  void AllowGarbageCollection() OVERRIDE {
+    if (--prevent_garbage_collection_count_ == 0 && handle_) {
+      JSAutoRequest auto_request(context_);
+      JS_RemoveValueRoot(context_, &persistent_root_.value());
+      persistent_root_ = base::nullopt;
+    }
+  }
+
   const typename MozjsUserObjectType::BaseType* GetScriptValue()
       const OVERRIDE {
     return handle_ ? &handle_.value() : NULL;
@@ -131,9 +159,11 @@
   typedef base::hash_map<const Wrappable*, base::WeakPtr<WrapperPrivate> >
       WrappableAndPrivateHashMap;
 
-  JSContext* context_;
   base::optional<MozjsUserObjectType> handle_;
+  JSContext* context_;
   WrapperFactory* wrapper_factory_;
+  int prevent_garbage_collection_count_;
+  base::optional<JS::Value> persistent_root_;
 };
 
 }  // namespace mozjs
diff --git a/src/cobalt/script/mozjs/native_promise.h b/src/cobalt/script/mozjs/native_promise.h
new file mode 100644
index 0000000..e291197
--- /dev/null
+++ b/src/cobalt/script/mozjs/native_promise.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_NATIVE_PROMISE_H_
+#define COBALT_SCRIPT_MOZJS_NATIVE_PROMISE_H_
+
+#include "cobalt/script/mozjs/conversion_helpers.h"
+#include "cobalt/script/mozjs/mozjs_exception_state.h"
+#include "cobalt/script/mozjs/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs/promise_wrapper.h"
+#include "cobalt/script/mozjs/type_traits.h"
+#include "cobalt/script/mozjs/weak_heap_object.h"
+#include "cobalt/script/promise.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// Shared functionality for NativePromise<T>. Does not implement the Resolve
+// function, since that needs to be specialized for Promise<T>.
+template <typename T>
+class NativePromiseBase : public Promise<T> {
+ public:
+  // ScriptObject boilerplate.
+  typedef Promise<T> BaseType;
+  JSObject* handle() const { return promise_resolver_->get().GetObject(); }
+  const JS::Value& value() const { return promise_resolver_->get().GetValue(); }
+  bool WasCollected() const { return promise_resolver_->get().WasCollected(); }
+
+  // The Promise JS object (not the resolver).
+  JSObject* promise() const { return promise_resolver_->GetPromise(); }
+
+  void Reject() const OVERRIDE {
+    JS::RootedObject promise_resolver(context_,
+                                      promise_resolver_->get().GetObject());
+    if (promise_resolver) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_resolver);
+      promise_resolver_->Reject(JS::UndefinedHandleValue);
+    }
+  }
+
+  void Reject(SimpleExceptionType exception) const OVERRIDE {
+    JS::RootedObject promise_resolver(context_,
+                                      promise_resolver_->get().GetObject());
+    if (promise_resolver) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_resolver);
+      JS::RootedValue error_result(
+          context_, OBJECT_TO_JSVAL(MozjsExceptionState::CreateErrorObject(
+                        context_, exception)));
+      promise_resolver_->Reject(error_result);
+    }
+  }
+
+  void Reject(const scoped_refptr<ScriptException>& result) const OVERRIDE {
+    JS::RootedObject promise_resolver(context_,
+                                      promise_resolver_->get().GetObject());
+    if (promise_resolver) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_resolver);
+      JS::RootedValue converted_result(context_);
+      ToJSValue(context_, result, &converted_result);
+      promise_resolver_->Reject(converted_result);
+    }
+  }
+
+ protected:
+  NativePromiseBase(JSContext* context, JS::HandleObject resolver_object)
+      : context_(context) {
+    promise_resolver_.emplace(context, resolver_object);
+  }
+
+  NativePromiseBase(JSContext* context, JS::HandleValue resolver_value)
+      : context_(context) {
+    DCHECK(resolver_value.isObject());
+    JS::RootedObject resolver_object(context, &resolver_value.toObject());
+    promise_resolver_.emplace(context, resolver_object);
+  }
+
+  JSContext* context_;
+  base::optional<PromiseWrapper> promise_resolver_;
+};
+
+// Implements the Resolve() function for T != void.
+template <typename T>
+class NativePromise : public NativePromiseBase<T> {
+ public:
+  NativePromise(JSContext* context, JS::HandleObject resolver_object)
+      : NativePromiseBase<T>(context, resolver_object) {}
+
+  NativePromise(JSContext* context, JS::HandleValue resolver_value)
+      : NativePromiseBase<T>(context, resolver_value) {}
+
+  void Resolve(const T& value) const OVERRIDE {
+    JS::RootedObject promise_wrapper(
+        this->context_, this->promise_resolver_->get().GetObject());
+    if (promise_wrapper) {
+      JSAutoRequest auto_request(this->context_);
+      JSAutoCompartment auto_compartment(this->context_, promise_wrapper);
+      JS::RootedValue converted_value(this->context_);
+      ToJSValue(this->context_, value, &converted_value);
+      this->promise_resolver_->Resolve(converted_value);
+    }
+  }
+};
+
+// Implements the Resolve() function for T == void.
+template <>
+class NativePromise<void> : public NativePromiseBase<void> {
+ public:
+  NativePromise(JSContext* context, JS::HandleObject resolver_object)
+      : NativePromiseBase<void>(context, resolver_object) {}
+
+  NativePromise(JSContext* context, JS::HandleValue resolver_value)
+      : NativePromiseBase<void>(context, resolver_value) {}
+
+  void Resolve() const OVERRIDE {
+    JS::RootedObject promise_wrapper(context_,
+                                     promise_resolver_->get().GetObject());
+    if (promise_wrapper) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_wrapper);
+      promise_resolver_->Resolve(JS::UndefinedHandleValue);
+    }
+  }
+};
+
+template <typename T>
+struct TypeTraits<NativePromise<T> > {
+  typedef MozjsUserObjectHolder<NativePromise<T> > ConversionType;
+  typedef const ScriptValue<Promise<T> >* ReturnType;
+};
+
+// Promise<T> -> JSValue
+// Note that JSValue -> Promise<T> is not yet supported.
+template <typename T>
+inline void ToJSValue(JSContext* context,
+                      const ScriptValue<Promise<T> >* promise_holder,
+                      JS::MutableHandleValue out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  if (!promise_holder) {
+    out_value.set(JS::NullValue());
+    return;
+  }
+  const MozjsUserObjectHolder<NativePromise<T> >* user_object_holder =
+      base::polymorphic_downcast<
+          const MozjsUserObjectHolder<NativePromise<T> >*>(promise_holder);
+
+  const NativePromise<T>* native_promise =
+      base::polymorphic_downcast<const NativePromise<T>*>(
+          user_object_holder->GetScriptValue());
+
+  DCHECK(native_promise);
+  out_value.setObjectOrNull(native_promise->promise());
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+#endif  // COBALT_SCRIPT_MOZJS_NATIVE_PROMISE_H_
diff --git a/src/cobalt/script/mozjs/native_promise_test.cc b/src/cobalt/script/mozjs/native_promise_test.cc
new file mode 100644
index 0000000..e287f73
--- /dev/null
+++ b/src/cobalt/script/mozjs/native_promise_test.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Simple tests to exercise basic NativePromise functionality. Difficult to
+// verify functionality without bindings support.
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+namespace {
+typedef ScriptValue<Promise<void> > VoidPromise;
+class NativePromiseTest : public ::testing::Test {
+ protected:
+  NativePromiseTest()
+      : environment_settings_(new script::EnvironmentSettings),
+        engine_(script::JavaScriptEngine::CreateEngine()),
+        global_environment_(engine_->CreateGlobalEnvironment(
+            script::JavaScriptEngine::Options())) {
+    global_environment_->CreateGlobalObject();
+  }
+
+  const scoped_ptr<script::EnvironmentSettings> environment_settings_;
+  const scoped_ptr<script::JavaScriptEngine> engine_;
+  const scoped_refptr<script::GlobalEnvironment> global_environment_;
+};
+
+TEST_F(NativePromiseTest, CreateNativePromise) {
+  scoped_ptr<VoidPromise> promise;
+  promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<void>();
+}
+
+TEST_F(NativePromiseTest, ResolveNativePromise) {
+  scoped_ptr<VoidPromise> promise;
+  promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<void>();
+  VoidPromise::StrongReference reference(*promise.get());
+  reference.value().Resolve();
+}
+
+TEST_F(NativePromiseTest, RejectNativePromise) {
+  scoped_ptr<VoidPromise> promise;
+  promise =
+      global_environment_->script_value_factory()->CreateBasicPromise<void>();
+  VoidPromise::StrongReference reference(*promise.get());
+  reference.value().Reject();
+}
+
+}  // namespace
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs/promise_wrapper.cc b/src/cobalt/script/mozjs/promise_wrapper.cc
new file mode 100644
index 0000000..36689cb
--- /dev/null
+++ b/src/cobalt/script/mozjs/promise_wrapper.cc
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/script/mozjs/promise_wrapper.h"
+
+#include "base/logging.h"
+#include "third_party/mozjs/js/src/jsfun.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+namespace {
+enum ReservedSlots {
+  kResolveFunction,
+  kRejectFunction,
+  kPromiseObject,
+  kNumReservedSlots,
+};
+
+JSClass native_promise_class = {
+    "NativePromise",  // name
+
+    JSCLASS_HAS_RESERVED_SLOTS(kNumReservedSlots),  // flags
+
+    JS_PropertyStub,        // addProperty
+    JS_DeletePropertyStub,  // delProperty
+    JS_PropertyStub,        // getProperty
+    JS_StrictPropertyStub,  // setProperty
+    JS_EnumerateStub,       // enumerate
+    JS_ResolveStub,         // resolve
+    JS_ConvertStub,         // convert
+    NULL,                   // finalize
+    NULL,                   // trace
+};
+
+JSBool NativeExecutor(JSContext* context, unsigned argc, JS::Value* vp) {
+  // Get the resolve/reject functions from the call args.
+  JS::CallArgs call_args = CallArgsFromVp(argc, vp);
+  DCHECK_EQ(call_args.length(), 2);
+
+  // Get the this object. Should be the native_promise object.
+  JS::RootedValue this_value(context, call_args.computeThis(context));
+  DCHECK(this_value.isObject());
+  JS::RootedObject this_object(context, JSVAL_TO_OBJECT(this_value));
+  DCHECK_EQ(JS_GetClass(this_object), &native_promise_class);
+
+  // First argument is the resolve function. Second is the reject function.
+  // Stash these in the reserved slots. Reserved slots get visited so there is
+  // no need to define a special trace function.
+  JS::RootedValue resolve_function_value(context, call_args.get(0));
+  JS::RootedValue reject_function_value(context, call_args.get(1));
+  DCHECK(resolve_function_value.isObject());
+  DCHECK(JS_ObjectIsFunction(context, JSVAL_TO_OBJECT(resolve_function_value)));
+  DCHECK(reject_function_value.isObject());
+  DCHECK(JS_ObjectIsFunction(context, JSVAL_TO_OBJECT(reject_function_value)));
+
+  JS_SetReservedSlot(this_object, kResolveFunction, resolve_function_value);
+  JS_SetReservedSlot(this_object, kRejectFunction, reject_function_value);
+  return true;
+}
+
+// Creates a new NativePromise object and initializes its reserved slots.
+JSObject* CreateNativePromise(JSContext* context) {
+  JS::RootedObject native_promise(
+      context, JS_NewObject(context, &native_promise_class, NULL, NULL));
+  DCHECK(native_promise);
+  for (uint32_t i = 0; i < kNumReservedSlots; ++i) {
+    JS_SetReservedSlot(native_promise, i, JS::NullHandleValue);
+  }
+  return native_promise;
+}
+
+// Create a new native function with the |native_promise| bound as |this|.
+JSObject* CreateExecutorArgument(JSContext* context,
+                                 JS::HandleObject native_promise) {
+  JS::RootedObject executor_function(context);
+  executor_function =
+      JS_NewFunction(context, &NativeExecutor, 2, 0, NULL, NULL);
+  DCHECK(executor_function);
+
+  JS::RootedObject bound_executor(context);
+  bound_executor = JS_BindCallable(context, executor_function, native_promise);
+  DCHECK(bound_executor);
+  return bound_executor;
+}
+
+// Get the Promise constructor from the global object.
+JSObject* GetPromiseConstructor(JSContext* context,
+                                JS::HandleObject global_object) {
+  JS::RootedValue promise_constructor_property(context);
+  JSBool result = JS_GetProperty(context, global_object, "Promise",
+                                 promise_constructor_property.address());
+  DCHECK(result);
+  if (!promise_constructor_property.isObject() ||
+      !JS_ObjectIsFunction(context,
+                           JSVAL_TO_OBJECT(promise_constructor_property))) {
+    DLOG(ERROR) << "\"Promise\" property is not a function.";
+    return NULL;
+  }
+  return JSVAL_TO_OBJECT(promise_constructor_property);
+}
+
+void Settle(JSContext* context, JS::HandleValue result,
+            JS::HandleObject resolver, ReservedSlots slot) {
+  JS::RootedValue slot_value(context, JS_GetReservedSlot(resolver, slot));
+  DCHECK(slot_value.isObject());
+  DCHECK(JS_ObjectIsFunction(context, JSVAL_TO_OBJECT(slot_value)));
+
+  JS::RootedValue return_value(context);
+  const size_t kNumArguments = result.isUndefined() ? 0 : 1;
+  JS::Value args[1] = {result};
+  JSBool call_result =
+      JS_CallFunctionValue(context, resolver, slot_value, kNumArguments, args,
+                           return_value.address());
+  if (!call_result) {
+    DLOG(ERROR) << "Exception calling Promise function.";
+    JS_ClearPendingException(context);
+  }
+}
+}  // namespace
+
+JSObject* PromiseWrapper::Create(JSContext* context,
+                                 JS::HandleObject global_object) {
+  // Get the Promise constructor.
+  JS::RootedObject constructor(context,
+                               GetPromiseConstructor(context, global_object));
+  if (!constructor) {
+    DLOG(ERROR) << "Failed to find Promise constructor.";
+    return NULL;
+  }
+  // Create a new NativePromise JS object, and bind it to the NativeExecutor
+  // function.
+  JS::RootedObject promise_wrapper(context, CreateNativePromise(context));
+  DCHECK(promise_wrapper);
+  JS::RootedObject executor(context,
+                            CreateExecutorArgument(context, promise_wrapper));
+  DCHECK(executor);
+
+  // Invoke the Promise constructor with the native executor function.
+  const size_t kNumArguments = 1;
+  JS::Value args[kNumArguments] = {OBJECT_TO_JSVAL(executor)};
+  JS::RootedObject promise_object(context);
+  promise_object = JS_New(context, constructor, kNumArguments, args);
+  if (!promise_object) {
+    DLOG(ERROR) << "Failed to create a new Promise.";
+    return NULL;
+  }
+  // Maintain a handle to the promise object on the NativePromise.
+  JS_SetReservedSlot(promise_wrapper, kPromiseObject,
+                     OBJECT_TO_JSVAL(promise_object));
+
+  return promise_wrapper;
+}
+
+JSObject* PromiseWrapper::GetPromise() const {
+  JS::RootedObject promise(context_);
+  JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+  if (promise_wrapper) {
+    JS::RootedValue slot_value(
+        context_, JS_GetReservedSlot(promise_wrapper, kPromiseObject));
+    DCHECK(slot_value.isObject());
+    promise = JSVAL_TO_OBJECT(slot_value);
+  }
+  return promise;
+}
+
+void PromiseWrapper::Resolve(JS::HandleValue value) const {
+  JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+  if (promise_wrapper) {
+    Settle(context_, value, promise_wrapper, kResolveFunction);
+  }
+}
+
+void PromiseWrapper::Reject(JS::HandleValue value) const {
+  JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+  if (promise_wrapper) {
+    Settle(context_, value, promise_wrapper, kRejectFunction);
+  }
+}
+
+PromiseWrapper::PromiseWrapper(JSContext* context,
+                               JS::HandleObject promise_wrapper)
+    : context_(context), weak_promise_wrapper_(context, promise_wrapper) {
+  DCHECK_EQ(JS_GetClass(promise_wrapper), &native_promise_class);
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs/promise_wrapper.h b/src/cobalt/script/mozjs/promise_wrapper.h
new file mode 100644
index 0000000..1725119
--- /dev/null
+++ b/src/cobalt/script/mozjs/promise_wrapper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_PROMISE_WRAPPER_H_
+#define COBALT_SCRIPT_MOZJS_PROMISE_WRAPPER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/script/mozjs/weak_heap_object.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// Native class that maintains a weak handle to a JS object that is a proxy to
+// a JS Promise object. The wrapper object maintains references to the reject
+// and resolve functions that are passed to the Promise executor function.
+class PromiseWrapper {
+ public:
+  // Creates a new JS object that wraps a new Promise, created using the
+  // Promise constructor on |global_object|. Returns NULL on failure.
+  static JSObject* Create(JSContext* context, JS::HandleObject global_object);
+
+  PromiseWrapper(JSContext* context, JS::HandleObject promise_wrapper);
+
+  const WeakHeapObject& get() const { return weak_promise_wrapper_; }
+  JSObject* GetPromise() const;
+  void Resolve(JS::HandleValue value) const;
+  void Reject(JS::HandleValue value) const;
+
+ private:
+  JSContext* context_;
+  WeakHeapObject weak_promise_wrapper_;
+};
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_MOZJS_PROMISE_WRAPPER_H_
diff --git a/src/cobalt/script/promise.h b/src/cobalt/script/promise.h
new file mode 100644
index 0000000..a3e312c
--- /dev/null
+++ b/src/cobalt/script/promise.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_PROMISE_H_
+#define COBALT_SCRIPT_PROMISE_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/script/exception_message.h"
+#include "cobalt/script/script_exception.h"
+#include "cobalt/script/script_value.h"
+
+namespace cobalt {
+namespace script {
+
+// Interface for interacting with a JavaScript Promise object that is resolved
+// or rejected from native code.
+template <typename T>
+class Promise {
+ public:
+  // Call the |resolve| function that was passed as an argument to the Promise's
+  // executor function supplying |result| as its argument.
+  virtual void Resolve(const T& result) const = 0;
+
+  // Call the |reject| function passed as an argument to the Promise's executor
+  // function.
+  virtual void Reject() const = 0;
+  virtual void Reject(SimpleExceptionType exception) const = 0;
+  virtual void Reject(const scoped_refptr<ScriptException>& result) const = 0;
+  virtual ~Promise() {}
+};
+
+// Specialization of the Promise<T> class for Promise<void>, which does not take
+// a value for the Resolve function.
+template <>
+class Promise<void> {
+ public:
+  // Call the |resolve| function passed as an argument to the Promise's executor
+  // function.
+  virtual void Resolve() const = 0;
+
+  // Call the |reject| function passed as an argument to the Promise's executor
+  // function.
+  virtual void Reject() const = 0;
+  virtual void Reject(SimpleExceptionType exception) const = 0;
+  virtual void Reject(const scoped_refptr<ScriptException>& result) const = 0;
+  virtual ~Promise() {}
+};
+
+}  // namespace script
+}  // namespace cobalt
+#endif  // COBALT_SCRIPT_PROMISE_H_
diff --git a/src/cobalt/script/script_value.h b/src/cobalt/script/script_value.h
index 2fc1eca..0553bd1 100644
--- a/src/cobalt/script/script_value.h
+++ b/src/cobalt/script/script_value.h
@@ -78,6 +78,36 @@
     DISALLOW_COPY_AND_ASSIGN(Reference);
   };
 
+  // Prevent garbage collection of the ScriptValue. This should be used with
+  // care as it can result in resource leaks if not managed appropriately.
+  // A common use case is to create a StrongReference on the stack when a
+  // ScriptValue is passed into a function, but a reference to the ScriptValue
+  // doesn't need to be retained past the scope of the function.
+  class StrongReference {
+   public:
+    explicit StrongReference(const ScriptValue& script_value)
+        : referenced_value_(script_value.MakeCopy()) {
+      DCHECK(referenced_value_);
+      referenced_value_->PreventGarbageCollection();
+    }
+
+    const T& value() const { return *(referenced_value_->GetScriptValue()); }
+
+    // Return the referenced ScriptValue. This ScriptValue can
+    // be passed back into the JavaScript bindings layer where the referenced
+    // JavaScript object can be extracted from the ScriptValue.
+    const ScriptValue<T>& referenced_value() const {
+      return *(referenced_value_.get());
+    }
+
+    ~StrongReference() { referenced_value_->AllowGarbageCollection(); }
+
+   private:
+    scoped_ptr<ScriptValue> referenced_value_;
+
+    DISALLOW_COPY_AND_ASSIGN(StrongReference);
+  };
+
   // Return true iff |other| refers to the same underlying JavaScript object.
   virtual bool EqualTo(const ScriptValue& other) const = 0;
 
@@ -100,6 +130,13 @@
   virtual void RegisterOwner(Wrappable* owner) = 0;
   virtual void DeregisterOwner(Wrappable* owner) = 0;
 
+  // Prevent/Allow garbage collection of the underlying ScriptValue. Calls must
+  // be balanced and are not idempodent. While the number of calls to |Prevent|
+  // are greater than the number of calls to |Allow|, the underlying object
+  // will never be garbage collected.
+  virtual void PreventGarbageCollection() = 0;
+  virtual void AllowGarbageCollection() = 0;
+
   // Return a pointer to the object that wraps the underlying JavaScript object.
   virtual const T* GetScriptValue() const = 0;
 
diff --git a/src/cobalt/script/script_value_factory.h b/src/cobalt/script/script_value_factory.h
new file mode 100644
index 0000000..3c7884b
--- /dev/null
+++ b/src/cobalt/script/script_value_factory.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_SCRIPT_VALUE_FACTORY_H_
+#define COBALT_SCRIPT_SCRIPT_VALUE_FACTORY_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/script/promise.h"
+#include "cobalt/script/script_value.h"
+
+namespace cobalt {
+namespace script {
+class ScriptValueFactory {
+ public:
+  typedef ScriptValue<Promise<scoped_refptr<Wrappable> > >
+      WrappablePromiseValue;
+
+  // Create a Promise<T> where T is void or a primitive type.
+  template <typename T>
+  scoped_ptr<ScriptValue<Promise<T> > > CreateBasicPromise() {
+    return CreatePromise<T>();
+  }
+
+  // Note that Promise<T> where T is an interface will return a
+  // Promise<scoped_refptr<Wrappable>>. Calling code could supply any Wrappable
+  // when calling Resolve. This is because we need to explicitly instantiate
+  // the template specializations for creating Promises. It is not
+  // realistic to instantiate the template specialization for every interface
+  // defined in Cobalt.
+  // TODO: Figure out how to make it so only a scoped_refptr<T> can be accepted.
+  template <typename T>
+  scoped_ptr<WrappablePromiseValue> CreateInterfacePromise() {
+    return CreatePromise<scoped_refptr<Wrappable> >();
+  }
+
+  virtual ~ScriptValueFactory() {}
+
+ private:
+  // Non-virtual template function that will be implemented in the
+  // engine-specific implementation for ScriptValueFactory.
+  template <typename T>
+  scoped_ptr<ScriptValue<Promise<T> > > CreatePromise();
+};
+
+}  // namespace script
+}  // namespace cobalt
+#endif  // COBALT_SCRIPT_SCRIPT_VALUE_FACTORY_H_
diff --git a/src/cobalt/script/script_value_factory_instantiations.h b/src/cobalt/script/script_value_factory_instantiations.h
new file mode 100644
index 0000000..3053837
--- /dev/null
+++ b/src/cobalt/script/script_value_factory_instantiations.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_SCRIPT_VALUE_FACTORY_INSTANTIATIONS_H_
+#define COBALT_SCRIPT_SCRIPT_VALUE_FACTORY_INSTANTIATIONS_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace script {
+
+// Explicit template instantiations for all Promise types that will be used
+// in Cobalt.
+#define PROMISE_TEMPLATE_INSTANTIATION(TYPE)        \
+  template scoped_ptr<ScriptValue<Promise<TYPE> > > \
+  ScriptValueFactory::CreatePromise<TYPE>();
+
+PROMISE_TEMPLATE_INSTANTIATION(void);
+PROMISE_TEMPLATE_INSTANTIATION(bool);
+PROMISE_TEMPLATE_INSTANTIATION(int8_t);
+PROMISE_TEMPLATE_INSTANTIATION(int16_t);
+PROMISE_TEMPLATE_INSTANTIATION(int32_t);
+PROMISE_TEMPLATE_INSTANTIATION(int64_t);
+PROMISE_TEMPLATE_INSTANTIATION(uint8_t);
+PROMISE_TEMPLATE_INSTANTIATION(uint16_t);
+PROMISE_TEMPLATE_INSTANTIATION(uint32_t);
+PROMISE_TEMPLATE_INSTANTIATION(uint64_t);
+PROMISE_TEMPLATE_INSTANTIATION(float);
+PROMISE_TEMPLATE_INSTANTIATION(double);
+PROMISE_TEMPLATE_INSTANTIATION(std::string);
+PROMISE_TEMPLATE_INSTANTIATION(scoped_refptr<Wrappable>);
+
+#undef PROMISE_TEMPLATE_INSTANTIATION
+
+}  // namespace script
+}  // namespace cobalt
+#endif  // COBALT_SCRIPT_SCRIPT_VALUE_FACTORY_INSTANTIATIONS_H_
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index c016372..9f0c58e 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -54,6 +54,14 @@
         'speech_recognition_result_list.h',
         'speech_recognizer.cc',
         'speech_recognizer.h',
+        'speech_synthesis.cc',
+        'speech_synthesis.h',
+        'speech_synthesis_error_event.h',
+        'speech_synthesis_event.cc',
+        'speech_synthesis_event.h',
+        'speech_synthesis_utterance.cc',
+        'speech_synthesis_utterance.h',
+        'speech_synthesis_voice.h',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
diff --git a/src/cobalt/speech/speech_recognition_alternative.h b/src/cobalt/speech/speech_recognition_alternative.h
index 4b66780..780a95b 100644
--- a/src/cobalt/speech/speech_recognition_alternative.h
+++ b/src/cobalt/speech/speech_recognition_alternative.h
@@ -17,6 +17,7 @@
 
 #include <string>
 
+#include "base/basictypes.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
diff --git a/src/cobalt/speech/speech_recognition_error.h b/src/cobalt/speech/speech_recognition_error.h
index 9ae329f..3791df4 100644
--- a/src/cobalt/speech/speech_recognition_error.h
+++ b/src/cobalt/speech/speech_recognition_error.h
@@ -17,6 +17,7 @@
 
 #include <string>
 
+#include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
 #include "cobalt/dom/event.h"
 #include "cobalt/script/wrappable.h"
diff --git a/src/cobalt/speech/speech_recognition_event.h b/src/cobalt/speech/speech_recognition_event.h
index 3cb2f41..a52b90b 100644
--- a/src/cobalt/speech/speech_recognition_event.h
+++ b/src/cobalt/speech/speech_recognition_event.h
@@ -15,6 +15,7 @@
 #ifndef COBALT_SPEECH_SPEECH_RECOGNITION_EVENT_H_
 #define COBALT_SPEECH_SPEECH_RECOGNITION_EVENT_H_
 
+#include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
 #include "cobalt/dom/event.h"
 #include "cobalt/script/wrappable.h"
diff --git a/src/cobalt/speech/speech_recognition_result.h b/src/cobalt/speech/speech_recognition_result.h
index a77b10b..50eb985 100644
--- a/src/cobalt/speech/speech_recognition_result.h
+++ b/src/cobalt/speech/speech_recognition_result.h
@@ -18,6 +18,7 @@
 #include <limits>
 #include <vector>
 
+#include "base/basictypes.h"
 #include "cobalt/script/wrappable.h"
 #include "cobalt/speech/speech_recognition_alternative.h"
 
diff --git a/src/cobalt/speech/speech_recognition_result_list.h b/src/cobalt/speech/speech_recognition_result_list.h
index 6118ad2..dc5efcb 100644
--- a/src/cobalt/speech/speech_recognition_result_list.h
+++ b/src/cobalt/speech/speech_recognition_result_list.h
@@ -18,6 +18,7 @@
 #include <limits>
 #include <vector>
 
+#include "base/basictypes.h"
 #include "cobalt/script/wrappable.h"
 #include "cobalt/speech/speech_recognition_result.h"
 
diff --git a/src/cobalt/speech/speech_synthesis.cc b/src/cobalt/speech/speech_synthesis.cc
new file mode 100644
index 0000000..ca0573f
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis.cc
@@ -0,0 +1,118 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/speech/speech_synthesis.h"
+
+#include <string>
+
+#include "cobalt/dom/navigator.h"
+#include "starboard/speech_synthesis.h"
+
+namespace cobalt {
+namespace speech {
+
+SpeechSynthesis::SpeechSynthesis(const scoped_refptr<dom::Navigator>& navigator)
+    : paused_(false), navigator_(navigator) {
+#if SB_HAS(SPEECH_SYNTHESIS)
+  const char* kVoiceName = "Cobalt";
+  std::string voice_urn(kVoiceName);
+  std::string voice_lang(navigator_->language());
+  voice_urn.append(" ");
+  voice_urn.append(voice_lang);
+  voices_.push_back(
+      new SpeechSynthesisVoice(voice_urn, kVoiceName, voice_lang, false, true));
+#endif
+}
+
+SpeechSynthesis::~SpeechSynthesis() {}
+
+void SpeechSynthesis::set_onvoiceschanged(
+    const EventListenerScriptValue& event_listener) {
+  base::Token event_name = base::Tokens::voiceschanged();
+  SetAttributeEventListener(event_name, event_listener);
+  DispatchEvent(new dom::Event(event_name));
+}
+
+void SpeechSynthesis::Cancel() {
+  for (UtterancesList::iterator utterance_iterator = utterances_.begin();
+       utterance_iterator != utterances_.end(); ++utterance_iterator) {
+    (*utterance_iterator)->DispatchErrorCancelledEvent();
+  }
+  utterances_.clear();
+}
+
+void SpeechSynthesis::Resume() {
+  if (paused_) {
+    paused_ = false;
+    for (UtterancesList::iterator utterance_iterator = utterances_.begin();
+         utterance_iterator != utterances_.end(); ++utterance_iterator) {
+      Speak(*utterance_iterator);
+      if (utterances_.empty()) break;
+    }
+  }
+}
+
+void SpeechSynthesis::DispatchErrorEvent(
+    const scoped_refptr<SpeechSynthesisUtterance>& utterance,
+    SpeechSynthesisErrorEvent::SpeechErrorCode error_code) {
+  utterance->DispatchErrorEvent(error_code);
+  Cancel();
+}
+
+void SpeechSynthesis::Speak(
+    const scoped_refptr<SpeechSynthesisUtterance>& utterance) {
+  if (paused_) {
+    // When the synthesis is paused, the utterance needs to be added to a queue
+    // and preserved until synthesis is canceled or resumed.
+    // A copy of the utterance needs to be made, so that the current state of
+    // the object is preserved.
+    SpeechSynthesisUtterance* copied_utterance =
+        new SpeechSynthesisUtterance(utterance);
+    copied_utterance->SignalPendingSpeak();
+    utterances_.push_back(copied_utterance);
+    return;
+  }
+  utterance->SignalPendingSpeak();
+#if SB_HAS(SPEECH_SYNTHESIS)
+  if (!utterance->lang().empty() &&
+      utterance->lang() != navigator_->language()) {
+    DispatchErrorEvent(utterance,
+                       SpeechSynthesisErrorEvent::kLanguageUnavailable);
+    return;
+  }
+  if ((utterance->volume() != 1.0f) || (utterance->rate() != 1.0f) ||
+      (utterance->pitch() != 1.0f)) {
+    DispatchErrorEvent(utterance, SpeechSynthesisErrorEvent::kInvalidArgument);
+    return;
+  }
+
+#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+  // DEPRECATED IN API VERSION 4
+  std::string language =
+      utterance->lang().empty() ? navigator_->language() : utterance->lang();
+  SbSpeechSynthesisSetLanguage(language.c_str());
+#endif
+  SB_DLOG(INFO) << "Speaking: \"" << utterance->text() << "\" "
+                << utterance->lang();
+  SbSpeechSynthesisSpeak(utterance->text().c_str());
+  utterance->DispatchStartEvent();
+  utterance->DispatchEndEvent();
+#else
+  DispatchErrorEvent(utterance,
+                     SpeechSynthesisErrorEvent::kSynthesisUnavailable);
+#endif
+}
+
+}  // namespace speech
+}  // namespace cobalt
diff --git a/src/cobalt/speech/speech_synthesis.h b/src/cobalt/speech/speech_synthesis.h
new file mode 100644
index 0000000..e067d36
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis.h
@@ -0,0 +1,90 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_SPEECH_SYNTHESIS_H_
+#define COBALT_SPEECH_SPEECH_SYNTHESIS_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/script/sequence.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/speech/speech_synthesis_utterance.h"
+#include "cobalt/speech/speech_synthesis_voice.h"
+#include "starboard/system.h"
+
+namespace cobalt {
+
+namespace dom {
+// Forward declare Navigator, so that we don't pull in it's dependency on
+// media_session.
+class Navigator;
+}  // namespace dom
+
+namespace speech {
+
+// The speech synthesis interface is the scripted web API for controlling a
+// given speech synthesis.
+//   https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+class SpeechSynthesis : public dom::EventTarget {
+ public:
+  typedef script::Sequence<scoped_refptr<SpeechSynthesisVoice> >
+      SpeechSynthesisVoiceSequence;
+
+  explicit SpeechSynthesis(const scoped_refptr<dom::Navigator>& navigator);
+
+  // Readonly Attributes.
+  bool pending() const { return !utterances_.empty(); }
+  bool speaking() const { return false; }
+  bool paused() const { return paused_; }
+
+  // EventHandlers.
+  const EventListenerScriptValue* onvoiceschanged() const {
+    return GetAttributeEventListener(base::Tokens::voiceschanged());
+  }
+
+  void set_onvoiceschanged(const EventListenerScriptValue& event_listener);
+
+  // Functions.
+  void Speak(const scoped_refptr<SpeechSynthesisUtterance>& utterance);
+  void Cancel();
+  void Pause() { paused_ = true; }
+  void Resume();
+  SpeechSynthesisVoiceSequence GetVoices() const { return voices_; }
+
+  DEFINE_WRAPPABLE_TYPE(SpeechSynthesis);
+
+ private:
+  ~SpeechSynthesis() OVERRIDE;
+
+  void DispatchErrorEvent(
+      const scoped_refptr<SpeechSynthesisUtterance>& utterance,
+      SpeechSynthesisErrorEvent::SpeechErrorCode error_code);
+
+  bool paused_;
+  typedef std::list<scoped_refptr<SpeechSynthesisUtterance> > UtterancesList;
+  UtterancesList utterances_;
+  SpeechSynthesisVoiceSequence voices_;
+
+  scoped_refptr<dom::Navigator> navigator_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpeechSynthesis);
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_SPEECH_SYNTHESIS_H_
diff --git a/src/cobalt/speech/speech_synthesis.idl b/src/cobalt/speech/speech_synthesis.idl
new file mode 100644
index 0000000..5a7ce65
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis.idl
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+
+interface SpeechSynthesis {
+  readonly attribute boolean pending;
+  readonly attribute boolean speaking;
+  readonly attribute boolean paused;
+
+  attribute EventHandler onvoiceschanged;
+
+  void speak(SpeechSynthesisUtterance utterance);
+  void cancel();
+  void pause();
+  void resume();
+  sequence<SpeechSynthesisVoice> getVoices();
+};
+
+typedef EventListener? EventHandler;
diff --git a/src/cobalt/speech/speech_synthesis_error_event.h b/src/cobalt/speech/speech_synthesis_error_event.h
new file mode 100644
index 0000000..0e60750
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_error_event.h
@@ -0,0 +1,66 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_SPEECH_SYNTHESIS_ERROR_EVENT_H_
+#define COBALT_SPEECH_SPEECH_SYNTHESIS_ERROR_EVENT_H_
+
+#include "base/basictypes.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/speech/speech_synthesis_event.h"
+
+namespace cobalt {
+namespace speech {
+
+// The SpeechSynthesisErrorEvent is the interface used for the
+// SpeechSynthesisUtterance error event.
+//   https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+class SpeechSynthesisErrorEvent : public SpeechSynthesisEvent {
+ public:
+  enum SpeechErrorCode {
+    kCanceled,
+    kInterrupted,
+    kAudioBusy,
+    kAudioHardware,
+    kNetwork,
+    kSynthesisUnavailable,
+    kSynthesisFailed,
+    kLanguageUnavailable,
+    kVoiceUnavailable,
+    kTextTooLong,
+    kInvalidArgument
+  };
+
+  explicit SpeechSynthesisErrorEvent(
+      SpeechErrorCode error_code,
+      const scoped_refptr<SpeechSynthesisUtterance>& utterance)
+      : SpeechSynthesisEvent(base::Tokens::error(), utterance),
+        error_code_(error_code) {}
+
+  SpeechErrorCode error() const { return error_code_; }
+
+  DEFINE_WRAPPABLE_TYPE(SpeechSynthesisErrorEvent);
+
+ private:
+  ~SpeechSynthesisErrorEvent() OVERRIDE {}
+
+  const SpeechErrorCode error_code_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisErrorEvent);
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_SPEECH_SYNTHESIS_ERROR_EVENT_H_
diff --git a/src/cobalt/speech/speech_synthesis_error_event.idl b/src/cobalt/speech/speech_synthesis_error_event.idl
new file mode 100644
index 0000000..8cd554f
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_error_event.idl
@@ -0,0 +1,33 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+
+enum SpeechErrorCode {
+  "canceled",
+  "interrupted",
+  "audio-busy",
+  "audio-hardware",
+  "network",
+  "synthesis-unavailable",
+  "synthesis-failed",
+  "language-unavailable",
+  "voice-unavailable",
+  "text-too-long",
+  "invalid-argument",
+};
+
+interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent {
+  readonly attribute SpeechErrorCode error;
+};
diff --git a/src/cobalt/speech/speech_synthesis_event.cc b/src/cobalt/speech/speech_synthesis_event.cc
new file mode 100644
index 0000000..1c185c8
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_event.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/speech/speech_synthesis_event.h"
+
+#include "cobalt/speech/speech_synthesis_utterance.h"
+
+namespace cobalt {
+namespace speech {
+
+SpeechSynthesisEvent::SpeechSynthesisEvent(
+    base::Token event_name,
+    const scoped_refptr<SpeechSynthesisUtterance>& utterance)
+    : Event(event_name, Event::kBubbles, Event::kNotCancelable),
+      utterance_(utterance) {}
+
+SpeechSynthesisEvent::~SpeechSynthesisEvent() {}
+
+}  // namespace speech
+}  // namespace cobalt
diff --git a/src/cobalt/speech/speech_synthesis_event.h b/src/cobalt/speech/speech_synthesis_event.h
new file mode 100644
index 0000000..7d44583
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_event.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_SPEECH_SYNTHESIS_EVENT_H_
+#define COBALT_SPEECH_SPEECH_SYNTHESIS_EVENT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace speech {
+
+class SpeechSynthesisUtterance;
+
+// The speech synthesis event interface is the scripted web API for defining a
+// speech synthesis event.
+//   https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html#speechsynthesisevent
+class SpeechSynthesisEvent : public dom::Event {
+ public:
+  SpeechSynthesisEvent(
+      base::Token event_name,
+      const scoped_refptr<SpeechSynthesisUtterance>& utterance);
+
+  ~SpeechSynthesisEvent();
+
+  // Readonly Attributes.
+  const scoped_refptr<SpeechSynthesisUtterance>& utterance() const {
+    return utterance_;
+  }
+  const base::optional<unsigned int> char_index() const { return char_index_; }
+  const base::optional<float> elapsed_time() const { return elapsed_time_; }
+  const base::optional<std::string>& name() const { return name_; }
+
+  DEFINE_WRAPPABLE_TYPE(SpeechSynthesisEvent);
+
+ private:
+  const scoped_refptr<SpeechSynthesisUtterance> utterance_;
+  const base::optional<unsigned int> char_index_;
+  const base::optional<float> elapsed_time_;
+  const base::optional<std::string> name_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisEvent);
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_SPEECH_SYNTHESIS_EVENT_H_
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/cobalt/speech/speech_synthesis_event.idl
similarity index 60%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/cobalt/speech/speech_synthesis_event.idl
index b75f65f..f9b6b90 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/cobalt/speech/speech_synthesis_event.idl
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
 
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
-}
+interface SpeechSynthesisEvent : Event {
+  readonly attribute SpeechSynthesisUtterance utterance;
+  readonly attribute unsigned long charIndex;
+  readonly attribute float elapsedTime;
+  readonly attribute DOMString name;
+};
diff --git a/src/cobalt/speech/speech_synthesis_utterance.cc b/src/cobalt/speech/speech_synthesis_utterance.cc
new file mode 100644
index 0000000..ca96359
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_utterance.cc
@@ -0,0 +1,80 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/speech/speech_synthesis_utterance.h"
+
+namespace cobalt {
+namespace speech {
+
+SpeechSynthesisUtterance::SpeechSynthesisUtterance()
+    : volume_(1.0f), rate_(1.0f), pitch_(1.0f), pending_speak_(false) {}
+SpeechSynthesisUtterance::SpeechSynthesisUtterance(const std::string& text)
+    : text_(text), pending_speak_(false) {}
+
+SpeechSynthesisUtterance::SpeechSynthesisUtterance(
+    const scoped_refptr<SpeechSynthesisUtterance>& utterance)
+    : text_(utterance->text_),
+      lang_(utterance->lang_),
+      voice_(utterance->voice_),
+      volume_(utterance->volume_),
+      rate_(utterance->rate_),
+      pitch_(utterance->pitch_),
+      pending_speak_(false) {
+  set_onstart(EventListenerScriptValue::Reference(this, *utterance->onstart())
+                  .referenced_value());
+  set_onend(EventListenerScriptValue::Reference(this, *utterance->onend())
+                .referenced_value());
+  set_onerror(EventListenerScriptValue::Reference(this, *utterance->onerror())
+                  .referenced_value());
+  set_onpause(EventListenerScriptValue::Reference(this, *utterance->onpause())
+                  .referenced_value());
+  set_onresume(EventListenerScriptValue::Reference(this, *utterance->onresume())
+                   .referenced_value());
+  set_onmark(EventListenerScriptValue::Reference(this, *utterance->onmark())
+                 .referenced_value());
+  set_onboundary(
+      EventListenerScriptValue::Reference(this, *utterance->onboundary())
+          .referenced_value());
+}
+
+SpeechSynthesisUtterance::~SpeechSynthesisUtterance() {
+  SB_DCHECK(!pending_speak_) << "Destructed utterance has a pending Speak()";
+}
+
+void SpeechSynthesisUtterance::DispatchErrorCancelledEvent() {
+  if (pending_speak_) {
+    SB_DLOG(INFO) << "Utterance has a pending Speak()";
+    DispatchErrorEvent(SpeechSynthesisErrorEvent::kCanceled);
+  }
+}
+
+void SpeechSynthesisUtterance::SignalPendingSpeak() { pending_speak_ = true; }
+
+void SpeechSynthesisUtterance::DispatchStartEvent() {
+  DispatchEvent(new SpeechSynthesisEvent(base::Tokens::start(), this));
+}
+
+void SpeechSynthesisUtterance::DispatchEndEvent() {
+  pending_speak_ = false;
+  DispatchEvent(new SpeechSynthesisEvent(base::Tokens::end(), this));
+}
+
+void SpeechSynthesisUtterance::DispatchErrorEvent(
+    SpeechSynthesisErrorEvent::SpeechErrorCode error_code) {
+  pending_speak_ = false;
+  DispatchEvent(new SpeechSynthesisErrorEvent(error_code, this));
+}
+
+}  // namespace speech
+}  // namespace cobalt
diff --git a/src/cobalt/speech/speech_synthesis_utterance.h b/src/cobalt/speech/speech_synthesis_utterance.h
new file mode 100644
index 0000000..9c476c3
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_utterance.h
@@ -0,0 +1,150 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_SPEECH_SYNTHESIS_UTTERANCE_H_
+#define COBALT_SPEECH_SPEECH_SYNTHESIS_UTTERANCE_H_
+
+#include <string>
+
+#include "cobalt/dom/event_target.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/speech/speech_synthesis_error_event.h"
+#include "cobalt/speech/speech_synthesis_voice.h"
+
+namespace cobalt {
+namespace speech {
+
+// The speech synthesis voice interface is the scripted web API for defining a
+// speech synthesis voice.
+//   https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#speechsynthesisvoice
+class SpeechSynthesisUtterance : public dom::EventTarget {
+ public:
+  SpeechSynthesisUtterance();
+  SpeechSynthesisUtterance(
+      const scoped_refptr<SpeechSynthesisUtterance>& utterance);
+  explicit SpeechSynthesisUtterance(const std::string& text);
+
+  // Web API: SpeechSynthesisUtterance
+  //
+
+  // Read+Write Attributes.
+  std::string text() const { return text_; }
+  void set_text(const std::string& text) { text_ = text; }
+
+  std::string lang() const { return lang_; }
+  void set_lang(const std::string& lang) { lang_ = lang; }
+
+  scoped_refptr<SpeechSynthesisVoice> voice() const { return voice_; }
+  void set_voice(const scoped_refptr<SpeechSynthesisVoice>& voice) {
+    voice_ = voice;
+  }
+
+  float volume() const { return volume_; }
+  void set_volume(const float volume) { volume_ = volume; }
+
+  float rate() const { return rate_; }
+  void set_rate(const float rate) { rate_ = rate; }
+
+  float pitch() const { return pitch_; }
+  void set_pitch(const float pitch) { pitch_ = pitch; }
+
+  // EventHandlers.
+  const EventListenerScriptValue* onstart() const {
+    return GetAttributeEventListener(base::Tokens::start());
+  }
+
+  void set_onstart(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::start(), event_listener);
+  }
+
+  const EventListenerScriptValue* onend() const {
+    return GetAttributeEventListener(base::Tokens::end());
+  }
+
+  void set_onend(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::end(), event_listener);
+  }
+
+  const EventListenerScriptValue* onerror() const {
+    return GetAttributeEventListener(base::Tokens::error());
+  }
+
+  void set_onerror(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::error(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpause() const {
+    return GetAttributeEventListener(base::Tokens::pause());
+  }
+
+  void set_onpause(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pause(), event_listener);
+  }
+
+  const EventListenerScriptValue* onresume() const {
+    return GetAttributeEventListener(base::Tokens::resume());
+  }
+
+  void set_onresume(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::resume(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmark() const {
+    return GetAttributeEventListener(base::Tokens::mark());
+  }
+
+  void set_onmark(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mark(), event_listener);
+  }
+
+  const EventListenerScriptValue* onboundary() const {
+    return GetAttributeEventListener(base::Tokens::boundary());
+  }
+
+  void set_onboundary(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::boundary(), event_listener);
+  }
+
+  // Custom, not in any spec.
+  //
+  void SignalPendingSpeak();
+  void DispatchErrorCancelledEvent();
+
+  void DispatchStartEvent();
+  void DispatchEndEvent();
+  void DispatchErrorEvent(
+      SpeechSynthesisErrorEvent::SpeechErrorCode error_code);
+
+  DEFINE_WRAPPABLE_TYPE(SpeechSynthesisUtterance);
+
+ private:
+  ~SpeechSynthesisUtterance() OVERRIDE;
+
+  std::string text_;
+  std::string lang_;
+  scoped_refptr<SpeechSynthesisVoice> voice_;
+
+  float volume_;
+  float rate_;
+  float pitch_;
+
+  bool pending_speak_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisUtterance);
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_SPEECH_SYNTHESIS_UTTERANCE_H_
diff --git a/src/cobalt/speech/speech_synthesis_utterance.idl b/src/cobalt/speech/speech_synthesis_utterance.idl
new file mode 100644
index 0000000..d24bf0a
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_utterance.idl
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+
+[
+  Constructor,
+  Constructor(DOMString text)
+]
+interface SpeechSynthesisUtterance : EventTarget {
+  attribute DOMString text;
+  attribute DOMString lang;
+  attribute SpeechSynthesisVoice voice;
+  attribute float volume;
+  attribute float rate;
+  attribute float pitch;
+
+  attribute EventHandler onstart;
+  attribute EventHandler onend;
+  attribute EventHandler onerror;
+  attribute EventHandler onpause;
+  attribute EventHandler onresume;
+  attribute EventHandler onmark;
+  attribute EventHandler onboundary;
+};
+
+typedef EventListener? EventHandler;
diff --git a/src/cobalt/speech/speech_synthesis_voice.h b/src/cobalt/speech/speech_synthesis_voice.h
new file mode 100644
index 0000000..f1289e4
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_voice.h
@@ -0,0 +1,63 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_SPEECH_SYNTHESIS_VOICE_H_
+#define COBALT_SPEECH_SPEECH_SYNTHESIS_VOICE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace speech {
+
+// The speech synthesis voice interface is the scripted web API for defining a
+// speech synthesis voice.
+//   https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#speechsynthesisvoice
+class SpeechSynthesisVoice : public script::Wrappable {
+ public:
+  SpeechSynthesisVoice(const std::string& voice_uri, const std::string& name,
+                       const std::string& lang, const bool local_service,
+                       const bool default_voice)
+      : voice_uri_(voice_uri),
+        name_(name),
+        lang_(lang),
+        local_service_(local_service),
+        default_(default_voice) {}
+  ~SpeechSynthesisVoice() OVERRIDE {}
+
+  // Readonly Attributes.
+  const std::string& voice_uri() const { return voice_uri_; }
+  const std::string& name() const { return name_; }
+  const std::string& lang() const { return lang_; }
+  bool local_service() const { return local_service_; }
+  bool attribute_default() const { return default_; }
+
+  DEFINE_WRAPPABLE_TYPE(SpeechSynthesisVoice);
+
+ private:
+  const std::string voice_uri_;
+  const std::string name_;
+  const std::string lang_;
+  const bool local_service_;
+  const bool default_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisVoice);
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_SPEECH_SYNTHESIS_VOICE_H_
diff --git a/src/cobalt/speech/speech_synthesis_voice.idl b/src/cobalt/speech/speech_synthesis_voice.idl
new file mode 100644
index 0000000..472552e
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_voice.idl
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+
+interface SpeechSynthesisVoice {
+  readonly attribute DOMString voiceURI;
+  readonly attribute DOMString name;
+  readonly attribute DOMString lang;
+  readonly attribute boolean localService;
+
+  // Not supported by IDL compiler: Results in a member using a reserved
+  // keyword, IDL compiler doesn't support ImplementedAs.
+  // [ImplementedAs=attribute_default] readonly attribute boolean default;
+};
diff --git a/src/cobalt/webdriver/webdriver.gyp b/src/cobalt/webdriver/webdriver.gyp
index 24bd490..6fd1acf 100644
--- a/src/cobalt/webdriver/webdriver.gyp
+++ b/src/cobalt/webdriver/webdriver.gyp
@@ -84,6 +84,7 @@
       'dependencies': [
         '<(DEPTH)/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/net/net.gyp:http_server',
         'copy_webdriver_data',
       ],
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index 86b2655..d970680 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -37,6 +37,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
       ],
     },
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp
index a1a836d..1fbd0c0 100644
--- a/src/cobalt/xhr/xhr.gyp
+++ b/src/cobalt/xhr/xhr.gyp
@@ -30,6 +30,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
       ],
     },
@@ -43,6 +44,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
index a6b7f28..b74b39a 100644
--- a/src/media/base/starboard_player.cc
+++ b/src/media/base/starboard_player.cc
@@ -157,6 +157,8 @@
   ++ticket_;
 #if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
   SbPlayerSetPause(player_, true);
+#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+  SbPlayerSetPlaybackRate(player_, 0.f);
 #endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
   seek_pending_ = true;
 }
@@ -185,6 +187,8 @@
   seek_pending_ = false;
 #if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
   SbPlayerSetPause(player_, playback_rate_ == 0.0);
+#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+  SbPlayerSetPlaybackRate(player_, playback_rate_);
 #endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
 }
 
@@ -331,37 +335,19 @@
   DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
 #endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
 
-#if SB_API_VERSION <= 3
-
   player_ = SbPlayerCreate(
       window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
       &audio_header, &StarboardPlayer::DeallocateSampleCB,
       &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
-#if SB_API_VERSION == 3
-      ,
-      ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider()  // provider
-#endif  // SB_API_VERSION == 3
-      );
-
-#else  //  SB_API_VERSION <= 3
-
-  player_ = SbPlayerCreate(
-      window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
-      &audio_header,
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-      NULL,
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-      &StarboardPlayer::DeallocateSampleCB, &StarboardPlayer::DecoderStatusCB,
-      &StarboardPlayer::PlayerStatusCB,
 #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-      output_mode_,
+      ,
+      output_mode_
 #endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
 #if SB_API_VERSION >= 3
-      ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider(),  // provider
+      ,
+      ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider()  // provider
 #endif  // SB_API_VERSION >= 3
-      this);
-
-#endif  //  SB_API_VERSION <= 3
+      );
 
 #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
diff --git a/src/nb/analytics/memory_tracker_impl_test.cc b/src/nb/analytics/memory_tracker_impl_test.cc
index 970520c..26e3500 100644
--- a/src/nb/analytics/memory_tracker_impl_test.cc
+++ b/src/nb/analytics/memory_tracker_impl_test.cc
@@ -25,6 +25,35 @@
 #define STRESS_TEST_DURATION_SECONDS 1
 #define NUM_STRESS_TEST_THREADS 3
 
+// The following is necessary to prevent operator new from being optimized
+// out using some compilers. This is required because we rely on operator new
+// to report memory usage. Overly aggressive optimizing compilers will
+// eliminate the call to operator new, even though it causes a lost of side
+// effects. This will therefore break the memory reporting mechanism. This is
+// a bug in the compiler.
+//
+// The solution here is to use macro-replacement to substitute calls to global
+// new to instead be delegated to our custom new, which prevents elimination
+// by using a temporay volatile.
+namespace {
+struct CustomObject {
+  static CustomObject Make() {
+    CustomObject o;
+    return o;
+  }
+};
+}
+
+void* operator new(std::size_t size, CustomObject ignored) {
+  SB_UNREFERENCED_PARAMETER(ignored);
+  // Volitile prevent optimization and elmination of operator new.
+  volatile void* ptr = SbMemoryAllocate(size);
+  return const_cast<void*>(ptr);
+}
+
+#define NEW_NO_OPTIMIZER_ELIMINATION new (CustomObject::Make())
+#define new NEW_NO_OPTIMIZER_ELIMINATION
+
 namespace nb {
 namespace analytics {
 namespace {
diff --git a/src/nb/lexical_cast.h b/src/nb/lexical_cast.h
index 0ce3a20..8b576c1 100644
--- a/src/nb/lexical_cast.h
+++ b/src/nb/lexical_cast.h
@@ -83,16 +83,46 @@
   return static_cast<int8_t>(value_i16);
 }
 
+template <typename SmallInt>
+SmallInt NarrowingLexicalCast(const char* s, bool* cast_ok) {
+  int64_t value = lexical_cast<int64_t>(s, cast_ok);
+  if ((value > std::numeric_limits<SmallInt>::max()) ||
+      (value < std::numeric_limits<SmallInt>::min())) {
+    if (cast_ok) {
+      *cast_ok = false;
+    }
+    return static_cast<SmallInt>(0);
+  }
+  return static_cast<SmallInt>(value);
+}
+
 template <>
 uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) {
-  uint16_t value_i16 = lexical_cast<uint16_t>(s, cast_ok);
-  if (value_i16 > std::numeric_limits<uint8_t>::max()) {
-    value_i16 = 0;
+  return NarrowingLexicalCast<uint8_t>(s, cast_ok);
+}
+
+template <>
+uint16_t lexical_cast<uint16_t>(const char* s, bool* cast_ok) {
+  return NarrowingLexicalCast<uint16_t>(s, cast_ok);
+}
+
+template <>
+uint32_t lexical_cast<uint32_t>(const char* s, bool* cast_ok) {
+  return NarrowingLexicalCast<uint32_t>(s, cast_ok);
+}
+
+// uint64_t types will have a max value of int64_t. But this is acceptable.
+template <>
+uint64_t lexical_cast<uint64_t>(const char* s, bool* cast_ok) {
+  int64_t val_i64 = lexical_cast<int64_t>(s, cast_ok);
+  // Handle failure condition for negative values.
+  if (val_i64 < 0) {
+    val_i64 = 0;
     if (cast_ok) {
       *cast_ok = false;
     }
   }
-  return static_cast<uint8_t>(value_i16);
+  return static_cast<uint64_t>(val_i64);
 }
 
 }  // namespace nb
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 5ce779d..4c4b6c5 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -55,6 +55,7 @@
             'scoped_ptr.h',
             'simple_thread.cc',
             'simple_thread.h',
+            'std_allocator.h',
             'string_interner.cc',
             'string_interner.h',
             'thread_collision_warner.cc',
@@ -95,6 +96,7 @@
             'reuse_allocator_test.cc',
             'rewindable_vector_test.cc',
             'run_all_unittests.cc',
+            'std_allocator_test.cc',
             'string_interner_test.cc',
             'test_thread.h',
             'thread_local_object_test.cc',
diff --git a/src/nb/std_allocator.h b/src/nb/std_allocator.h
new file mode 100644
index 0000000..ce3a818
--- /dev/null
+++ b/src/nb/std_allocator.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NB_STD_ALLOCATOR_H_
+#define NB_STD_ALLOCATOR_H_
+
+#include <memory>
+
+#include "starboard/types.h"
+
+namespace nb {
+
+// A standard container compatible allocator that delegates allocations to a
+// custom allocator. This allows standard containers like vector<> and map<> to
+// use custom allocator schemes.
+//
+// AllocatorT:
+//   This is the backing allocator that implements Allocate(...) and
+//   Deallocate(...).
+//
+// Example:
+// struct AllocatorImpl {
+//   static void* Allocate(size_t n) {
+//     return SbMemoryAllocate(n);
+//   }
+//   // Second argument can be used for accounting, but is otherwise optional.
+//   static void Deallocate(void* ptr, size_t n) {
+//     SbMemoryDeallocate(ptr);
+//   }
+// };
+//
+// typedef std::vector<int, StdAllocator<T, AllocatorImpl> > MyVector;
+// MyVector vector;
+// ...
+template <typename T, typename AllocatorT>
+class StdAllocator : public std::allocator<T> {
+ public:
+  typedef typename std::allocator<T>::pointer pointer;
+  typedef typename std::allocator<T>::const_pointer const_pointer;
+  typedef typename std::allocator<T>::reference reference;
+  typedef typename std::allocator<T>::const_reference const_reference;
+  typedef typename std::allocator<T>::size_type size_type;
+  typedef typename std::allocator<T>::value_type value_type;
+  typedef typename std::allocator<T>::difference_type difference_type;
+
+  StdAllocator() {}
+
+  // Constructor used for rebinding
+  template <typename U, typename V>
+  StdAllocator(const StdAllocator<U, V>& x) {}
+
+  pointer allocate(size_type n,
+                   std::allocator<void>::const_pointer hint = NULL) {
+    void* ptr = AllocatorT::Allocate(n * sizeof(value_type));
+    return static_cast<pointer>(ptr);
+  }
+
+  void deallocate(pointer ptr, size_type n) {
+    AllocatorT::Deallocate(ptr, n * sizeof(value_type));
+  }
+  template <typename U>
+  struct rebind {
+    typedef StdAllocator<U, AllocatorT> other;
+  };
+};
+
+}  // namespace nb
+
+#endif  // NB_STD_ALLOCATOR_H_
diff --git a/src/nb/std_allocator_test.cc b/src/nb/std_allocator_test.cc
new file mode 100644
index 0000000..06d634e
--- /dev/null
+++ b/src/nb/std_allocator_test.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nb/std_allocator.h"
+
+#include <map>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace nb {
+namespace {
+
+struct CountingAllocator {
+  static int num_allocs;
+  static int64_t num_bytes;
+  static void Reset() {
+    num_allocs = 0;
+    num_bytes = 0;
+  }
+
+  static void* Allocate(size_t n) {
+    ++num_allocs;
+    num_bytes += n;
+    return SbMemoryAllocate(n);
+  }
+
+  static void Deallocate(void* ptr, size_t n) {
+    num_bytes -= n;
+    --num_allocs;
+    SbMemoryDeallocate(ptr);
+  }
+};
+int CountingAllocator::num_allocs = 0;
+int64_t CountingAllocator::num_bytes = 0;
+
+// Test the expectation that vector will go through the supplied allocator.
+TEST(StdAllocator, vector) {
+  CountingAllocator::Reset();
+
+  typedef std::vector<int, StdAllocator<int, CountingAllocator> > IntVector;
+
+  EXPECT_EQ(0, CountingAllocator::num_allocs);
+  EXPECT_EQ(0, CountingAllocator::num_bytes);
+
+  IntVector* int_vector = new IntVector;
+  int_vector->push_back(0);
+
+  for (int i = 0; i < 10; ++i) {
+    int_vector->push_back(i);
+  }
+
+  // We aren't sure how much allocation is here, but we know it's more than 0.
+  EXPECT_LT(0, CountingAllocator::num_allocs);
+  EXPECT_LT(0, CountingAllocator::num_bytes);
+
+  delete int_vector;
+
+  EXPECT_EQ(0, CountingAllocator::num_allocs);
+  EXPECT_EQ(0, CountingAllocator::num_bytes);
+
+  CountingAllocator::Reset();
+}
+
+// Test the expectation that map will go through the supplied allocator.
+TEST(StdAllocator, map) {
+  CountingAllocator::Reset();
+
+  typedef std::map<int, int, std::less<int>,
+                   StdAllocator<int, CountingAllocator> > IntMap;
+
+  EXPECT_EQ(0, CountingAllocator::num_allocs);
+  EXPECT_EQ(0, CountingAllocator::num_bytes);
+
+  IntMap* int_map = new IntMap;
+  for (int i = 0; i < 10; ++i) {
+    (*int_map)[i] = i;
+  }
+
+  // We aren't sure how much allocation is here, but we know it's more than 0.
+  EXPECT_LT(0, CountingAllocator::num_allocs);
+  EXPECT_LT(0, CountingAllocator::num_bytes);
+
+  delete int_map;
+
+  EXPECT_EQ(0, CountingAllocator::num_allocs);
+  EXPECT_EQ(0, CountingAllocator::num_bytes);
+
+  CountingAllocator::Reset();
+}
+
+}  // namespace
+}  // namespace nb
diff --git a/src/net/base/cert_verify_proc.cc b/src/net/base/cert_verify_proc.cc
index 5c002b6..1d5dc6b 100644
--- a/src/net/base/cert_verify_proc.cc
+++ b/src/net/base/cert_verify_proc.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram.h"
 #include "base/sha1.h"
 #include "build/build_config.h"
+#include "nb/memory_scope.h"
 #include "net/base/cert_status_flags.h"
 #include "net/base/cert_verifier.h"
 #include "net/base/cert_verify_result.h"
@@ -28,7 +29,6 @@
 #error Implement certificate verification.
 #endif
 
-
 namespace net {
 
 namespace {
@@ -75,6 +75,7 @@
                            int flags,
                            CRLSet* crl_set,
                            CertVerifyResult* verify_result) {
+  TRACK_MEMORY_SCOPE("Network");
   verify_result->Reset();
   verify_result->verified_cert = cert;
 
diff --git a/src/net/socket/tcp_client_socket_starboard.cc b/src/net/socket/tcp_client_socket_starboard.cc
index 794a009..0536c85 100644
--- a/src/net/socket/tcp_client_socket_starboard.cc
+++ b/src/net/socket/tcp_client_socket_starboard.cc
@@ -348,14 +348,14 @@
 }
 
 bool TCPClientSocketStarboard::IsConnected() const {
-  if (socket_ < 0 || waiting_connect())
+  if (!SbSocketIsValid(socket_) || waiting_connect())
     return false;
 
   return SbSocketIsConnected(socket_);
 }
 
 bool TCPClientSocketStarboard::IsConnectedAndIdle() const {
-  if (socket_ < 0 || waiting_connect())
+  if (!SbSocketIsValid(socket_) || waiting_connect())
     return false;
 
   return SbSocketIsConnectedAndIdle(socket_);
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index c61a235..f9efb66 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -93,8 +93,8 @@
 // ints.
 #define SB_INPUT_FLOATING_POINT_INPUT_VECTOR_VERSION SB_EXPERIMENTAL_API_VERSION
 
-// SbPlayerCreate() will accept SbMediaVideoHeader as a parameter.
-#define SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION SB_EXPERIMENTAL_API_VERSION
+// Deleted the vestigal struct SbUserApplicationTokenResults from user.h.
+#define SB_DELETE_USER_APPLICATION_TOKEN_VERSION SB_EXPERIMENTAL_API_VERSION
 
 // --- Common Detected Features ----------------------------------------------
 
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc
index 1bc081d..b06571d 100644
--- a/src/starboard/linux/shared/system_get_path.cc
+++ b/src/starboard/linux/shared/system_get_path.cc
@@ -78,7 +78,7 @@
   path[0] = '\0';
 
   switch (path_id) {
-    case kSbSystemPathContentDirectory: {
+    case kSbSystemPathContentDirectory:
       if (!GetExecutableDirectory(path, kPathSize)) {
         return false;
       }
@@ -86,9 +86,7 @@
         return false;
       }
       break;
-    }
-
-    case kSbSystemPathCacheDirectory: {
+    case kSbSystemPathCacheDirectory:
       if (!SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize)) {
         return false;
       }
@@ -98,9 +96,7 @@
 
       SbDirectoryCreate(path);
       break;
-    }
-
-    case kSbSystemPathDebugOutputDirectory: {
+    case kSbSystemPathDebugOutputDirectory:
       if (!SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize)) {
         return false;
       }
@@ -110,9 +106,7 @@
 
       SbDirectoryCreate(path);
       break;
-    }
-
-    case kSbSystemPathSourceDirectory: {
+    case kSbSystemPathSourceDirectory:
       if (!GetExecutableDirectory(path, kPathSize)) {
         return false;
       }
@@ -121,25 +115,18 @@
         return false;
       }
       break;
-    }
-
-    case kSbSystemPathTempDirectory: {
+    case kSbSystemPathTempDirectory:
       if (SbStringCopy(path, "/tmp/cobalt", kPathSize) >= kPathSize) {
         return false;
       }
 
       SbDirectoryCreate(path);
       break;
-    }
-
-    case kSbSystemPathTestOutputDirectory: {
+    case kSbSystemPathTestOutputDirectory:
       return SbSystemGetPath(kSbSystemPathDebugOutputDirectory, out_path,
                              path_size);
-    }
-
-    case kSbSystemPathExecutableFile: {
+    case kSbSystemPathExecutableFile:
       return GetExecutablePath(out_path, path_size);
-    }
   }
 
   int length = strlen(path);
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/starboard/linux/x64x11/mse2016/atomic_public.h
similarity index 86%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/starboard/linux/x64x11/mse2016/atomic_public.h
index b75f65f..889c4c2 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/starboard/linux/x64x11/mse2016/atomic_public.h
@@ -12,8 +12,4 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
-
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
-}
+#include "starboard/linux/x64x11/atomic_public.h"
diff --git a/src/starboard/linux/x64x11/mse2016/configuration_public.h b/src/starboard/linux/x64x11/mse2016/configuration_public.h
new file mode 100644
index 0000000..49d5f8a
--- /dev/null
+++ b/src/starboard/linux/x64x11/mse2016/configuration_public.h
@@ -0,0 +1,28 @@
+// 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.
+
+// The Starboard configuration for Desktop X86 Linux. Other devices will have
+// specific Starboard implementations, even if they ultimately are running some
+// version of Linux.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_LINUX_X64X11_MSE2016_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_MSE2016_CONFIGURATION_PUBLIC_H_
+
+// Include the Linux configuration that's common between all x64x11 Linuxes.
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_MSE2016_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/mse2016/gyp_configuration.gypi b/src/starboard/linux/x64x11/mse2016/gyp_configuration.gypi
new file mode 100644
index 0000000..75362de
--- /dev/null
+++ b/src/starboard/linux/x64x11/mse2016/gyp_configuration.gypi
@@ -0,0 +1,40 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    'cobalt_media_source_2016': 1,
+  },
+  'target_defaults': {
+    'default_configuration': 'linux-x64x11-mse2016_debug',
+    'configurations': {
+      'linux-x64x11-mse2016_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'linux-x64x11-mse2016_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'linux-x64x11-mse2016_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'linux-x64x11-mse2016_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '../gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/mse2016/gyp_configuration.py b/src/starboard/linux/x64x11/mse2016/gyp_configuration.py
new file mode 100644
index 0000000..ec9d0e5
--- /dev/null
+++ b/src/starboard/linux/x64x11/mse2016/gyp_configuration.py
@@ -0,0 +1,31 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 MSE2016 platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared platform configuration.
+sys.path.append(os.path.realpath(os.path.join(
+    os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
+import gyp_configuration
+
+
+def CreatePlatformConfig():
+  try:
+    return gyp_configuration.PlatformConfig('linux-x64x11-mse2016')
+  except RuntimeError as e:
+    logging.critical(e)
+    return None
diff --git a/src/starboard/linux/x64x11/mse2016/starboard_platform.gyp b/src/starboard/linux/x64x11/mse2016/starboard_platform.gyp
new file mode 100644
index 0000000..d64fb48
--- /dev/null
+++ b/src/starboard/linux/x64x11/mse2016/starboard_platform.gyp
@@ -0,0 +1,35 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../starboard_platform.gypi'
+  ],
+  'targets': [
+    {
+      'target_name': 'starboard_platform',
+      'type': 'static_library',
+      'sources': [
+        '<@(starboard_platform_sources)',
+      ],
+      'defines': [
+        # This must be defined when building Starboard, and must not when
+        # building Starboard client code.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '<@(starboard_platform_dependencies)',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/starboard/linux/x64x11/mse2016/thread_types_public.h
similarity index 86%
rename from src/starboard/shared/nouser/user_start_sign_in.cc
rename to src/starboard/linux/x64x11/mse2016/thread_types_public.h
index b75f65f..04d844d 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/starboard/linux/x64x11/mse2016/thread_types_public.h
@@ -12,8 +12,4 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
-
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
-}
+#include "starboard/linux/x64x11/thread_types_public.h"
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 275de9a..1b11b8f 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -474,20 +474,6 @@
   int8_t audio_specific_config[8];
 } SbMediaAudioHeader;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-// Used to pass extra video information to SbPlayerCreate().
-typedef struct SbMediaVideoHeader {
-  // The maximum frame width of this video.  This takes alignment into account
-  // and can be greater than or equal to the maximum visible width of the video.
-  int max_encoded_frame_width;
-
-  // The maximum frame height of this video.  This takes alignment into account
-  // and can be greater than or equal to the maximum visible height of the
-  // video.
-  int max_encoded_frame_height;
-} SbMediaVideoHeader;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
 // --- Constants -------------------------------------------------------------
 // One second in SbMediaTime (90KHz ticks).
 #define kSbMediaTimeSecond ((SbMediaTime)(90000))
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index cbdc6df..f4247c1 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -52,12 +52,6 @@
                                           audio_header.number_of_channels *
                                           audio_header.bits_per_sample / 8;
 
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-  SbMediaVideoHeader video_header;
-  video_header.max_encoded_frame_width = 1280;
-  video_header.max_encoded_frame_height = 720;
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
   SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
@@ -79,30 +73,19 @@
     decode_target_provider.context = NULL;
 #endif  // SB_API_VERSION >= 3
 
-#if SB_API_VERSION <= 3
     SbPlayer player =
         SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
                        SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid,
                        &audio_header, NULL, NULL, NULL, NULL
-#if SB_API_VERSION == 3
+#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+                       ,
+                       output_mode
+#endif
+#if SB_API_VERSION >= 3
                        ,
                        &decode_target_provider
 #endif
                        );  // NOLINT
-#else                      // SB_API_VERSION <= 3
-  SbPlayer player =
-      SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
-                     SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header,
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-                     &video_header,
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-                     NULL, NULL, NULL,
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-                     output_mode,
-#endif
-                     &decode_target_provider, NULL);
-#endif  // SB_API_VERSION <= 3
-
     EXPECT_TRUE(SbPlayerIsValid(player));
 
 #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
diff --git a/src/starboard/nplb/rwlock_test.cc b/src/starboard/nplb/rwlock_test.cc
index c172d5c..b31df05 100644
--- a/src/starboard/nplb/rwlock_test.cc
+++ b/src/starboard/nplb/rwlock_test.cc
@@ -119,7 +119,7 @@
 };
 TEST(RWLock, HoldsLockForTime) {
   const SbTime kTimeToHold = kSbTimeMillisecond * 5;
-  const SbTime kAllowedError = kSbTimeMillisecond * 2;
+  const SbTime kAllowedError = kSbTimeMillisecond * 10;
 
   ThreadHoldsWriteLockForTime::SharedData shared_data(kTimeToHold);
   ThreadHoldsWriteLockForTime thread(&shared_data);
diff --git a/src/starboard/player.h b/src/starboard/player.h
index 19e5ceb..69074a1 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -252,12 +252,6 @@
 //   if the audio codec is |kSbMediaAudioCodecAac|. Otherwise, |audio_header|
 //   can be NULL. See media.h for the format of the |SbMediaAudioHeader| struct.
 //
-// |video_header|: The caller must provide a populated |video_header|.  See
-//   media.h for the format of the |SbMediaVideoHeader| struct.  Note that this
-//   can be NULL to indicate that the caller has no information on the maximum
-//   resolution.  In this case the implementation should assume that the video
-//   can reach the maximum resolution the implementation supports.
-//
 // |sample_deallocator_func|: If not |NULL|, the player calls this function
 //   on an internal thread to free the sample buffers passed into
 //   SbPlayerWriteSample().
@@ -287,8 +281,6 @@
 //   use the provider to create SbDecodeTargets. A provider could also
 //   potentially be required by the player, in which case, if the provider is
 //   not given, the player will fail by returning kSbPlayerInvalid.
-#if SB_API_VERSION <= 3
-
 SB_EXPORT SbPlayer SbPlayerCreate(
     SbWindow window,
     SbMediaVideoCodec video_codec,
@@ -300,35 +292,16 @@
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
     void* context
-#if SB_API_VERSION == 3
+#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+    ,
+    SbPlayerOutputMode output_mode
+#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 3
     ,
     SbDecodeTargetProvider* provider
-#endif  // SB_API_VERSION == 3
+#endif  // SB_API_VERSION >= 3
     );  // NOLINT
 
-#else  // SB_API_VERSION <= 3
-
-SB_EXPORT SbPlayer
-SbPlayerCreate(SbWindow window,
-               SbMediaVideoCodec video_codec,
-               SbMediaAudioCodec audio_codec,
-               SbMediaTime duration_pts,
-               SbDrmSystem drm_system,
-               const SbMediaAudioHeader* audio_header,
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-               const SbMediaVideoHeader* video_header,
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-               SbPlayerDeallocateSampleFunc sample_deallocate_func,
-               SbPlayerDecoderStatusFunc decoder_status_func,
-               SbPlayerStatusFunc player_status_func,
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-               SbPlayerOutputMode output_mode,
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-               SbDecodeTargetProvider* provider,
-               void* context);
-
-#endif  // SB_API_VERSION <= 3
-
 #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
 // Returns true if the given player output mode is supported by the platform.
 // If this function returns true, it is okay to call SbPlayerCreate() with
diff --git a/src/starboard/shared/nouser/user_is_age_restricted.cc b/src/starboard/shared/nouser/user_is_age_restricted.cc
deleted file mode 100644
index f28ddac..0000000
--- a/src/starboard/shared/nouser/user_is_age_restricted.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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/user.h"
-
-bool SbUserIsAgeRestricted(SbUser user) {
-  return false;
-}
diff --git a/src/starboard/shared/speechd/speech_synthesis_set_language.cc b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
index cbd2c81..6943fb7 100644
--- a/src/starboard/shared/speechd/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/speech_synthesis.h"
+
 #if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
 // DEPRECATED IN API VERSION 4
 
-#include "starboard/speech_synthesis.h"
-
 #include "starboard/shared/speechd/speechd_internal.h"
 
 using starboard::shared::speechd::SpeechDispatcher;
diff --git a/src/starboard/shared/speechd/speechd_internal.cc b/src/starboard/shared/speechd/speechd_internal.cc
index ee30ff9..86ca3e0 100644
--- a/src/starboard/shared/speechd/speechd_internal.cc
+++ b/src/starboard/shared/speechd/speechd_internal.cc
@@ -66,7 +66,7 @@
   if (connection_ && text && *text) {
     // Priority SPD_MESSAGE will be queued with other text of same priority.
     int result = spd_say(connection_, SPD_MESSAGE, text);
-    if (result != 0) {
+    if (result < 0) {
       SB_DLOG(ERROR) << "Failed to speak: " << text;
     }
   }
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index 679ab41..c3a12dd 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -210,6 +210,9 @@
         delete event;
         return true;
       }
+      if (state() == kStateSuspended) {
+        OnResume();
+      }
       break;
     case kSbEventTypeStop:
       if (state() == kStateStarted) {
@@ -252,6 +255,7 @@
     case kSbEventTypeSuspend:
       SB_DCHECK(state() == kStatePaused);
       state_ = kStateSuspended;
+      OnSuspend();
       break;
     case kSbEventTypeResume:
       SB_DCHECK(state() == kStateSuspended);
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index 3ee1f6b..51f5a84 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -241,6 +241,14 @@
   // must be run after the application stop event is handled.
   virtual void Teardown() {}
 
+  // Does any platform-specific tearing-down AFTER the application has
+  // processed the Suspend event, but before actual suspension.
+  virtual void OnSuspend() {}
+
+  // Does any platform-specific initialization BEFORE the application has
+  // processed the Resume event.
+  virtual void OnResume() {}
+
 #if SB_HAS(PLAYER) &&                                             \
     (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
      SB_IS(PLAYER_PUNCHED_OUT))
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index 85ccf03..ac98df9 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -42,6 +42,7 @@
       frames_in_buffer_(0),
       offset_in_frames_(0),
       frames_consumed_(0),
+      frames_consumed_set_at_(SbTimeGetMonotonicNow()),
       end_of_stream_written_(false),
       end_of_stream_decoded_(false),
       decoder_(decoder.Pass()),
@@ -51,6 +52,20 @@
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
   frame_buffers_[0] = &frame_buffer_[0];
+
+// TODO: The audio sink on Android is currently broken on certain devices,
+// which causes all of playback to hang.  Log it for now, so we can tell
+// when it happens, but this should be removed once the sink is fixed.
+#if defined(NDEBUG)
+  const bool kLogFramesConsumed = false;
+#else
+  const bool kLogFramesConsumed = true;
+#endif
+  if (kLogFramesConsumed) {
+    log_frames_consumed_closure_ =
+        Bind(&AudioRendererImpl::LogFramesConsumed, this);
+    job_queue_->Schedule(log_frames_consumed_closure_, kSbTimeSecond);
+  }
 }
 
 AudioRendererImpl::~AudioRendererImpl() {
@@ -63,6 +78,10 @@
   if (read_from_decoder_closure_.is_valid()) {
     job_queue_->Remove(read_from_decoder_closure_);
   }
+
+  if (log_frames_consumed_closure_.is_valid()) {
+    job_queue_->Remove(log_frames_consumed_closure_);
+  }
 }
 
 void AudioRendererImpl::WriteSample(const InputBuffer& input_buffer) {
@@ -143,6 +162,7 @@
   frames_in_buffer_ = 0;
   offset_in_frames_ = 0;
   frames_consumed_ = 0;
+  frames_consumed_set_at_ = SbTimeGetMonotonicNow();
   end_of_stream_written_ = false;
   end_of_stream_decoded_ = false;
   pending_decoded_audio_ = NULL;
@@ -210,6 +230,7 @@
   offset_in_frames_ %= kMaxCachedFrames;
   frames_in_buffer_ -= frames_consumed;
   frames_consumed_ += frames_consumed;
+  frames_consumed_set_at_ = SbTimeGetMonotonicNow();
 
   bool decoded_audio_available =
       pending_decoded_audio_ ||
@@ -221,6 +242,19 @@
   }
 }
 
+void AudioRendererImpl::LogFramesConsumed() {
+  SbTimeMonotonic time_since =
+      SbTimeGetMonotonicNow() - frames_consumed_set_at_;
+  if (time_since > kSbTimeSecond) {
+    SB_DLOG(WARNING) << "|frames_consumed_| has not been updated for "
+                     << (time_since / kSbTimeSecond) << "."
+                     << ((time_since / (kSbTimeSecond / 10)) % 10)
+                     << " seconds, and |pending_decoded_audio_| is "
+                     << (!!pending_decoded_audio_ ? "" : "not ") << "ready.";
+  }
+  job_queue_->Schedule(log_frames_consumed_closure_, kSbTimeSecond);
+}
+
 // Try to read some audio data from the decoder.  Note that this operation is
 // valid across seeking.  If a seek happens immediately after a ReadFromDecoder
 // request is scheduled, the seek will reset the decoder.  So the
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
index d21c7c4..78dff81 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
@@ -79,6 +79,7 @@
                           bool* is_playing,
                           bool* is_eos_reached);
   void ConsumeFrames(int frames_consumed);
+  void LogFramesConsumed();
 
   void ReadFromDecoder();
   bool AppendDecodedAudio_Locked(
@@ -109,8 +110,10 @@
   int offset_in_frames_;
 
   int frames_consumed_;
+  SbTimeMonotonic frames_consumed_set_at_;
   bool end_of_stream_written_;
   bool end_of_stream_decoded_;
+  Closure log_frames_consumed_closure_;
 
   scoped_ptr<AudioDecoder> decoder_;
   SbAudioSink audio_sink_;
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index 93d6bf2..5b0e64c 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -25,7 +25,6 @@
     FilterBasedPlayerWorkerHandler;
 using starboard::shared::starboard::player::PlayerWorker;
 
-#if SB_API_VERSION <= 3
 SbPlayer SbPlayerCreate(SbWindow window,
                         SbMediaVideoCodec video_codec,
                         SbMediaAudioCodec audio_codec,
@@ -36,34 +35,16 @@
                         SbPlayerDecoderStatusFunc decoder_status_func,
                         SbPlayerStatusFunc player_status_func,
                         void* context
-#if SB_API_VERSION == 3
+#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+                        ,
+                        SbPlayerOutputMode output_mode
+#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 3
                         ,
                         SbDecodeTargetProvider* provider
-#endif  // SB_API_VERSION == 3
+#endif  // SB_API_VERSION >= 3
                         ) {
-#else  // SB_API_VERSION <= 3
-SbPlayer SbPlayerCreate(SbWindow window,
-                        SbMediaVideoCodec video_codec,
-                        SbMediaAudioCodec audio_codec,
-                        SbMediaTime duration_pts,
-                        SbDrmSystem drm_system,
-                        const SbMediaAudioHeader* audio_header,
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-                        const SbMediaVideoHeader* video_header,
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-                        SbPlayerDeallocateSampleFunc sample_deallocate_func,
-                        SbPlayerDecoderStatusFunc decoder_status_func,
-                        SbPlayerStatusFunc player_status_func,
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-                        SbPlayerOutputMode output_mode,
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-                        SbDecodeTargetProvider* provider,
-                        void* context) {
-#endif  // SB_API_VERSION <= 3
   SB_UNREFERENCED_PARAMETER(window);
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-  SB_UNREFERENCED_PARAMETER(video_header);
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
 
   if (audio_codec != kSbMediaAudioCodecAac) {
     SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc
index 90b23cf..de2ad10 100644
--- a/src/starboard/shared/stub/player_create.cc
+++ b/src/starboard/shared/stub/player_create.cc
@@ -18,7 +18,6 @@
 #error "SbPlayerCreate requires SB_HAS(PLAYER)."
 #endif
 
-#if SB_API_VERSION <= 3
 SbPlayer SbPlayerCreate(SbWindow /*window*/,
                         SbMediaVideoCodec /*video_codec*/,
                         SbMediaAudioCodec /*audio_codec*/,
@@ -29,30 +28,14 @@
                         SbPlayerDecoderStatusFunc /*decoder_status_func*/,
                         SbPlayerStatusFunc /*player_status_func*/,
                         void* /*context*/
-#if SB_API_VERSION == 3
+#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+                        ,
+                        SbPlayerOutputMode output_mode
+#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_VERSION(3)
                         ,
                         SbDecodeTargetProvider* /*provider*/
-#endif  // SB_API_VERSION == 3
-
-#else  // SB_API_VERSION <= 3
-SbPlayer SbPlayerCreate(SbWindow /*window*/,
-                        SbMediaVideoCodec /*video_codec*/,
-                        SbMediaAudioCodec /*audio_codec*/,
-                        SbMediaTime /*duration_pts*/,
-                        SbDrmSystem /*drm_system*/,
-                        const SbMediaAudioHeader* /*audio_header*/,
-#if SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-                        const SbMediaVideoHeader* /*video_header*/,
-#endif  // SB_API_VERSION >= SB_PLAYER_CREATE_WITH_VIDEO_HEADER_VERSION
-                        SbPlayerDeallocateSampleFunc /*sample_deallocate_func*/,
-                        SbPlayerDecoderStatusFunc /*decoder_status_func*/,
-                        SbPlayerStatusFunc /*player_status_func*/,
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-                        SbPlayerOutputMode /*output_mode*/,
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-                        SbDecodeTargetProvider* /*provider*/,
-                        void* /*context*/
-#endif  // SB_API_VERSION <= 3
+#endif
                         ) {
   return kSbPlayerInvalid;
 }
diff --git a/src/starboard/shared/wayland/application_wayland.cc b/src/starboard/shared/wayland/application_wayland.cc
new file mode 100644
index 0000000..e054624
--- /dev/null
+++ b/src/starboard/shared/wayland/application_wayland.cc
@@ -0,0 +1,322 @@
+// Copyright 2016 Samsung Electronics. 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/shared/wayland/application_wayland.h"
+
+#include <EGL/egl.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/wayland/dev_input.h"
+#include "starboard/shared/wayland/window_internal.h"
+#include "starboard/time.h"
+
+// YouTube Technical Requirement 2018 (2016/11/1 - Initial draft)
+// 9.5 The device MUST dispatch the following key events, as appropriate:
+//  * Window.keydown
+//      * After a key is held down for 500ms, the Window.keydown event
+//        MUST repeat every 50ms until a user stops holding the key down.
+//  * Window.keyup
+static const SbTime kKeyHoldTime = 500 * kSbTimeMillisecond;
+static const SbTime kKeyRepeatTime = 50 * kSbTimeMillisecond;
+
+namespace starboard {
+namespace shared {
+namespace wayland {
+
+// Tizen application engine using the generic queue and a tizen implementation.
+
+ApplicationWayland::ApplicationWayland()
+    : seat_(NULL),
+      keyboard_(NULL),
+      key_repeat_event_id_(kSbEventIdInvalid),
+      key_repeat_interval_(kKeyHoldTime) {}
+
+SbWindow ApplicationWayland::CreateWindow(const SbWindowOptions* options) {
+  SB_DLOG(INFO) << "CreateWindow";
+  SbWindow window = new SbWindowPrivate(options);
+  window_ = window;
+
+// Video Plane
+#if SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
+  window->video_window = display_;
+#else
+  window->video_window = elm_win_add(NULL, "Cobalt_Video", ELM_WIN_BASIC);
+  elm_win_title_set(window->video_window, "Cobalt_Video");
+  elm_win_autodel_set(window->video_window, EINA_TRUE);
+  evas_object_resize(window->video_window, window->width, window->height);
+  evas_object_hide(window->video_window);
+#endif
+
+  // Graphics Plane
+  window->surface = wl_compositor_create_surface(compositor_);
+  window->shell_surface = wl_shell_get_shell_surface(shell_, window->surface);
+  wl_shell_surface_add_listener(window->shell_surface, &shell_surface_listener,
+                                window);
+
+  window->tz_visibility =
+      tizen_policy_get_visibility(tz_policy_, window->surface);
+  tizen_visibility_add_listener(window->tz_visibility,
+                                &tizen_visibility_listener, window);
+  tizen_policy_activate(tz_policy_, window->surface);
+  wl_shell_surface_set_title(window->shell_surface, "cobalt");
+  WindowRaise();
+
+  struct wl_region* region;
+  region = wl_compositor_create_region(compositor_);
+  wl_region_add(region, 0, 0, window->width, window->height);
+  wl_surface_set_opaque_region(window->surface, region);
+  wl_region_destroy(region);
+
+  window->egl_window =
+      wl_egl_window_create(window->surface, window->width, window->height);
+
+  return window;
+}
+
+bool ApplicationWayland::DestroyWindow(SbWindow window) {
+  SB_DLOG(INFO) << "DestroyWindow";
+  if (!SbWindowIsValid(window)) {
+    SB_DLOG(WARNING) << "wayland window destroy failed!!";
+    return false;
+  }
+
+// Video Plane
+#if !SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
+  evas_object_hide(window->video_window);
+#endif
+  window->video_window = NULL;
+
+  // Graphics Plane
+  tizen_visibility_destroy(window->tz_visibility);
+  wl_egl_window_destroy(window->egl_window);
+  wl_shell_surface_destroy(window->shell_surface);
+  wl_surface_destroy(window->surface);
+
+  return true;
+}
+
+void ApplicationWayland::Initialize() {
+  SB_DLOG(INFO) << "Initialize";
+  // Video Plane
+  elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+
+  // Graphics Plane
+  display_ = wl_display_connect(NULL);
+  struct wl_registry* registry = wl_display_get_registry(display_);
+  wl_registry_add_listener(registry, &registry_listener, this);
+  wl_display_dispatch(display_);
+  wl_display_roundtrip(display_);
+
+  // Open wakeup event
+  wakeup_fd_ = eventfd(0, 0);
+  if (wakeup_fd_ == -1)
+    SB_DLOG(ERROR) << "wakeup_fd_ creation failed";
+
+  InitializeEgl();
+}
+
+void ApplicationWayland::Teardown() {
+  SB_DLOG(INFO) << "Teardown";
+  DeleteRepeatKey();
+
+  TerminateEgl();
+
+  wl_display_flush(display_);
+  wl_display_disconnect(display_);
+
+  // Close wakeup event
+  close(wakeup_fd_);
+}
+
+void ApplicationWayland::OnSuspend() {
+  TerminateEgl();
+}
+
+void ApplicationWayland::OnResume() {
+  InitializeEgl();
+}
+
+void ApplicationWayland::InitializeEgl() {
+  EGLDisplay egl_display = eglGetDisplay(display_);
+  SB_DLOG(INFO) << __FUNCTION__ << ": INITIALIZE egl_display=" << egl_display;
+  SB_DCHECK(egl_display);
+  eglInitialize(egl_display, NULL, NULL);
+}
+
+void ApplicationWayland::TerminateEgl() {
+  EGLDisplay egl_display = eglGetDisplay(display_);
+  SB_DLOG(INFO) << __FUNCTION__ << ": TERMINATE egl_display=" << egl_display;
+  SB_DCHECK(egl_display);
+  eglTerminate(egl_display);
+}
+
+bool ApplicationWayland::MayHaveSystemEvents() {
+  // SB_DCHECK(SbThreadIsValid(wayland_thread_));
+  return display_;
+}
+
+shared::starboard::Application::Event*
+ApplicationWayland::PollNextSystemEvent() {
+  // if queue is empty, try to read fd and push event to queue
+  if (wl_display_prepare_read(display_) == 0) {
+    wl_display_read_events(display_);
+  }
+
+  // dispatch queue if any event
+  if (wl_display_dispatch_pending(display_) < 0) {
+    SB_DLOG(ERROR) << "wl_display_dispatch_pending Error";
+    return NULL;
+  }
+
+  return NULL;
+}
+
+shared::starboard::Application::Event*
+ApplicationWayland::WaitForSystemEventWithTimeout(SbTime duration) {
+  if (wl_display_flush(display_) < 0) {
+    SB_DLOG(ERROR) << "wl_display_flush Error";
+    return NULL;
+  }
+
+  struct pollfd fds[2];
+  struct timespec timeout_ts;
+  int ret;
+
+  timeout_ts.tv_sec = duration / kSbTimeSecond;
+  timeout_ts.tv_nsec =
+      (duration % kSbTimeSecond) * kSbTimeNanosecondsPerMicrosecond;
+
+  // wait wayland event
+  fds[0].fd = wl_display_get_fd(display_);
+  fds[0].events = POLLIN;
+  fds[0].revents = 0;
+
+  // wait wakeup event by event injection
+  fds[1].fd = wakeup_fd_;
+  fds[1].events = POLLIN;
+  fds[1].revents = 0;
+
+  ret = ppoll(fds, 2, &timeout_ts, NULL);
+
+  if (timeout_ts.tv_sec > 0)  // long-wait log
+    SB_DLOG(INFO) << "WaitForSystemEventWithTimeout : wakeup " << ret << " 0("
+                  << fds[0].revents << ") 1(" << fds[1].revents << ")";
+
+  if (ret > 0 && fds[1].revents & POLLIN) {  // clear wakeup event
+    uint64_t u;
+    read(wakeup_fd_, &u, sizeof(uint64_t));
+  }
+
+  // TODO : print log for too short waiting(under 2ms) to prevent abnormal fd
+  // events.
+
+  return NULL;
+}
+
+void ApplicationWayland::WakeSystemEventWait() {
+  uint64_t u = 1;
+  write(wakeup_fd_, &u, sizeof(uint64_t));
+}
+
+void ApplicationWayland::CreateRepeatKey() {
+  if (!key_repeat_state_) {
+    return;
+  }
+
+  if (key_repeat_interval_) {
+    key_repeat_interval_ = kKeyRepeatTime;
+  }
+
+  CreateKey(key_repeat_key_, key_repeat_state_, true);
+}
+
+void ApplicationWayland::DeleteRepeatKey() {
+  if (key_repeat_event_id_ != kSbEventIdInvalid) {
+    SbEventCancel(key_repeat_event_id_);
+    key_repeat_event_id_ = kSbEventIdInvalid;
+  }
+}
+
+void ApplicationWayland::CreateKey(int key, int state, bool is_repeat) {
+  SbInputData* data = new SbInputData();
+  SbMemorySet(data, 0, sizeof(*data));
+  data->window = window_;
+  data->type = (state == 0 ? kSbInputEventTypeUnpress : kSbInputEventTypePress);
+  data->device_type = kSbInputDeviceTypeRemote;  // device_type;
+  data->device_id = 1;                           // kKeyboardDeviceId;
+  data->key = KeyCodeToSbKey(key);
+  data->key_location = KeyCodeToSbKeyLocation(key);
+  data->key_modifiers = 0;  // modifiers;
+  Inject(new Event(kSbEventTypeInput, data,
+                   &Application::DeleteDestructor<SbInputData>));
+
+  DeleteRepeatKey();
+
+  if (is_repeat && state) {
+    key_repeat_key_ = key;
+    key_repeat_state_ = state;
+    key_repeat_event_id_ = SbEventSchedule([](void* window) {
+      ApplicationWayland* application =
+          reinterpret_cast<ApplicationWayland*>(window);
+      application->CreateRepeatKey();
+    }, this, key_repeat_interval_);
+  } else {
+    key_repeat_interval_ = kKeyHoldTime;
+  }
+}
+
+void ApplicationWayland::WindowRaise() {
+  if (tz_policy_)
+    tizen_policy_raise(tz_policy_, window_->surface);
+  if (window_->shell_surface)
+    wl_shell_surface_set_toplevel(window_->shell_surface);
+}
+
+void ApplicationWayland::Pause() {
+  Application::Pause(NULL, NULL);
+
+  ScopedLock lock(observers_mutex_);
+  std::for_each(observers_.begin(), observers_.end(),
+                [](StateObserver* i) { i->OnAppPause(); });
+}
+
+void ApplicationWayland::Unpause() {
+  Application::Unpause(NULL, NULL);
+
+  ScopedLock lock(observers_mutex_);
+  std::for_each(observers_.begin(), observers_.end(),
+                [](StateObserver* i) { i->OnAppUnpause(); });
+}
+
+void ApplicationWayland::RegisterObserver(StateObserver* observer) {
+  ScopedLock lock(observers_mutex_);
+  observers_.push_back(observer);
+}
+
+void ApplicationWayland::UnregisterObserver(StateObserver* observer) {
+  ScopedLock lock(observers_mutex_);
+  auto it = std::find_if(
+      observers_.begin(), observers_.end(),
+      [observer](const StateObserver* i) { return observer == i; });
+  if (it == observers_.end())
+    return;
+  observers_.erase(it);
+}
+
+}  // namespace wayland
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/wayland/application_wayland.h b/src/starboard/shared/wayland/application_wayland.h
new file mode 100644
index 0000000..e355c8b
--- /dev/null
+++ b/src/starboard/shared/wayland/application_wayland.h
@@ -0,0 +1,128 @@
+// Copyright 2016 Samsung Electronics. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_WAYLAND_APPLICATION_WAYLAND_H_
+#define STARBOARD_SHARED_WAYLAND_APPLICATION_WAYLAND_H_
+
+#include <tizen-extension-client-protocol.h>
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+#include <deque>
+
+#include "starboard/configuration.h"
+#include "starboard/input.h"
+#include "starboard/mutex.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/starboard/queue_application.h"
+#include "starboard/types.h"
+#include "starboard/window.h"
+
+namespace starboard {
+namespace shared {
+namespace wayland {
+
+class ApplicationWayland : public shared::starboard::QueueApplication {
+ public:
+  ApplicationWayland();
+  ~ApplicationWayland() SB_OVERRIDE{};
+
+  static ApplicationWayland* Get() {
+    return static_cast<ApplicationWayland*>(
+        shared::starboard::Application::Get());
+  }
+
+  // window
+  SbWindow CreateWindow(const SbWindowOptions* options);
+  bool DestroyWindow(SbWindow window);
+  void SetCompositor(wl_compositor* compositor) { compositor_ = compositor; }
+  wl_compositor* GetCompositor() { return compositor_; }
+  void SetShell(wl_shell* shell) { shell_ = shell; }
+  wl_shell* GetShell() { return shell_; }
+  void SetPolicy(tizen_policy* policy) { tz_policy_ = policy; }
+  tizen_policy* GetPolicy() { return tz_policy_; }
+  void WindowRaise();
+
+  // input devices
+  void SetKeyboard(wl_keyboard* keyboard) { keyboard_ = keyboard; }
+  wl_keyboard* GetKeyboard() { return keyboard_; }
+  void SetSeat(wl_seat* seat) { seat_ = seat; }
+  wl_seat* GetSeat() { return seat_; }
+
+  // key event
+  void CreateRepeatKey();
+  void DeleteRepeatKey();
+  void CreateKey(int key, int state, bool is_repeat);
+
+  // state change
+  void Pause() SB_OVERRIDE;
+  void Unpause() SB_OVERRIDE;
+
+  // state change observer
+  class StateObserver {
+   public:
+    StateObserver() { ApplicationWayland::Get()->RegisterObserver(this); }
+    ~StateObserver() { ApplicationWayland::Get()->UnregisterObserver(this); }
+    virtual void OnAppPause() {}
+    virtual void OnAppUnpause() {}
+  };
+  void RegisterObserver(StateObserver* observer);
+  void UnregisterObserver(StateObserver* observer);
+
+ protected:
+  // --- Application overrides ---
+  void Initialize() SB_OVERRIDE;
+  void Teardown() SB_OVERRIDE;
+  void OnSuspend() SB_OVERRIDE;
+  void OnResume() SB_OVERRIDE;
+
+  // --- QueueApplication overrides ---
+  bool MayHaveSystemEvents() SB_OVERRIDE;
+  Event* PollNextSystemEvent() SB_OVERRIDE;
+  Event* WaitForSystemEventWithTimeout(SbTime time) SB_OVERRIDE;
+  void WakeSystemEventWait() SB_OVERRIDE;
+
+ private:
+  void InitializeEgl();
+  void TerminateEgl();
+
+  // window
+  SbWindow window_;
+  wl_display* display_;
+  wl_compositor* compositor_;
+  wl_shell* shell_;
+  tizen_policy* tz_policy_;
+
+  // input devices
+  wl_seat* seat_;
+  wl_keyboard* keyboard_;
+  int key_repeat_key_;
+  int key_repeat_state_;
+  SbEventId key_repeat_event_id_;
+  SbTime key_repeat_interval_;
+
+  // wakeup event
+  int wakeup_fd_;
+
+  // observers
+  Mutex observers_mutex_;
+  std::deque<StateObserver*> observers_;
+};
+
+}  // namespace wayland
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_WAYLAND_APPLICATION_WAYLAND_H_
diff --git a/src/starboard/shared/wayland/dev_input.h b/src/starboard/shared/wayland/dev_input.h
new file mode 100644
index 0000000..7bc5450
--- /dev/null
+++ b/src/starboard/shared/wayland/dev_input.h
@@ -0,0 +1,486 @@
+// Copyright 2016 Samsung Electronics. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_WAYLAND_DEV_INPUT_H_
+#define STARBOARD_SHARED_WAYLAND_DEV_INPUT_H_
+
+#include <EGL/egl.h>
+#include <linux/input.h>
+
+#include "starboard/input.h"
+#include "starboard/key.h"
+#include "starboard/log.h"
+#include "starboard/shared/wayland/application_wayland.h"
+#include "starboard/shared/wayland/window_internal.h"
+
+namespace starboard {
+namespace shared {
+namespace wayland {
+
+#define KEY_INFO_BUTTON 0xbc
+
+// Converts an input_event code into an SbKey.
+static SbKey KeyCodeToSbKey(uint16_t code) {
+  switch (code) {
+    case KEY_BACKSPACE:
+      return kSbKeyBack;
+    case KEY_DELETE:
+      return kSbKeyDelete;
+    case KEY_TAB:
+      return kSbKeyTab;
+    case KEY_LINEFEED:
+    case KEY_ENTER:
+    case KEY_KPENTER:
+      return kSbKeyReturn;
+    case KEY_CLEAR:
+      return kSbKeyClear;
+    case KEY_SPACE:
+      return kSbKeySpace;
+    case KEY_HOME:
+      return kSbKeyHome;
+    case KEY_END:
+      return kSbKeyEnd;
+    case KEY_PAGEUP:
+      return kSbKeyPrior;
+    case KEY_PAGEDOWN:
+      return kSbKeyNext;
+    case KEY_LEFT:
+      return kSbKeyLeft;
+    case KEY_RIGHT:
+      return kSbKeyRight;
+    case KEY_DOWN:
+      return kSbKeyDown;
+    case KEY_UP:
+      return kSbKeyUp;
+    case KEY_ESC:
+      return kSbKeyEscape;
+    case KEY_KATAKANA:
+    case KEY_HIRAGANA:
+    case KEY_KATAKANAHIRAGANA:
+      return kSbKeyKana;
+    case KEY_HANGEUL:
+      return kSbKeyHangul;
+    case KEY_HANJA:
+      return kSbKeyHanja;
+    case KEY_HENKAN:
+      return kSbKeyConvert;
+    case KEY_MUHENKAN:
+      return kSbKeyNonconvert;
+    case KEY_ZENKAKUHANKAKU:
+      return kSbKeyDbeDbcschar;
+    case KEY_A:
+      return kSbKeyA;
+    case KEY_B:
+      return kSbKeyB;
+    case KEY_C:
+      return kSbKeyC;
+    case KEY_D:
+      return kSbKeyD;
+    case KEY_E:
+      return kSbKeyE;
+    case KEY_F:
+      return kSbKeyF;
+    case KEY_G:
+      return kSbKeyG;
+    case KEY_H:
+      return kSbKeyH;
+    case KEY_I:
+      return kSbKeyI;
+    case KEY_J:
+      return kSbKeyJ;
+    case KEY_K:
+      return kSbKeyK;
+    case KEY_L:
+      return kSbKeyL;
+    case KEY_M:
+      return kSbKeyM;
+    case KEY_N:
+      return kSbKeyN;
+    case KEY_O:
+      return kSbKeyO;
+    case KEY_P:
+      return kSbKeyP;
+    case KEY_Q:
+      return kSbKeyQ;
+    case KEY_R:
+      return kSbKeyR;
+    case KEY_S:
+      return kSbKeyS;
+    case KEY_T:
+      return kSbKeyT;
+    case KEY_U:
+      return kSbKeyU;
+    case KEY_V:
+      return kSbKeyV;
+    case KEY_W:
+      return kSbKeyW;
+    case KEY_X:
+      return kSbKeyX;
+    case KEY_Y:
+      return kSbKeyY;
+    case KEY_Z:
+      return kSbKeyZ;
+
+    case KEY_0:
+      return kSbKey0;
+    case KEY_1:
+      return kSbKey1;
+    case KEY_2:
+      return kSbKey2;
+    case KEY_3:
+      return kSbKey3;
+    case KEY_4:
+      return kSbKey4;
+    case KEY_5:
+      return kSbKey5;
+    case KEY_6:
+      return kSbKey6;
+    case KEY_7:
+      return kSbKey7;
+    case KEY_8:
+      return kSbKey8;
+    case KEY_9:
+      return kSbKey9;
+
+    case KEY_NUMERIC_0:
+    case KEY_NUMERIC_1:
+    case KEY_NUMERIC_2:
+    case KEY_NUMERIC_3:
+    case KEY_NUMERIC_4:
+    case KEY_NUMERIC_5:
+    case KEY_NUMERIC_6:
+    case KEY_NUMERIC_7:
+    case KEY_NUMERIC_8:
+    case KEY_NUMERIC_9:
+      return static_cast<SbKey>(kSbKey0 + (code - KEY_NUMERIC_0));
+
+    case KEY_KP0:
+      return kSbKeyNumpad0;
+    case KEY_KP1:
+      return kSbKeyNumpad1;
+    case KEY_KP2:
+      return kSbKeyNumpad2;
+    case KEY_KP3:
+      return kSbKeyNumpad3;
+    case KEY_KP4:
+      return kSbKeyNumpad4;
+    case KEY_KP5:
+      return kSbKeyNumpad5;
+    case KEY_KP6:
+      return kSbKeyNumpad6;
+    case KEY_KP7:
+      return kSbKeyNumpad7;
+    case KEY_KP8:
+      return kSbKeyNumpad8;
+    case KEY_KP9:
+      return kSbKeyNumpad9;
+
+    case KEY_KPASTERISK:
+      return kSbKeyMultiply;
+    case KEY_KPDOT:
+      return kSbKeyDecimal;
+    case KEY_KPSLASH:
+      return kSbKeyDivide;
+    case KEY_KPPLUS:
+    case KEY_EQUAL:
+      return kSbKeyOemPlus;
+    case KEY_COMMA:
+      return kSbKeyOemComma;
+    case KEY_KPMINUS:
+    case KEY_MINUS:
+      return kSbKeyOemMinus;
+    case KEY_DOT:
+      return kSbKeyOemPeriod;
+    case KEY_SEMICOLON:
+      return kSbKeyOem1;
+    case KEY_SLASH:
+      return kSbKeyOem2;
+    case KEY_GRAVE:
+      return kSbKeyOem3;
+    case KEY_LEFTBRACE:
+      return kSbKeyOem4;
+    case KEY_BACKSLASH:
+      return kSbKeyOem5;
+    case KEY_RIGHTBRACE:
+      return kSbKeyOem6;
+    case KEY_APOSTROPHE:
+      return kSbKeyOem7;
+    case KEY_LEFTSHIFT:
+    case KEY_RIGHTSHIFT:
+      return kSbKeyShift;
+    case KEY_LEFTCTRL:
+    case KEY_RIGHTCTRL:
+      return kSbKeyControl;
+    case KEY_LEFTMETA:
+    case KEY_RIGHTMETA:
+    case KEY_LEFTALT:
+    case KEY_RIGHTALT:
+      return kSbKeyMenu;
+    case KEY_PAUSE:
+      return kSbKeyPause;
+    case KEY_CAPSLOCK:
+      return kSbKeyCapital;
+    case KEY_NUMLOCK:
+      return kSbKeyNumlock;
+    case KEY_SCROLLLOCK:
+      return kSbKeyScroll;
+    case KEY_SELECT:
+      return kSbKeySelect;
+    case KEY_PRINT:
+      return kSbKeyPrint;
+    case KEY_INSERT:
+      return kSbKeyInsert;
+    case KEY_HELP:
+      return kSbKeyHelp;
+    case KEY_MENU:
+      return kSbKeyApps;
+    case KEY_FN_F1:
+    case KEY_FN_F2:
+    case KEY_FN_F3:
+    case KEY_FN_F4:
+    case KEY_FN_F5:
+    case KEY_FN_F6:
+    case KEY_FN_F7:
+    case KEY_FN_F8:
+    case KEY_FN_F9:
+    case KEY_FN_F10:
+    case KEY_FN_F11:
+    case KEY_FN_F12:
+      return static_cast<SbKey>(kSbKeyF1 + (code - KEY_FN_F1));
+
+    // For supporting multimedia buttons on a USB keyboard.
+    case KEY_BACK:
+      return kSbKeyBrowserBack;
+    case KEY_FORWARD:
+      return kSbKeyBrowserForward;
+    case KEY_REFRESH:
+      return kSbKeyBrowserRefresh;
+    case KEY_STOP:
+      return kSbKeyBrowserStop;
+    case KEY_SEARCH:
+      return kSbKeyBrowserSearch;
+    case KEY_FAVORITES:
+      return kSbKeyBrowserFavorites;
+    case KEY_HOMEPAGE:
+      return kSbKeyBrowserHome;
+    case KEY_MUTE:
+      return kSbKeyVolumeMute;
+    case KEY_VOLUMEDOWN:
+      return kSbKeyVolumeDown;
+    case KEY_VOLUMEUP:
+      return kSbKeyVolumeUp;
+    case KEY_NEXTSONG:
+      return kSbKeyMediaNextTrack;
+    case KEY_PREVIOUSSONG:
+      return kSbKeyMediaPrevTrack;
+    case KEY_STOPCD:
+      return kSbKeyMediaStop;
+    case KEY_PLAYPAUSE:
+      return kSbKeyMediaPlayPause;
+    case KEY_MAIL:
+      return kSbKeyMediaLaunchMail;
+    case KEY_CALC:
+      return kSbKeyMediaLaunchApp2;
+    case KEY_WLAN:
+      return kSbKeyWlan;
+    case KEY_POWER:
+      return kSbKeyPower;
+    case KEY_BRIGHTNESSDOWN:
+      return kSbKeyBrightnessDown;
+    case KEY_BRIGHTNESSUP:
+      return kSbKeyBrightnessUp;
+
+    case KEY_INFO_BUTTON:
+      return kSbKeyF1;
+  }
+  SB_DLOG(WARNING) << "Unknown code: 0x" << std::hex << code;
+  return kSbKeyUnknown;
+}  // NOLINT(readability/fn_size)
+
+// Get a SbKeyLocation from an input_event.code.
+static SbKeyLocation KeyCodeToSbKeyLocation(uint16_t code) {
+  switch (code) {
+    case KEY_LEFTALT:
+    case KEY_LEFTCTRL:
+    case KEY_LEFTMETA:
+    case KEY_LEFTSHIFT:
+      return kSbKeyLocationLeft;
+    case KEY_RIGHTALT:
+    case KEY_RIGHTCTRL:
+    case KEY_RIGHTMETA:
+    case KEY_RIGHTSHIFT:
+      return kSbKeyLocationRight;
+  }
+
+  return kSbKeyLocationUnspecified;
+}
+
+// keyboard_listener
+static void KeyboardHandleKeyMap(void* data,
+                                 struct wl_keyboard* keyboard,
+                                 uint32_t format,
+                                 int fd,
+                                 uint32_t size) {
+  SB_DLOG(INFO) << "[Key] Keyboard keymap";
+}
+
+static void KeyboardHandleEnter(void* data,
+                                struct wl_keyboard* keyboard,
+                                uint32_t serial,
+                                struct wl_surface* surface,
+                                struct wl_array* keys) {
+  SB_DLOG(INFO) << "[Key] Keyboard gained focus";
+}
+
+static void KeyboardHandleLeave(void* data,
+                                struct wl_keyboard* keyboard,
+                                uint32_t serial,
+                                struct wl_surface* surface) {
+  SB_DLOG(INFO) << "[Key] Keyboard lost focus";
+  ApplicationWayland* wayland_window_ =
+      reinterpret_cast<ApplicationWayland*> data;
+  wayland_window_->DeleteRepeatKey();
+}
+
+static void KeyboardHandleKey(void* data,
+                              struct wl_keyboard* keyboard,
+                              uint32_t serial,
+                              uint32_t time,
+                              uint32_t key,
+                              uint32_t state) {
+  SB_DLOG(INFO) << "[Key] Key :" << key << ", state:" << state;
+  ApplicationWayland* wayland_window_ =
+      reinterpret_cast<ApplicationWayland*> data;
+  if (key == KEY_LEFT || key == KEY_RIGHT || key == KEY_UP || key == KEY_DOWN) {
+    wayland_window_->CreateKey(key, state, true);
+  } else {
+    wayland_window_->CreateKey(key, state, false);
+  }
+}
+
+static void KeyboardHandleModifiers(void* data,
+                                    struct wl_keyboard* keyboard,
+                                    uint32_t serial,
+                                    uint32_t mods_depressed,
+                                    uint32_t mods_latched,
+                                    uint32_t mods_locked,
+                                    uint32_t group) {
+  SB_DLOG(INFO) << "[Key] Modifiers depressed " << mods_depressed
+                << ", latched " << mods_latched << ", locked " << mods_locked
+                << ", group " << group;
+}
+
+static const struct wl_keyboard_listener keyboard_listener_ = {
+    &KeyboardHandleKeyMap, &KeyboardHandleEnter,     &KeyboardHandleLeave,
+    &KeyboardHandleKey,    &KeyboardHandleModifiers,
+};
+
+// seat_listener
+static void SeatHandleCapabilities(void* data,
+                                   struct wl_seat* seat,
+                                   unsigned int caps) {
+  ApplicationWayland* wayland_window_ =
+      reinterpret_cast<ApplicationWayland*> data;
+  if (!wayland_window_->GetKeyboard()) {
+    SB_DLOG(INFO) << "[Key] seat_handle_capabilities caps: " << caps;
+    if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
+      SB_DLOG(INFO) << "[Key] wl_seat_get_keyboard";
+      wayland_window_->SetKeyboard(wl_seat_get_keyboard(seat));
+      wl_keyboard_add_listener(wayland_window_->GetKeyboard(),
+                               &keyboard_listener, data);
+    } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
+      SB_DLOG(INFO) << "[Key] wl_keyboard_destroy";
+      wl_keyboard_destroy(wayland_window_->GetKeyboard());
+      wayland_window_->SetKeyboard(NULL);
+    }
+  }
+}
+
+static const struct wl_seat_listener seat_listener = {
+    &SeatHandleCapabilities,
+};
+
+// registry_listener
+static void RegistryAddObject(void* data,
+                              struct wl_registry* registry,
+                              uint32_t name,
+                              const char* interface,
+                              uint32_t version) {
+  ApplicationWayland* wayland_window_ =
+      reinterpret_cast<ApplicationWayland*> data;
+  if (strcmp(interface, "wl_compositor") == 0) {
+    wayland_window_->SetCompositor(static_cast<wl_compositor*>(
+        wl_registry_bind(registry, name, &wl_compositor_interface, 1)));
+  } else if (strcmp(interface, "wl_shell") == 0) {
+    wayland_window_->SetShell(static_cast<wl_shell*>(
+        wl_registry_bind(registry, name, &wl_shell_interface, 1)));
+  } else if (strcmp(interface, "wl_seat") == 0) {
+    wayland_window_->SetSeat(static_cast<wl_seat*>(
+        wl_registry_bind(registry, name, &wl_seat_interface, 1)));
+    wl_seat_add_listener(wayland_window_->GetSeat(), &seat_listener, data);
+  } else if (!strcmp(interface, "tizen_policy")) {
+    wayland_window_->SetPolicy(static_cast<tizen_policy*>(
+        wl_registry_bind(registry, name, &tizen_policy_interface, 1)));
+  }
+}
+
+static void RegistryRemoveObject(void*, struct wl_registry*, uint32_t) {}
+
+static struct wl_registry_listener registry_listener = {&RegistryAddObject,
+                                                        &RegistryRemoveObject};
+
+// shell_surface_listener
+static void ShellSurfacePing(void*,
+                             struct wl_shell_surface* shell_surface,
+                             uint32_t serial) {
+  wl_shell_surface_pong(shell_surface, serial);
+}
+static void ShellSurfaceConfigure(void* data,
+                                  struct wl_shell_surface*,
+                                  uint32_t,
+                                  int32_t width,
+                                  int32_t height) {
+  SB_DLOG(INFO) << "shell_surface_configure width(" << width << "), height("
+                << height << ")";
+  if (width && height) {
+    SbWindowPrivate* window = reinterpret_cast<SbWindowPrivate*> data;
+    wl_egl_window_resize(window->egl_window, width, height, 0, 0);
+  } else {
+    SB_DLOG(INFO) << "width and height is 0. we don't resize that";
+  }
+}
+
+static void ShellSurfacePopupDone(void*, struct wl_shell_surface*) {}
+
+static struct wl_shell_surface_listener shell_surface_listener = {
+    &ShellSurfacePing, &ShellSurfaceConfigure, &ShellSurfacePopupDone};
+
+static void WindowCbVisibilityChange(void* data,
+                                     struct tizen_visibility* tizen_visibility
+                                         EINA_UNUSED,
+                                     uint32_t visibility) {
+  if (visibility == TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED)
+    ApplicationWayland::Get()->Pause();
+  else
+    ApplicationWayland::Get()->Unpause();
+}
+
+static const struct tizen_visibility_listener tizen_visibility_listener = {
+    WindowCbVisibilityChange};
+
+}  // namespace wayland
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_WAYLAND_DEV_INPUT_H_
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/starboard/shared/wayland/window_create.cc
similarity index 64%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/starboard/shared/wayland/window_create.cc
index b75f65f..1a9147d 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/starboard/shared/wayland/window_create.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2016 Samsung Electronics. 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.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
+#include "starboard/window.h"
 
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
+#include "starboard/shared/wayland/application_wayland.h"
+
+SbWindow SbWindowCreate(const SbWindowOptions* options) {
+  return starboard::shared::wayland::ApplicationWayland::Get()->CreateWindow(
+      options);
 }
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/starboard/shared/wayland/window_destroy.cc
similarity index 66%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/starboard/shared/wayland/window_destroy.cc
index b75f65f..733453d 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/starboard/shared/wayland/window_destroy.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2016 Samsung Electronics. 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.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
+#include "starboard/window.h"
 
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
+#include "starboard/shared/wayland/application_wayland.h"
+
+bool SbWindowDestroy(SbWindow window) {
+  return starboard::shared::wayland::ApplicationWayland::Get()->DestroyWindow(
+      window);
 }
diff --git a/src/starboard/shared/wayland/window_get_platform_handle.cc b/src/starboard/shared/wayland/window_get_platform_handle.cc
new file mode 100644
index 0000000..9fe7906
--- /dev/null
+++ b/src/starboard/shared/wayland/window_get_platform_handle.cc
@@ -0,0 +1,27 @@
+// Copyright 2016 Samsung Electronics. 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/window.h"
+
+#include "starboard/shared/wayland/application_wayland.h"
+#include "starboard/shared/wayland/window_internal.h"
+
+void* SbWindowGetPlatformHandle(SbWindow window) {
+  if (!SbWindowIsValid(window)) {
+    return NULL;
+  }
+  struct wl_egl_window* handle = window->egl_window;
+
+  return reinterpret_cast<void*>(handle);
+}
diff --git a/src/starboard/shared/wayland/window_get_size.cc b/src/starboard/shared/wayland/window_get_size.cc
new file mode 100644
index 0000000..436eec1
--- /dev/null
+++ b/src/starboard/shared/wayland/window_get_size.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 Samsung Electronics. 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/window.h"
+
+#include "starboard/log.h"
+#include "starboard/shared/wayland/window_internal.h"
+
+bool SbWindowGetSize(SbWindow window, SbWindowSize* size) {
+  if (!SbWindowIsValid(window)) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid window.";
+    return false;
+  }
+
+  size->width = window->width;
+  size->height = window->height;
+#if SB_HAS_MEDIA_4K_SUPPORT
+  size->video_pixel_ratio = 2.0f;
+#else
+  size->video_pixel_ratio = 1.0f;
+#endif
+  return true;
+}
diff --git a/src/starboard/shared/wayland/window_internal.cc b/src/starboard/shared/wayland/window_internal.cc
new file mode 100644
index 0000000..3b60686
--- /dev/null
+++ b/src/starboard/shared/wayland/window_internal.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 Samsung Electronics. 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/shared/wayland/window_internal.h"
+
+namespace {
+const int kWindowWidth = 1920;
+const int kWindowHeight = 1080;
+}
+
+SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* options) {
+  width = kWindowWidth;
+  height = kWindowHeight;
+  if (options && options->size.width > 0 && options->size.height > 0) {
+    width = options->size.width;
+    height = options->size.height;
+  }
+}
diff --git a/src/starboard/shared/wayland/window_internal.h b/src/starboard/shared/wayland/window_internal.h
new file mode 100644
index 0000000..574e2dd
--- /dev/null
+++ b/src/starboard/shared/wayland/window_internal.h
@@ -0,0 +1,46 @@
+// Copyright 2016 Samsung Electronics. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_WAYLAND_WINDOW_INTERNAL_H_
+#define STARBOARD_SHARED_WAYLAND_WINDOW_INTERNAL_H_
+
+#include <Elementary.h>
+#include <string.h>
+#include <tizen-extension-client-protocol.h>
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+#include "starboard/window.h"
+
+struct SbWindowPrivate {
+  explicit SbWindowPrivate(const SbWindowOptions* options);
+  ~SbWindowPrivate() {}
+
+  struct wl_surface* surface;
+  struct wl_shell_surface* shell_surface;
+  struct wl_egl_window* egl_window;
+  struct tizen_visibility* tz_visibility;
+
+#if SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
+  wl_display* video_window;
+#else
+  Evas_Object* video_window;
+#endif
+
+  // The width, height of this window.
+  int width;
+  int height;
+};
+
+#endif  // STARBOARD_SHARED_WAYLAND_WINDOW_INTERNAL_H_
diff --git a/src/starboard/tizen/armv7l/atomic_public.h b/src/starboard/tizen/armv7l/atomic_public.h
new file mode 100644
index 0000000..d42b571
--- /dev/null
+++ b/src/starboard/tizen/armv7l/atomic_public.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 Samsung Electronics. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STARBOARD_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
+#define STARBOARD_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
+
+#include "starboard/tizen/shared/atomic_public.h"
+
+#endif  // STARBOARD_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/tizen/armv7l/configuration_public.h b/src/starboard/tizen/armv7l/configuration_public.h
new file mode 100644
index 0000000..9b1c14a
--- /dev/null
+++ b/src/starboard/tizen/armv7l/configuration_public.h
@@ -0,0 +1,196 @@
+// Copyright 2016 Samsung Electronics. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for armv7l Tizen. Other devices will have
+// specific Starboard implementations, even if they ultimately are running some
+// version of Tizen.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
+
+// --- Architecture Configuration --------------------------------------------
+
+// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
+// automatically set based on this.
+#define SB_IS_BIG_ENDIAN 0
+
+// Whether the current platform is an ARM architecture.
+#define SB_IS_ARCH_ARM 1
+
+// Whether the current platform is a MIPS architecture.
+#define SB_IS_ARCH_MIPS 0
+
+// Whether the current platform is a PPC architecture.
+#define SB_IS_ARCH_PPC 0
+
+// Whether the current platform is an x86 architecture.
+#define SB_IS_ARCH_X86 0
+
+// Whether the current platform is a 32-bit architecture.
+#define SB_IS_32_BIT 1
+
+#define SB_IS_64_BIT 0
+// Whether the current platform's pointers are 32-bit.
+// Whether the current platform's longs are 32-bit.
+#if SB_IS(32_BIT)
+#define SB_HAS_32_BIT_POINTERS 1
+#define SB_HAS_32_BIT_LONG 1
+#else
+#define SB_HAS_32_BIT_POINTERS 0
+#define SB_HAS_32_BIT_LONG 0
+#endif
+
+// Whether the current platform's pointers are 64-bit.
+// Whether the current platform's longs are 64-bit.
+#if SB_IS(64_BIT)
+#define SB_HAS_64_BIT_POINTERS 1
+#define SB_HAS_64_BIT_LONG 1
+#else
+#define SB_HAS_64_BIT_POINTERS 0
+#define SB_HAS_64_BIT_LONG 0
+#endif
+
+// Configuration parameters that allow the application to make some general
+// compile-time decisions with respect to the the number of cores likely to be
+// available on this platform. For a definitive measure, the application should
+// still call SbSystemGetNumberOfProcessors at runtime.
+
+// Whether the current platform is expected to have many cores (> 6), or a
+// wildly varying number of cores.
+#define SB_HAS_MANY_CORES 1
+
+// Whether the current platform is expected to have exactly 1 core.
+#define SB_HAS_1_CORE 0
+
+// Whether the current platform is expected to have exactly 2 cores.
+#define SB_HAS_2_CORES 0
+
+// Whether the current platform is expected to have exactly 4 cores.
+#define SB_HAS_4_CORES 0
+
+// Whether the current platform is expected to have exactly 6 cores.
+#define SB_HAS_6_CORES 0
+
+// Whether the current platform's thread scheduler will automatically balance
+// threads between cores, as opposed to systems where threads will only ever run
+// on the specifically pinned core.
+#define SB_HAS_CROSS_CORE_SCHEDULER 1
+
+// --- Media Configuration ---------------------------------------------------
+
+// Specifies whether this platform has support for a possibly-decrypting
+// elementary stream player for at least H.264/AAC (and AES-128-CTR, if
+// decrypting). A player is responsible for ingesting an audio and video
+// elementary stream, optionally-encrypted, and ultimately producing
+// synchronized audio/video. If a player is defined, it must choose one of the
+// supported composition methods below.
+#define SB_HAS_PLAYER 1
+
+// Specifies whether this platform's player will produce an OpenGL texture that
+// the client must draw every frame with its graphics rendering. It may be that
+// we get a texture handle, but cannot perform operations like GlReadPixels on
+// it if it is DRM-protected.
+#define SB_IS_PLAYER_PRODUCING_TEXTURE 0
+
+// Specifies whether this platform's player is composited with a formal
+// compositor, where the client must specify how video is to be composited into
+// the graphicals scene.
+#define SB_IS_PLAYER_COMPOSITED 0
+
+// Specifies whether this platform's player uses a "punch-out" model, where
+// video is rendered to the far background, and the graphics plane is
+// automatically composited on top of the video by the platform. The client must
+// punch an alpha hole out of the graphics plane for video to show through.  In
+// this case, changing the video bounds must be tightly synchronized between the
+// player and the graphics plane.
+#define SB_IS_PLAYER_PUNCHED_OUT 1
+
+// Specifies the maximum amount of memory used by audio buffers of media source
+// before triggering a garbage collection.  A large value will cause more memory
+// being used by audio buffers but will also make JavaScript app less likely to
+// re-download audio data.  Note that the JavaScript app may experience
+// significant difficulty if this value is too low.
+#define SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT (3U * 1024U * 1024U)
+
+// Specifies the maximum amount of memory used by video buffers of media source
+// before triggering a garbage collection.  A large value will cause more memory
+// being used by video buffers but will also make JavaScript app less likely to
+// re-download video data.  Note that the JavaScript app may experience
+// significant difficulty if this value is too low.
+#define SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT (16U * 1024U * 1024U)
+
+// Specifies how much memory to reserve up-front for the main media buffer
+// (usually resides inside the CPU memory) used by media source and demuxers.
+// The main media buffer can work in one of the following two ways:
+// 1. If GPU buffer is used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is non-zero), the
+//    main buffer will be used as a cache so a media buffer will be copied from
+//    GPU memory to main memory before sending to the decoder for further
+//    processing.  In this case this macro should be set to a value that is
+//    large enough to hold all media buffers being decoded.
+// 2. If GPU buffer is not used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is zero) all
+//    media buffers will reside in the main memory buffer.  In this case the
+//    macro should be set to a value that is greater than the sum of the above
+//    source buffer stream memory limits with extra room to take account of
+//    fragmentations and memory used by demuxers.
+#define SB_MEDIA_MAIN_BUFFER_BUDGET (80U * 1024U * 1024U)
+
+// Specifies how much GPU memory to reserve up-front for media source buffers.
+// This should only be set to non-zero on system with limited CPU memory and
+// excess GPU memory so the app can store media buffer in GPU memory.
+// SB_MEDIA_MAIN_BUFFER_BUDGET has to be set to a non-zero value to avoid
+// media buffers being decoded when being stored in GPU.
+#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+
+// Specifies whether this platform has webm/vp9 support.  This should be set to
+// non-zero on platforms with webm/vp9 support.
+#define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
+
+// Specifies the stack size for threads created inside media stack.  Set to 0 to
+// use the default thread stack size.  Set to non-zero to explicitly set the
+// stack size for media stack threads.
+#define SB_MEDIA_THREAD_STACK_SIZE 0U
+
+// --- Decoder-only Params ---
+
+// Specifies how media buffers must be aligned on this platform as some
+// decoders may have special requirement on the alignment of buffers being
+// decoded.
+#define SB_MEDIA_BUFFER_ALIGNMENT 128U
+
+// Specifies how video frame buffers must be aligned on this platform.
+#define SB_MEDIA_VIDEO_FRAME_ALIGNMENT 256U
+
+// The encoded video frames are compressed in different ways, their decoding
+// time can vary a lot.  Occasionally a single frame can take longer time to
+// decode than the average time per frame.  The player has to cache some frames
+// to account for such inconsistency.  The number of frames being cached are
+// controlled by the following two macros.
+//
+// Specify the number of video frames to be cached before the playback starts.
+// Note that set this value too large may increase the playback start delay.
+#define SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES 4
+
+// Specify the number of video frames to be cached during playback.  A large
+// value leads to more stable fps but also causes the app to use more memory.
+#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
+
+// --- Common Configuration ---------------------------------------------------
+
+// Include the Tizen configuration that's common between all Tizen.
+#include "starboard/tizen/shared/configuration_public.h"
+
+#endif  // STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/tizen/armv7l/gyp_configuration.gypi b/src/starboard/tizen/armv7l/gyp_configuration.gypi
new file mode 100644
index 0000000..711f40a
--- /dev/null
+++ b/src/starboard/tizen/armv7l/gyp_configuration.gypi
@@ -0,0 +1,146 @@
+# Copyright 2016 Samsung Electronics. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    'target_arch': 'arm',
+    'target_os': 'linux',
+    'tizen_os': 1,
+
+    'gl_type': 'system_gles2',
+
+    # Tizen uses ARMv7
+    'arm_version': 7,
+    'armv7': 1,
+    'arm_neon': 0,
+
+    # Some package use tizen system instead of third_party
+    'use_system_icu': 1,
+    'use_system_libxml': 1,
+
+    # scratch surface cache is designed to choose large offscreen surfaces so
+    # that they can be maximally reused, it is not a very good fit for a tiled
+    # renderer.
+    'scratch_surface_cache_size_in_bytes' : 0,
+
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 0,
+
+    # Reduce garbage collection threshold from the default of 8MB in order to
+    # save on memory.  This will mean that garbage collection occurs more
+    # frequently.
+    'mozjs_garbage_collection_threshold_in_bytes%': 4 * 1024 * 1024,
+
+    'platform_libraries': [
+      '-lasound',
+      '-lavcodec',
+      '-lavformat',
+      '-lavutil',
+      '-ldlog',
+    ],
+    'linker_flags': [
+    ],
+    'linker_flags_gold': [
+      '-O3',
+      '-flto',
+    ],
+    'compiler_flags_debug': [
+      '-O0',
+    ],
+    'compiler_flags_devel': [
+      '-O2',
+    ],
+    'compiler_flags_cc_qa': [
+      '-fno-rtti',
+    ],
+    'compiler_flags_qa': [
+      '-O3',
+    ],
+    'compiler_flags_cc_gold': [
+      '-fno-rtti',
+    ],
+    'compiler_flags_gold': [
+      '-O3',
+    ],
+    'conditions': [
+      ['cobalt_fastbuild==0', {
+        'compiler_flags_debug': [
+          '-g',
+        ],
+        'compiler_flags_devel': [
+          '-g',
+        ],
+        'compiler_flags_qa': [
+        ],
+        'compiler_flags_gold': [
+          '-flto',
+        ],
+      }],
+    ],
+  },
+
+  'target_defaults': {
+    'defines': [
+      # Cobalt on Tizen flag
+      'COBALT_TIZEN',
+      'PNG_SKIP_SETJMP_CHECK',
+      '__STDC_FORMAT_MACROS', # so that we get PRI*
+      # Enable GNU extensions to get prototypes like ffsl.
+      '_GNU_SOURCE=1',
+    ],
+    'cflags': [
+      '-pthread',
+      # Do not warn about locally defined but not used.
+      '-Wno-unused-local-typedefs',
+      # Do not warn about XXX is deprecated.
+      '-Wno-deprecated-declarations',
+      # Do not warn about missing initializer for member XXX.
+      '-Wno-missing-field-initializers',
+      # Do not warn about unused functions.
+      '-Wno-unused-function',
+      # Do not warn about type qualifiers ignored on function return type.
+      '-Wno-ignored-qualifiers',
+      # Do not warn about the use of multi-line comments.
+      '-Wno-comment',
+      # Do not warn about sign compares.
+      '-Wno-sign-compare',
+    ],
+    'cflags_c': [
+      '-std=c11',
+    ],
+    'cflags_cc': [
+      '-std=gnu++11',
+    ],
+    'ldflags': [
+      '-pthread',
+    ],
+    'default_configuration': 'tizen-armv7l_debug',
+    'configurations': {
+      'tizen-armv7l_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'tizen-armv7l_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'tizen-armv7l_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'tizen-armv7l_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  }, # end of target_defaults
+}
diff --git a/src/starboard/tizen/armv7l/gyp_configuration.py b/src/starboard/tizen/armv7l/gyp_configuration.py
new file mode 100644
index 0000000..3cee759
--- /dev/null
+++ b/src/starboard/tizen/armv7l/gyp_configuration.py
@@ -0,0 +1,50 @@
+# Copyright 2016 Samsung Electronics. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Tizen armv7l platform configuration for gyp_cobalt."""
+
+import logging
+
+import config.starboard
+
+
+def CreatePlatformConfig():
+  try:
+    return _PlatformConfig('tizen-armv7l')
+  except RuntimeError as e:
+    logging.critical(e)
+    return None
+
+
+class _PlatformConfig(config.starboard.PlatformConfigStarboard):
+  """Starboard Tizen Armv7l platform configuration."""
+
+  def __init__(self, platform):
+    super(_PlatformConfig, self).__init__(platform)
+
+  def GetVariables(self, configuration):
+    variables = super(_PlatformConfig, self).GetVariables(configuration)
+    variables.update({
+        'clang': 0,
+    })
+
+    return variables
+
+  def GetEnvironmentVariables(self):
+    env_variables = {
+        'CC': 'armv7l-tizen-linux-gnueabi-gcc',
+        'CXX': 'armv7l-tizen-linux-gnueabi-g++',
+        'CC_host': 'armv7l-tizen-linux-gnueabi-gcc',
+        'CXX_host': 'armv7l-tizen-linux-gnueabi-g++',
+    }
+    return env_variables
diff --git a/src/starboard/tizen/armv7l/starboard_common.gyp b/src/starboard/tizen/armv7l/starboard_common.gyp
new file mode 100644
index 0000000..faa178c
--- /dev/null
+++ b/src/starboard/tizen/armv7l/starboard_common.gyp
@@ -0,0 +1,257 @@
+# Copyright 2016 Samsung Electronics. 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.
+{
+  'targets': [
+    {
+      'target_name': 'starboard_base_symbolize',
+      'type': 'static_library',
+      'sources': [
+        '<(DEPTH)/base/third_party/symbolize/demangle.cc',
+        '<(DEPTH)/base/third_party/symbolize/symbolize.cc',
+      ],
+    },
+    {
+      'target_name': 'starboard_common',
+      'type': 'static_library',
+      'sources': [
+        '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
+        '<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
+        '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h',
+        '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc',
+        '<(DEPTH)/starboard/shared/iso/character_is_digit.cc',
+        '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc',
+        '<(DEPTH)/starboard/shared/iso/character_is_space.cc',
+        '<(DEPTH)/starboard/shared/iso/character_is_upper.cc',
+        '<(DEPTH)/starboard/shared/iso/character_to_lower.cc',
+        '<(DEPTH)/starboard/shared/iso/character_to_upper.cc',
+        '<(DEPTH)/starboard/shared/iso/directory_close.cc',
+        '<(DEPTH)/starboard/shared/iso/directory_get_next.cc',
+        '<(DEPTH)/starboard/shared/iso/directory_open.cc',
+        '<(DEPTH)/starboard/shared/iso/double_absolute.cc',
+        '<(DEPTH)/starboard/shared/iso/double_exponent.cc',
+        '<(DEPTH)/starboard/shared/iso/double_floor.cc',
+        '<(DEPTH)/starboard/shared/iso/double_is_finite.cc',
+        '<(DEPTH)/starboard/shared/iso/double_is_nan.cc',
+        '<(DEPTH)/starboard/shared/iso/memory_compare.cc',
+        '<(DEPTH)/starboard/shared/iso/memory_copy.cc',
+        '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc',
+        '<(DEPTH)/starboard/shared/iso/memory_move.cc',
+        '<(DEPTH)/starboard/shared/iso/memory_set.cc',
+        '<(DEPTH)/starboard/shared/iso/string_compare.cc',
+        '<(DEPTH)/starboard/shared/iso/string_compare_all.cc',
+        '<(DEPTH)/starboard/shared/iso/string_find_character.cc',
+        '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc',
+        '<(DEPTH)/starboard/shared/iso/string_find_string.cc',
+        '<(DEPTH)/starboard/shared/iso/string_get_length.cc',
+        '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc',
+        '<(DEPTH)/starboard/shared/iso/string_parse_double.cc',
+        '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc',
+        '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc',
+        '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc',
+        '<(DEPTH)/starboard/shared/iso/string_scan.cc',
+        '<(DEPTH)/starboard/shared/iso/system_binary_search.cc',
+        '<(DEPTH)/starboard/shared/iso/system_sort.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_add.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_create.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_destroy.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_internal.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_remove.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc',
+        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc',
+        '<(DEPTH)/starboard/shared/linux/byte_swap.cc',
+        '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
+        '<(DEPTH)/starboard/shared/linux/page_internal.cc',
+        '<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc',
+        '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
+        '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
+        '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
+        '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
+        '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
+        '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
+        '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
+        '<(DEPTH)/starboard/shared/linux/thread_get_name.cc',
+        '<(DEPTH)/starboard/shared/linux/thread_set_name.cc',
+        '<(DEPTH)/starboard/shared/nouser/user_get_current.cc',
+        '<(DEPTH)/starboard/shared/nouser/user_get_property.cc',
+        '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc',
+        '<(DEPTH)/starboard/shared/nouser/user_internal.cc',
+        '<(DEPTH)/starboard/shared/posix/directory_create.cc',
+        '<(DEPTH)/starboard/shared/posix/file_can_open.cc',
+        '<(DEPTH)/starboard/shared/posix/file_close.cc',
+        '<(DEPTH)/starboard/shared/posix/file_delete.cc',
+        '<(DEPTH)/starboard/shared/posix/file_exists.cc',
+        '<(DEPTH)/starboard/shared/posix/file_flush.cc',
+        '<(DEPTH)/starboard/shared/posix/file_get_info.cc',
+        '<(DEPTH)/starboard/shared/posix/file_get_path_info.cc',
+        '<(DEPTH)/starboard/shared/posix/file_open.cc',
+        '<(DEPTH)/starboard/shared/posix/file_read.cc',
+        '<(DEPTH)/starboard/shared/posix/file_seek.cc',
+        '<(DEPTH)/starboard/shared/posix/file_truncate.cc',
+        '<(DEPTH)/starboard/shared/posix/file_write.cc',
+        '<(DEPTH)/starboard/shared/posix/log_flush.cc',
+        '<(DEPTH)/starboard/shared/posix/log_format.cc',
+        '<(DEPTH)/starboard/shared/posix/log_is_tty.cc',
+        '<(DEPTH)/starboard/shared/posix/log_raw.cc',
+        '<(DEPTH)/starboard/shared/posix/memory_flush.cc',
+        '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_accept.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_bind.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_clear_last_error.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_connect.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_create.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_destroy.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_free_resolution.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_get_last_error.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_get_local_address.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_internal.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_is_connected.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_is_connected_and_idle.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_join_multicast_group.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_listen.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_receive_from.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_resolve.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_send_to.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_set_broadcast.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_set_receive_buffer_size.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_set_reuse_address.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_set_send_buffer_size.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_keep_alive.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_no_delay.cc',
+        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_window_scaling.cc',
+        '<(DEPTH)/starboard/shared/posix/string_compare_no_case.cc',
+        '<(DEPTH)/starboard/shared/posix/string_compare_no_case_n.cc',
+        '<(DEPTH)/starboard/shared/posix/string_compare_wide.cc',
+        '<(DEPTH)/starboard/shared/posix/string_format.cc',
+        '<(DEPTH)/starboard/shared/posix/string_format_wide.cc',
+        '<(DEPTH)/starboard/shared/posix/system_break_into_debugger.cc',
+        '<(DEPTH)/starboard/shared/posix/system_clear_last_error.cc',
+        '<(DEPTH)/starboard/shared/posix/system_get_error_string.cc',
+        '<(DEPTH)/starboard/shared/posix/system_get_last_error.cc',
+        '<(DEPTH)/starboard/shared/posix/system_get_locale_id.cc',
+        '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc',
+        '<(DEPTH)/starboard/shared/posix/thread_sleep.cc',
+        '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc',
+        '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc',
+        '<(DEPTH)/starboard/shared/posix/time_get_now.cc',
+        '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc',
+        '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
+        '<(DEPTH)/starboard/shared/posix/time_zone_get_name.cc',
+        '<(DEPTH)/starboard/shared/pthread/condition_variable_broadcast.cc',
+        '<(DEPTH)/starboard/shared/pthread/condition_variable_create.cc',
+        '<(DEPTH)/starboard/shared/pthread/condition_variable_destroy.cc',
+        '<(DEPTH)/starboard/shared/pthread/condition_variable_signal.cc',
+        '<(DEPTH)/starboard/shared/pthread/condition_variable_wait.cc',
+        '<(DEPTH)/starboard/shared/pthread/condition_variable_wait_timed.cc',
+        '<(DEPTH)/starboard/shared/pthread/mutex_acquire.cc',
+        '<(DEPTH)/starboard/shared/pthread/mutex_acquire_try.cc',
+        '<(DEPTH)/starboard/shared/pthread/mutex_create.cc',
+        '<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc',
+        '<(DEPTH)/starboard/shared/pthread/mutex_release.cc',
+        '<(DEPTH)/starboard/shared/pthread/once.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
+        '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_get_local_value.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_is_equal.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_join.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_set_local_value.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_yield.cc',
+        '<(DEPTH)/starboard/shared/signal/crash_signals.h',
+        '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc',
+        '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
+        '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
+        '<(DEPTH)/starboard/shared/starboard/application.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.h',
+        '<(DEPTH)/starboard/shared/starboard/directory_can_open.cc',
+        '<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
+        '<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
+        '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc',
+        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc',
+        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc',
+        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
+        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
+        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
+        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc',
+        '<(DEPTH)/starboard/shared/starboard/log_message.cc',
+        '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
+        '<(DEPTH)/starboard/shared/starboard/log_raw_format.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
+        '<(DEPTH)/starboard/shared/starboard/new.cc',
+        '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
+        '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
+        '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc',
+        '<(DEPTH)/starboard/shared/starboard/string_copy.cc',
+        '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
+        '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+        '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
+        '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
+        '<(DEPTH)/starboard/shared/stub/microphone_close.cc',
+        '<(DEPTH)/starboard/shared/stub/microphone_create.cc',
+        '<(DEPTH)/starboard/shared/stub/microphone_destroy.cc',
+        '<(DEPTH)/starboard/shared/stub/microphone_get_available.cc',
+        '<(DEPTH)/starboard/shared/stub/microphone_is_sample_rate_supported.cc',
+        '<(DEPTH)/starboard/shared/stub/microphone_open.cc',
+        '<(DEPTH)/starboard/shared/stub/microphone_read.cc',
+        '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc',
+        '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
+        '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
+        '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
+        '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
+        '<(DEPTH)/starboard/shared/wayland/application_wayland.cc',
+        '<(DEPTH)/starboard/shared/wayland/window_create.cc',
+        '<(DEPTH)/starboard/shared/wayland/window_destroy.cc',
+        '<(DEPTH)/starboard/shared/wayland/window_get_platform_handle.cc',
+        '<(DEPTH)/starboard/shared/wayland/window_get_size.cc',
+        '<(DEPTH)/starboard/shared/wayland/window_internal.cc',
+        '<(DEPTH)/starboard/tizen/shared/system_get_device_type.cc',
+        '<(DEPTH)/starboard/tizen/shared/system_get_path.cc',
+        '<(DEPTH)/starboard/tizen/shared/audio/audio_sink_adaptor.cc',
+        '<(DEPTH)/starboard/tizen/shared/audio/audio_sink_private.cc',
+        '<(DEPTH)/starboard/tizen/shared/log/log.cc',
+        '<(DEPTH)/starboard/tizen/shared/get_home_directory.cc',
+      ],
+      'defines': [
+        # This must be defined when building Starboard, and must not when
+        # building Starboard client code.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/common/common.gyp:common',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:capi-appfw-application',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:capi-media-audio-io',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:edbus',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:elementary',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:evas',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:gles20',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:tizen-extension-client',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:wayland-egl',
+        '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
+        'starboard_base_symbolize',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/tizen/armv7l/starboard_platform.gyp b/src/starboard/tizen/armv7l/starboard_platform.gyp
new file mode 100644
index 0000000..8cc53ab
--- /dev/null
+++ b/src/starboard/tizen/armv7l/starboard_platform.gyp
@@ -0,0 +1,96 @@
+# Copyright 2016 Samsung Electronics. 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.
+{
+  'targets': [
+    {
+      'target_name': 'starboard_platform',
+      'type': 'static_library',
+      'sources': [
+        '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
+        '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/player_components.h',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/job_queue.h',
+        '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
+        '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h',
+        '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
+        '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
+        '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
+        '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+        '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+        '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
+        '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
+        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc',
+        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h',
+        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc',
+        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_common.h',
+        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc',
+        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h',
+        '<(DEPTH)/starboard/tizen/shared/main.cc',
+        '<(DEPTH)/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc',
+        'atomic_public.h',
+        'configuration_public.h',
+        'system_get_property.cc',
+      ],
+      'defines': [
+        # This must be defined when building Starboard, and must not when
+        # building Starboard client code.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/common/common.gyp:common',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:aul',
+        '<(DEPTH)/starboard/tizen/shared/system.gyp:elementary',
+        '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
+        'starboard_common.gyp:starboard_common',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/tizen/armv7l/system_get_property.cc b/src/starboard/tizen/armv7l/system_get_property.cc
new file mode 100644
index 0000000..ee6665a
--- /dev/null
+++ b/src/starboard/tizen/armv7l/system_get_property.cc
@@ -0,0 +1,83 @@
+// Copyright 2016 Samsung Electronics. 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/system.h"
+
+#include "starboard/log.h"
+#include "starboard/string.h"
+
+namespace {
+
+const char* kBrandName = "Samsung";
+const char* kChipsetModelNumber = "JazzM";
+const char* kFirmwareVersion = "T-JZMPAKUC";
+const char* kFriendlyName = "Tizen";
+const char* kModelName = "UN49KS9000";
+const char* kModelYear = "2017";
+const char* kPlatformName = "LINUX; Tizen 3.0";
+
+bool CopyStringAndTestIfSuccess(char* out_value,
+                                int value_length,
+                                const char* from_value) {
+  if (SbStringGetLength(from_value) + 1 > value_length)
+    return false;
+  SbStringCopy(out_value, from_value, value_length);
+  return true;
+}
+
+}  // namespace
+
+static char* model_number;
+
+bool SbSystemGetProperty(SbSystemPropertyId property_id,
+                         char* out_value,
+                         int value_length) {
+  if (!out_value || !value_length) {
+    return false;
+  }
+
+  switch (property_id) {
+    case kSbSystemPropertyBrandName:
+      return CopyStringAndTestIfSuccess(out_value, value_length, kBrandName);
+    case kSbSystemPropertyChipsetModelNumber:
+      return CopyStringAndTestIfSuccess(out_value, value_length,
+                                        kChipsetModelNumber);
+    case kSbSystemPropertyFirmwareVersion:
+      return CopyStringAndTestIfSuccess(out_value, value_length,
+                                        kFirmwareVersion);
+    case kSbSystemPropertyModelName:
+      return CopyStringAndTestIfSuccess(out_value, value_length, kModelName);
+    case kSbSystemPropertyModelYear:
+      return CopyStringAndTestIfSuccess(out_value, value_length, kModelYear);
+    case kSbSystemPropertyNetworkOperatorName:
+      return false;
+
+    case kSbSystemPropertyFriendlyName:
+      return CopyStringAndTestIfSuccess(out_value, value_length, kFriendlyName);
+
+    case kSbSystemPropertyPlatformName:
+      return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName);
+
+    case kSbSystemPropertyPlatformUuid:
+      SB_NOTIMPLEMENTED();
+      return CopyStringAndTestIfSuccess(out_value, value_length, "N/A");
+
+    default:
+      SB_DLOG(WARNING) << __FUNCTION__
+                       << ": Unrecognized property: " << property_id;
+      break;
+  }
+
+  return false;
+}
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/starboard/tizen/armv7l/thread_types_public.h
similarity index 62%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/starboard/tizen/armv7l/thread_types_public.h
index b75f65f..301fc63 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/starboard/tizen/armv7l/thread_types_public.h
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2016 Samsung Electronics. 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.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
+// Includes threading primitive types and initializers.
 
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
-}
+#ifndef STARBOARD_TIZEN_ARMV7L_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_TIZEN_ARMV7L_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/shared/pthread/types_public.h"
+
+#endif  // STARBOARD_TIZEN_ARMV7L_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/tizen/packaging/README.md b/src/starboard/tizen/packaging/README.md
new file mode 100644
index 0000000..dc3c3a9
--- /dev/null
+++ b/src/starboard/tizen/packaging/README.md
@@ -0,0 +1,26 @@
+## Introduction
+
+cobalt tizen is a Cobalt engine port to tizen platform. The port
+implements starboard/tizen platform APIs.
+
+## Precondition
+
+1. Install gbs package
+  - Add Source list
+    $ sudo vi /etc/apt/sources.list
+      deb http://download.tizen.org/tools/latest-release/Ubuntu_14.04/ /
+    $ sudo apt-get update
+    $ sudo apt-get install gbs
+    $ gbs --version
+
+## Building
+
+1. Build commands:
+  - Need to add packaging-dir option
+    --packaging-dir src/starboard/tizen/packaging/
+  - armv7l
+    $ gbs -c packaging/gbs.conf -v build -A armv7l -P profile.cobalt [options]
+    Detail option see "gbs --help" and "gbs -v build --help"
+    ex) gbs -c src/starboard/tizen/packaging/gbs.conf -v build -A armv7l -P profile.cobalt --packaging-dir src/starboard/tizen/packaging/
+
+cobalt tizen code uses the BSD license, see our `LICENSE` file.
diff --git a/src/starboard/tizen/packaging/com.youtube.cobalt.manifest b/src/starboard/tizen/packaging/com.youtube.cobalt.manifest
new file mode 100644
index 0000000..baba947
--- /dev/null
+++ b/src/starboard/tizen/packaging/com.youtube.cobalt.manifest
@@ -0,0 +1,9 @@
+<manifest>
+  <define>
+    <domain name="com.youtube.cobalt" policy="shared" />
+  </define>
+  <request>
+    <domain name="_"/>
+  </request>
+</manifest>
+
diff --git a/src/starboard/tizen/packaging/com.youtube.cobalt.spec b/src/starboard/tizen/packaging/com.youtube.cobalt.spec
new file mode 100644
index 0000000..de10239
--- /dev/null
+++ b/src/starboard/tizen/packaging/com.youtube.cobalt.spec
@@ -0,0 +1,128 @@
+Name:      com.youtube.cobalt
+Summary:   Cobalt for Tizen
+Version:   0.1
+Release:   1
+License:   MPL or BSD-3-clause
+Source0:   %{name}-%{version}.tar.gz
+
+Requires: /usr/bin/systemctl
+Requires(post): /sbin/ldconfig
+Requires(post): xkeyboard-config
+Requires(postun): /sbin/ldconfig
+
+#####################
+# rpm require package
+#####################
+BuildRequires: cmake, bison, python, gperf
+BuildRequires: libasound-devel
+BuildRequires: pkgconfig(aul)
+BuildRequires: pkgconfig(capi-appfw-application)
+BuildRequires: pkgconfig(capi-media-audio-io)
+BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(edbus)
+BuildRequires: pkgconfig(elementary)
+BuildRequires: pkgconfig(evas)
+BuildRequires: pkgconfig(gles20)
+BuildRequires: pkgconfig(icu-i18n)
+BuildRequires: pkgconfig(libxml-2.0)
+BuildRequires: pkgconfig(openssl)
+BuildRequires: pkgconfig(tizen-extension-client)
+BuildRequires: pkgconfig(wayland-client)
+BuildRequires: pkgconfig(wayland-egl)
+%if "%{?target}" == "samsungtv"
+BuildRequires: pkgconfig(capi-media-player)
+BuildRequires: pkgconfig(drmdecrypt)
+%else
+BuildRequires: pkgconfig(libavcodec)
+BuildRequires: pkgconfig(libavformat)
+BuildRequires: pkgconfig(libavutil)
+%endif
+
+%description
+Youtube Engine based on Cobalt
+
+%prep
+%setup -q
+
+#define specific parameters
+%if "%{?build_type}"
+%define _build_type %{build_type}
+%else
+%define _build_type devel
+%endif
+
+%if "%{?target}"
+%define _target %{target}
+%else
+%define _target tizen
+%endif
+
+%if "%{?chipset}"
+%define _chipset %{chipset}
+%else
+%define _chipset armv7l
+%endif
+
+%define _name cobalt
+%define _pkgname com.youtube.cobalt
+%define _outdir src/out/%{_target}-%{_chipset}_%{_build_type}
+%define _manifestdir /usr/share/packages
+%define _pkgdir /usr/apps/%{_pkgname}
+%define _bindir %{_pkgdir}/bin
+%define _contentdir %{_pkgdir}/content
+%define _build_create_debug 0
+echo "Name: %{_name} / Target: %{_target} / Chipset: %{_chipset} / Build Type: %{_build_type}"
+
+#####################
+# rpm build
+#####################
+%build
+#gyp generate
+%if 0%{?nogyp}
+echo "Skip GYP"
+%else
+./src/cobalt/build/gyp_cobalt -v -C %{_build_type} %{_target}-%{_chipset}
+%endif
+#ninja build
+src/cobalt/build/ninja/ninja-linux32.armv7l \
+-C %{_outdir} %{_name}
+
+%clean
+#Don't delete src/out
+#rm -rf src/out
+
+#####################
+# rpm install
+#####################
+%install
+rm -rf %{buildroot}
+install -d %{buildroot}%{_bindir}
+install -d %{buildroot}%{_manifestdir}
+install -d %{buildroot}%{_contentdir}/data/fonts/
+install -m 0755 %{_outdir}/%{_name} %{buildroot}%{_bindir}
+%if "%{?target}" == "samsungtv"
+cp -rd %{_outdir}/content/data/fonts/fonts.xml %{buildroot}%{_contentdir}/data/fonts/
+%else
+cp -rd %{_outdir}/content/data/fonts %{buildroot}%{_contentdir}/data/
+%endif
+cp -rd %{_outdir}/content/data/ssl %{buildroot}%{_contentdir}/data/
+%if %{_build_type} != "gold"
+cp -rd %{_outdir}/content/data/web %{buildroot}%{_contentdir}/data/
+%endif
+cp src/starboard/tizen/packaging/%{_pkgname}.xml %{buildroot}%{_manifestdir}
+
+%post
+
+%postun
+
+#####################
+# rpm files
+#####################
+%files
+%manifest src/starboard/tizen/packaging/%{_pkgname}.manifest
+%defattr(-,root,root,-)
+%{_bindir}/%{_name}
+%{_contentdir}/*
+%{_manifestdir}/*
+
diff --git a/src/starboard/tizen/packaging/com.youtube.cobalt.xml b/src/starboard/tizen/packaging/com.youtube.cobalt.xml
new file mode 100644
index 0000000..4eda34a
--- /dev/null
+++ b/src/starboard/tizen/packaging/com.youtube.cobalt.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="3.0" package="com.youtube.cobalt" version="1.0.0" install-location="internal-only">
+  <label>Cobalt</label>
+  <!-- TODO icon -->
+    <icon>org.tizen.browser.png</icon>
+    <author email="zapati.ahn@samsung.com" href="www.samsung.com">Youngjoon Ahn</author>
+    <description>Cobalt</description>
+    <ui-application  type="capp"  multiple="false" taskmanage="true" nodisplay="false" exec="/usr/apps/com.youtube.cobalt/bin/cobalt" appid="com.youtube.cobalt" process-pool="true">
+      <icon>org.tizen.browser.png</icon>
+      <label>Cobalt</label>
+    </ui-application>
+  <!-- Adding needed privileges as per Tizen 3.0 guide. Some may not be required. Revisit and remove unnecessary ones -->
+  <privileges>
+    <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+    <privilege>http://tizen.org/privilege/appmanager.kill</privilege>
+    <privilege>http://tizen.org/privilege/appmanager.kill.bgapp</privilege>
+    <privilege>http://tizen.org/privilege/network.get</privilege>
+    <privilege>http://tizen.org/privilege/internet</privilege>
+    <privilege>http://tizen.org/privilege/window.priority.set</privilege>
+    <privilege>http://tizen.org/privilege/web-history.admin</privilege>
+    <privilege>http://tizen.org/privilege/screenshot</privilege>
+    <privilege>http://tizen.org/privilege/keygrab</privilege>
+    <privilege>http://tizen.org/privilege/download</privilege>
+    <privilege>http://tizen.org/privilege/bookmark.admin</privilege>
+    <privilege>http://tizen.org/privilege/notification</privilege>
+    <privilege>http://tizen.org/privilege/systemsettings.admin</privilege>
+    <privilege>http://tizen.org/privilege/externalstorage</privilege>
+    <privilege>http://tizen.org/privilege/mediastorage</privilege>
+    <privilege>http://tizen.org/privilege/keymanager</privilege>
+    <privilege>http://tizen.org/privilege/mediahistory.read</privilege>
+    <privilege>http://tizen.org/privilege/recorder</privilege>
+  </privileges>
+</manifest>
diff --git a/src/starboard/tizen/packaging/gbs.conf b/src/starboard/tizen/packaging/gbs.conf
new file mode 100644
index 0000000..39eaa67
--- /dev/null
+++ b/src/starboard/tizen/packaging/gbs.conf
@@ -0,0 +1,27 @@
+[general]
+profile = profile.cobalt
+
+###############################################
+#
+# Tizen v3.0
+#
+[obs.tizen_v3.0]
+url = https://api.tizen.org
+
+###############################################
+#
+# Tizen v3.0 for cobalt
+#
+[profile.cobalt]
+obs = obs.tizen_v3.0
+repos =  repo.local, repo.cobalt_base, repo.cobalt_tizen3.0
+buildroot = ~/GBS-ROOT-COBALT
+
+[repo.local]
+url=~/GBS-ROOT-COBALT/local/repos/cobalt/
+
+[repo.cobalt_base]
+url = http://download.tizen.org/snapshots/tizen/base/latest/repos/arm/packages/
+
+[repo.cobalt_tizen3.0]
+url = http://download.tizen.org/snapshots/tizen/3.0-tv/latest/repos/arm-wayland/packages/
diff --git a/src/starboard/shared/nouser/user_start_sign_in.cc b/src/starboard/tizen/shared/atomic_public.h
similarity index 65%
copy from src/starboard/shared/nouser/user_start_sign_in.cc
copy to src/starboard/tizen/shared/atomic_public.h
index b75f65f..51e1cc1 100644
--- a/src/starboard/shared/nouser/user_start_sign_in.cc
+++ b/src/starboard/tizen/shared/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2016 Samsung Electronics. 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.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/user.h"
+#ifndef STARBOARD_TIZEN_SHARED_ATOMIC_PUBLIC_H_
+#define STARBOARD_TIZEN_SHARED_ATOMIC_PUBLIC_H_
 
-void SbUserStartSignIn() {
-  // Do nothing on this platform.
-}
+#include "starboard/atomic.h"
+
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif  // STARBOARD_TIZEN_SHARED_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/tizen/shared/audio/audio_sink_adaptor.cc b/src/starboard/tizen/shared/audio/audio_sink_adaptor.cc
new file mode 100644
index 0000000..5e06b2f
--- /dev/null
+++ b/src/starboard/tizen/shared/audio/audio_sink_adaptor.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2016 Samsung Electronics Co., Ltd 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/audio_sink.h"
+#include "starboard/log.h"
+#include "starboard/tizen/shared/audio/audio_sink_private.h"
+
+bool SbAudioSinkIsValid(SbAudioSink audio_sink) {
+  if (audio_sink == kSbAudioSinkInvalid) {
+    return false;
+  }
+  return audio_sink->IsValid();
+}
+
+SbAudioSink SbAudioSinkCreate(
+    int channels,
+    int sampling_frequency_hz,
+    SbMediaAudioSampleType audio_sample_type,
+    SbMediaAudioFrameStorageType audio_frame_storage_type,
+    SbAudioSinkFrameBuffers frame_buffers,
+    int frames_per_channel,
+    SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+    SbAudioSinkConsumeFramesFunc consume_frames_func,
+    void* context) {
+  if (channels <= 0 || channels > SbAudioSinkGetMaxChannels()) {
+    SB_LOG(WARNING) << "Invalid audio channels " << channels;
+    return kSbAudioSinkInvalid;
+  }
+
+  if (sampling_frequency_hz <= 0) {
+    SB_LOG(WARNING) << "Invalid audio sampling frequency "
+                    << sampling_frequency_hz;
+    return kSbAudioSinkInvalid;
+  }
+
+  if (!SbAudioSinkIsAudioSampleTypeSupported(audio_sample_type)) {
+    SB_LOG(WARNING) << "Invalid audio sample type " << audio_sample_type;
+    return kSbAudioSinkInvalid;
+  }
+
+  if (!SbAudioSinkIsAudioFrameStorageTypeSupported(audio_frame_storage_type)) {
+    SB_LOG(WARNING) << "Invalid audio frame storage type "
+                    << audio_frame_storage_type;
+    return kSbAudioSinkInvalid;
+  }
+
+  if (frame_buffers == NULL) {
+    SB_LOG(WARNING) << "Pointer to frame buffers cannot be NULL";
+    return kSbAudioSinkInvalid;
+  }
+
+  if (frames_per_channel <= 0) {
+    SB_LOG(WARNING) << "Invalid frame buffer size " << frames_per_channel;
+    return kSbAudioSinkInvalid;
+  }
+
+  if (update_source_status_func == NULL) {
+    SB_LOG(WARNING) << "update_source_status_func cannot be NULL";
+    return kSbAudioSinkInvalid;
+  }
+
+  if (consume_frames_func == NULL) {
+    SB_LOG(WARNING) << "consume_frames_func cannot be NULL";
+    return kSbAudioSinkInvalid;
+  }
+
+  SbAudioSink audio_sink = new SbAudioSinkPrivate(
+      channels, sampling_frequency_hz, audio_sample_type,
+      audio_frame_storage_type, frame_buffers, frames_per_channel,
+      update_source_status_func, consume_frames_func, context);
+  if (!SbAudioSinkIsValid(audio_sink)) {
+    if (audio_sink) {
+      delete audio_sink;
+    }
+    return kSbAudioSinkInvalid;
+  }
+  return audio_sink;
+}
+
+void SbAudioSinkDestroy(SbAudioSink audio_sink) {
+  if (audio_sink) {
+    delete audio_sink;
+  }
+  SB_LOG(WARNING) << "Invalid audio sink.";
+}
+
+int SbAudioSinkGetMaxChannels() {
+  return 2;
+}
+
+int SbAudioSinkGetNearestSupportedSampleFrequency(int sampling_frequency_hz) {
+  if (sampling_frequency_hz <= 0) {
+    SB_LOG(ERROR) << "Invalid audio sampling frequency "
+                  << sampling_frequency_hz;
+    return 1;
+  }
+  return sampling_frequency_hz;
+}
+
+bool SbAudioSinkIsAudioSampleTypeSupported(
+    SbMediaAudioSampleType audio_sample_type) {
+  return audio_sample_type == kSbMediaAudioSampleTypeInt16;
+}
+
+bool SbAudioSinkIsAudioFrameStorageTypeSupported(
+    SbMediaAudioFrameStorageType audio_frame_storage_type) {
+  return audio_frame_storage_type == kSbMediaAudioFrameStorageTypeInterleaved;
+}
diff --git a/src/starboard/tizen/shared/audio/audio_sink_private.cc b/src/starboard/tizen/shared/audio/audio_sink_private.cc
new file mode 100644
index 0000000..4d6440a
--- /dev/null
+++ b/src/starboard/tizen/shared/audio/audio_sink_private.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2016 Samsung Electronics Co., Ltd 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/audio_sink.h"
+#include "starboard/log.h"
+#include "starboard/mutex.h"
+#include "starboard/tizen/shared/audio/audio_sink_private.h"
+
+#define CHECK_CAPI_AUDIO_ERROR(func)                            \
+  if (capi_ret != AUDIO_IO_ERROR_NONE) {                        \
+    SB_DLOG(ERROR) << "[MEDIA] " #func " (" << capi_ret << ", " \
+                   << GetCAPIErrorString(capi_ret) << ")";      \
+    return;                                                     \
+  }
+
+const int kSampleByte = 2;
+
+SbAudioSinkPrivate::SbAudioSinkPrivate(
+    int channels,
+    int sampling_frequency_hz,
+    SbMediaAudioSampleType audio_sample_type,
+    SbMediaAudioFrameStorageType audio_frame_storage_type,
+    SbAudioSinkFrameBuffers frame_buffers,
+    int frames_per_channel,
+    SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+    SbAudioSinkConsumeFramesFunc consume_frames_func,
+    void* context)
+    : channels_(channels),
+      sampling_frequency_hz_(sampling_frequency_hz),
+      audio_sample_type_(audio_sample_type),
+      audio_frame_storage_type_(audio_frame_storage_type),
+      frame_buffers_(frame_buffers),
+      frames_per_channel_(frames_per_channel),
+      update_source_status_func_(update_source_status_func),
+      consume_frames_func_(consume_frames_func),
+      context_(context),
+      destroying_(false),
+      is_paused_(true) {
+  SB_DLOG(INFO) << "[MEDIA] SbAudioSinkPrivate : "
+                << "channels " << channels << ", frequency "
+                << sampling_frequency_hz << ", sample_type "
+                << audio_sample_type << ", storage_type "
+                << audio_frame_storage_type << ", frame_buffers "
+                << static_cast<int>(frame_buffers) << ", frame_buff_sz "
+                << frames_per_channel;
+
+  int capi_ret;
+  capi_ret = audio_out_create_new(sampling_frequency_hz, AUDIO_CHANNEL_STEREO,
+                                  AUDIO_SAMPLE_TYPE_S16_LE,  // kSampleByte = 2
+                                  &capi_audio_out_);
+  CHECK_CAPI_AUDIO_ERROR(audio_out_create_new);
+
+  capi_ret = audio_out_set_interrupted_cb(capi_audio_out_,
+                                          OnCAPIAudioIOInterrupted_CB, this);
+  CHECK_CAPI_AUDIO_ERROR(audio_out_set_interrupted_cb);
+
+  // Starts the thread
+  audio_out_thread_ =
+      SbThreadCreate(0, kSbThreadPriorityRealTime, kSbThreadNoAffinity, true,
+                     "tizen_audio_out", AudioSinkThreadProc_CB, this);
+  SB_DCHECK(SbThreadIsValid(audio_out_thread_));
+}
+
+SbAudioSinkPrivate::~SbAudioSinkPrivate() {
+  // Stop the thread
+  {
+    starboard::ScopedLock lock(mutex_);
+    destroying_ = true;
+  }
+  if (SbThreadIsValid(audio_out_thread_)) {
+    SB_DLOG(INFO) << "[MEDIA] wait for audio sink thread exit";
+    SbThreadJoin(audio_out_thread_, NULL);
+  } else {
+    SB_DLOG(INFO) << "[MEDIA] audio sink thread is invalid. skip waiting";
+  }
+
+  // destroy capi audio
+  if (capi_audio_out_) {
+    int ret = audio_out_destroy(capi_audio_out_);
+    if (ret != AUDIO_IO_ERROR_NONE) {
+      SB_DLOG(ERROR) << "audio_out_destroy failed (" << ret << ")";
+    }
+  }
+}
+
+bool SbAudioSinkPrivate::IsValid() {
+  return SbThreadIsValid(audio_out_thread_);
+}
+
+// static callbacks
+void SbAudioSinkPrivate::OnCAPIAudioIOInterrupted_CB(
+    audio_io_interrupted_code_e code,
+    void* user_data) {
+  SbAudioSinkPrivate* audio_sink =
+      reinterpret_cast<SbAudioSinkPrivate*>(user_data);
+  if (audio_sink) {
+    audio_sink->OnCAPIAudioIOInterrupted(code);
+  }
+}
+void SbAudioSinkPrivate::OnCAPIAudioStreamWrite_CB(
+    audio_out_h handle,
+    size_t nbytes,
+    void* user_data) {  // not used
+  SbAudioSinkPrivate* audio_sink =
+      reinterpret_cast<SbAudioSinkPrivate*>(user_data);
+  if (audio_sink) {
+    audio_sink->OnCAPIAudioStreamWrite(handle, nbytes);
+  }
+}
+void* SbAudioSinkPrivate::AudioSinkThreadProc_CB(void* context) {
+  SbAudioSinkPrivate* audio_sink =
+      reinterpret_cast<SbAudioSinkPrivate*>(context);
+  if (audio_sink) {
+    return audio_sink->AudioSinkThreadProc();
+  }
+  return NULL;
+}
+
+const char* SbAudioSinkPrivate::GetCAPIErrorString(int ret) {
+  // TODO : Get CAPI error and print log
+  return "Unknown";
+}
+
+void SbAudioSinkPrivate::OnCAPIAudioIOInterrupted(
+    audio_io_interrupted_code_e code) {
+  SB_DLOG(WARNING) << "Play interrupted: audio_io_interrupted_code_e : "
+                   << code;
+}
+
+void SbAudioSinkPrivate::OnCAPIAudioStreamWrite(audio_out_h handle,
+                                                size_t nbytes) {
+  SB_DLOG(INFO) << "[MEDIA] OnAudioStreamWrite (not used) - request " << nbytes;
+}
+
+void* SbAudioSinkPrivate::AudioSinkThreadProc() {
+  void* buf;
+  int bytes_to_fill;
+  int bytes_written;
+  int bytes_per_frame = kSampleByte;
+  int consumed_frames;
+
+  SB_DLOG(INFO) << "[MEDIA] sink thread started";
+
+  for (;;) {
+    {
+      starboard::ScopedLock lock(mutex_);
+      if (destroying_) {
+        break;
+      }
+    }
+
+    int frames_in_buffer, offset_in_frames;
+    bool is_playing, is_eos_reached;
+    update_source_status_func_(&frames_in_buffer, &offset_in_frames,
+                               &is_playing, &is_eos_reached, context_);
+
+    if (is_playing) {
+      buf = reinterpret_cast<uint8_t*>(frame_buffers_[0]) +
+            offset_in_frames * bytes_per_frame;
+      if (offset_in_frames + frames_in_buffer <= frames_per_channel_) {
+        bytes_to_fill = frames_in_buffer * bytes_per_frame;
+      } else {
+        bytes_to_fill =
+            (frames_per_channel_ - offset_in_frames) * bytes_per_frame;
+      }
+
+      if (is_paused_) {
+        // audio_out_resume(capi_audio_out_);
+        audio_out_prepare(capi_audio_out_);
+        is_paused_ = false;
+        SB_DLOG(INFO) << "[MEDIA] audio_out_resume";
+      }
+
+      bytes_written = audio_out_write(capi_audio_out_, buf, bytes_to_fill);
+
+      if (bytes_written < 0) {
+        SB_DLOG(ERROR) << "[MEDIA] audio_out_write error (" << bytes_written
+                       << ", " << GetCAPIErrorString(bytes_written) << ")";
+        break;
+      }
+      consumed_frames = bytes_written / bytes_per_frame;
+
+      // SbThreadSleep(consumed_frames * kSbTimeSecond /
+      // sampling_frequency_hz_);
+      consume_frames_func_(consumed_frames, context_);
+    } else {
+      if (!is_paused_) {
+        audio_out_unprepare(capi_audio_out_);
+        is_paused_ = true;
+        SB_DLOG(INFO) << "[MEDIA] audio_out_pause";
+      }
+      // Wait for five millisecond if we are paused.
+      SbThreadSleep(kSbTimeMillisecond * 5);
+    }
+  }
+
+  SB_DLOG(INFO) << "[MEDIA] sink thread exited";
+  return NULL;
+}
diff --git a/src/starboard/tizen/shared/audio/audio_sink_private.h b/src/starboard/tizen/shared/audio/audio_sink_private.h
new file mode 100644
index 0000000..aa05f62
--- /dev/null
+++ b/src/starboard/tizen/shared/audio/audio_sink_private.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_TIZEN_SHARED_AUDIO_AUDIO_SINK_PRIVATE_H_
+#define STARBOARD_TIZEN_SHARED_AUDIO_AUDIO_SINK_PRIVATE_H_
+
+#include <audio_io.h>
+
+#include "starboard/audio_sink.h"
+#include "starboard/mutex.h"
+#include "starboard/thread.h"
+#include "starboard/time.h"
+
+// starboard object
+struct SbAudioSinkPrivate {
+ public:
+  SbAudioSinkPrivate(
+      int channels,
+      int sampling_frequency_hz,
+      SbMediaAudioSampleType audio_sample_type,
+      SbMediaAudioFrameStorageType audio_frame_storage_type,
+      SbAudioSinkFrameBuffers frame_buffers,
+      int frames_per_channel,
+      SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+      SbAudioSinkConsumeFramesFunc consume_frames_func,
+      void* context);
+  virtual ~SbAudioSinkPrivate();
+
+  bool IsValid();
+
+  // callbacks
+  static void OnCAPIAudioIOInterrupted_CB(audio_io_interrupted_code_e code,
+                                          void* user_data);
+  static void OnCAPIAudioStreamWrite_CB(audio_out_h handle,
+                                        size_t nbytes,
+                                        void* user_data);
+  static void* AudioSinkThreadProc_CB(void* context);
+
+ private:
+  const char* GetCAPIErrorString(int capi_ret);
+
+  void OnCAPIAudioIOInterrupted(audio_io_interrupted_code_e code);
+  void OnCAPIAudioStreamWrite(audio_out_h handle, size_t nbytes);
+  void* AudioSinkThreadProc();
+
+  // setting of creation
+  int channels_;
+  int sampling_frequency_hz_;
+  SbMediaAudioSampleType audio_sample_type_;
+  SbMediaAudioFrameStorageType audio_frame_storage_type_;
+  SbAudioSinkFrameBuffers frame_buffers_;
+  int frames_per_channel_;
+  SbAudioSinkUpdateSourceStatusFunc update_source_status_func_;
+  SbAudioSinkConsumeFramesFunc consume_frames_func_;
+  void* context_;
+
+  // thread related
+  bool destroying_;
+  SbThread audio_out_thread_;
+  ::starboard::Mutex mutex_;
+
+  // capi related
+  audio_out_h capi_audio_out_;
+  bool is_paused_;
+};
+
+#endif  // STARBOARD_TIZEN_SHARED_AUDIO_AUDIO_SINK_PRIVATE_H_
diff --git a/src/starboard/tizen/shared/configuration_public.h b/src/starboard/tizen/shared/configuration_public.h
new file mode 100644
index 0000000..16c4066
--- /dev/null
+++ b/src/starboard/tizen/shared/configuration_public.h
@@ -0,0 +1,292 @@
+// Copyright 2016 Samsung Electronics. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The shared Starboard configuration for Raspberry Pi devices.
+
+#ifndef STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
+
+// The API version implemented by this platform.
+#define SB_API_VERSION 1
+
+// --- System Header Configuration -------------------------------------------
+
+// Any system headers listed here that are not provided by the platform will be
+// emulated in starboard/types.h.
+
+// Whether the current platform provides the standard header stdarg.h.
+#define SB_HAS_STDARG_H 1
+
+// Whether the current platform provides the standard header stdbool.h.
+#define SB_HAS_STDBOOL_H 1
+
+// Whether the current platform provides the standard header stddef.h.
+#define SB_HAS_STDDEF_H 1
+
+// Whether the current platform provides the standard header stdint.h.
+#define SB_HAS_STDINT_H 1
+
+// Whether the current platform provides the standard header inttypes.h.
+#define SB_HAS_INTTYPES_H 1
+
+// Whether the current platform provides the standard header wchar.h.
+#define SB_HAS_WCHAR_H 1
+
+// Whether the current platform provides the standard header limits.h.
+#define SB_HAS_LIMITS_H 1
+
+// Whether the current platform provides the standard header float.h.
+#define SB_HAS_FLOAT_H 1
+
+// Type detection for wchar_t.
+#if defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define SB_IS_WCHAR_T_UTF32 1
+#elif defined(__WCHAR_MAX__) && \
+    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+#define SB_IS_WCHAR_T_UTF16 1
+#endif
+
+// Chrome only defines these two if ARMEL or MIPSEL are defined.
+#if defined(__ARMEL__)
+// Chrome has an exclusion for iOS here, we should too when we support iOS.
+#define SB_IS_WCHAR_T_UNSIGNED 1
+#elif defined(__MIPSEL__)
+#define SB_IS_WCHAR_T_SIGNED 1
+#endif
+
+// --- Architecture Configuration --------------------------------------------
+
+// On default Linux desktop, you must be a superuser in order to set real time
+// scheduling on threads.
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+
+// --- Attribute Configuration -----------------------------------------------
+
+// The platform's annotation for forcing a C function to be inlined.
+#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
+
+// The platform's annotation for marking a C function as suggested to be
+// inlined.
+#define SB_C_INLINE inline
+
+// The platform's annotation for marking a C function as forcibly not
+// inlined.
+#define SB_C_NOINLINE __attribute__((noinline))
+
+// The platform's annotation for marking a symbol as exported outside of the
+// current shared library.
+#define SB_EXPORT_PLATFORM __attribute__((visibility("default")))
+
+// The platform's annotation for marking a symbol as imported from outside of
+// the current linking unit.
+#define SB_IMPORT_PLATFORM
+
+// --- Extensions Configuration ----------------------------------------------
+
+// GCC/Clang doesn't define a long long hash function, except for Android and
+// Game consoles.
+#define SB_HAS_LONG_LONG_HASH 0
+
+// GCC/Clang doesn't define a string hash function, except for Game Consoles.
+#define SB_HAS_STRING_HASH 0
+
+// Desktop Linux needs a using statement for the hash functions.
+#define SB_HAS_HASH_USING 0
+
+// Set this to 1 if hash functions for custom types can be defined as a
+// hash_value() function. Otherwise, they need to be placed inside a
+// partially-specified hash struct template with an operator().
+#define SB_HAS_HASH_VALUE 0
+
+// Set this to 1 if use of hash_map or hash_set causes a deprecation warning
+// (which then breaks the build).
+#define SB_HAS_HASH_WARNING 1
+
+// The location to include hash_map on this platform.
+#define SB_HASH_MAP_INCLUDE <ext/hash_map>
+
+// C++'s hash_map and hash_set are often found in different namespaces depending
+// on the compiler.
+#define SB_HASH_NAMESPACE __gnu_cxx
+
+// The location to include hash_set on this platform.
+#define SB_HASH_SET_INCLUDE <ext/hash_set>
+
+// Define this to how this platform copies varargs blocks.
+#define SB_VA_COPY(dest, source) va_copy(dest, source)
+
+// --- Filesystem Configuration ----------------------------------------------
+
+// The current platform's maximum length of the name of a single directory
+// entry, not including the absolute path.
+#define SB_FILE_MAX_NAME 64
+
+// The current platform's maximum length of an absolute path.
+#define SB_FILE_MAX_PATH 4096
+
+// The current platform's maximum number of files that can be opened at the
+// same time by one process.
+#define SB_FILE_MAX_OPEN 256
+
+// The current platform's file path component separator character. This is the
+// character that appears after a directory in a file path. For example, the
+// absolute canonical path of the file "/path/to/a/file.txt" uses '/' as a path
+// component separator character.
+#define SB_FILE_SEP_CHAR '/'
+
+// The current platform's alternate file path component separator character.
+// This is like SB_FILE_SEP_CHAR, except if your platform supports an alternate
+// character, then you can place that here. For example, on windows machines,
+// the primary separator character is probably '\', but the alternate is '/'.
+#define SB_FILE_ALT_SEP_CHAR '/'
+
+// The current platform's search path component separator character. When
+// specifying an ordered list of absolute paths of directories to search for a
+// given reason, this is the character that appears between entries. For
+// example, the search path of "/etc/search/first:/etc/search/second" uses ':'
+// as a search path component separator character.
+#define SB_PATH_SEP_CHAR ':'
+
+// The string form of SB_FILE_SEP_CHAR.
+#define SB_FILE_SEP_STRING "/"
+
+// The string form of SB_FILE_ALT_SEP_CHAR.
+#define SB_FILE_ALT_SEP_STRING "/"
+
+// The string form of SB_PATH_SEP_CHAR.
+#define SB_PATH_SEP_STRING ":"
+
+// Whether this platform supports symbolic links.
+#define SB_HAS_SYMBOLIC_LINKS 1
+
+// --- Memory Configuration --------------------------------------------------
+
+// The memory page size, which controls the size of chunks on memory that
+// allocators deal with, and the alignment of those chunks. This doesn't have to
+// be the hardware-defined physical page size, but it should be a multiple of
+// it.
+#define SB_MEMORY_PAGE_SIZE 4096
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#define SB_HAS_MMAP 1
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// Whether this platform has and should use an growable heap (e.g. with sbrk())
+// to map physical memory to the virtual address space.
+#define SB_HAS_VIRTUAL_REGIONS 0
+
+// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
+// may require buffers to have a specific alignment, and this is the place to
+// specify that.
+#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
+
+// Determines the alignment that allocations should have on this platform.
+#define SB_MALLOC_ALIGNMENT ((size_t)16U)
+
+// Determines the threshhold of allocation size that should be done with mmap
+// (if available), rather than allocated within the core heap.
+#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
+
+// Defines the path where memory debugging logs should be written to.
+#define SB_MEMORY_LOG_PATH "/tmp/starboard"
+
+// --- Thread Configuration --------------------------------------------------
+
+// Defines the maximum number of simultaneous threads for this platform. Some
+// platforms require sharing thread handles with other kinds of system handles,
+// like mutexes, so we want to keep this managable.
+#define SB_MAX_THREADS 90
+
+// The maximum number of thread local storage keys supported by this platform.
+#define SB_MAX_THREAD_LOCAL_KEYS 512
+
+// The maximum length of the name for a thread, including the NULL-terminator.
+#define SB_MAX_THREAD_NAME_LENGTH 16;
+
+// --- Graphics Configuration ------------------------------------------------
+
+// Specifies whether this platform supports a performant accelerated blitter
+// API. The basic requirement is a scaled, clipped, alpha-blended blit.
+#define SB_HAS_BLITTER 0
+
+// Specifies the preferred byte order of color channels in a pixel. Refer to
+// starboard/configuration.h for the possible values. EGL/GLES platforms should
+// generally prefer a byte order of RGBA, regardless of endianness.
+#define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_RGBA
+
+// Indicates whether or not the given platform supports bilinear filtering.
+// This can be checked to enable/disable renderer tests that verify that this is
+// working properly.
+#define SB_HAS_BILINEAR_FILTERING_SUPPORT 1
+
+// Indicates whether or not the given platform supports rendering of NV12
+// textures. These textures typically originate from video decoders.
+#define SB_HAS_NV12_TEXTURE_SUPPORT 0
+
+// Whether the current platform should frequently flip their display buffer.
+// If this is not required (e.g. SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER is set
+// to 0), then optimizations where the display buffer is not flipped if the
+// scene hasn't changed are enabled.
+#define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER 0
+
+// --- Network Configuration -------------------------------------------------
+
+// Specifies whether this platform supports IPV6.
+#define SB_HAS_IPV6 1
+
+// Specifies whether this platform supports pipe.
+#define SB_HAS_PIPE 1
+
+// --- Tuneable Parameters ---------------------------------------------------
+
+// Specifies the network receive buffer size in bytes, set via
+// SbSocketSetReceiveBufferSize().
+//
+// Setting this to 0 indicates that SbSocketSetReceiveBufferSize() should
+// not be called. Use this for OSs (such as Linux) where receive buffer
+// auto-tuning is better.
+//
+// On some platforms, this may affect max TCP window size which may
+// dramatically affect throughput in the presence of latency.
+//
+// If your platform does not have a good TCP auto-tuning mechanism,
+// a setting of (128 * 1024) here is recommended.
+#define SB_NETWORK_RECEIVE_BUFFER_SIZE (0)
+
+// --- User Configuration ----------------------------------------------------
+
+// The maximum number of users that can be signed in at the same time.
+#define SB_USER_MAX_SIGNED_IN 1
+
+// --- Platform Specific Audits ----------------------------------------------
+#define SB_IS_TIZEN_OS 1
+
+// Tizen uses system icu package.
+// undefined reference to 'icu_46::TimeZone::createDefault()'
+#define SB_HAS_QUIRK_NO_TIMEZONE_NAME_SUPPORT 1
+
+// Tizen TV product possible to use wayland display video window
+// instead of Evas_Object
+#define SB_CAN_USE_WAYLAND_VIDEO_WINDOW 0
+
+#if !defined(__GNUC__)
+#error "Tizen builds need a GCC-like compiler (for the moment)."
+#endif
+
+#endif  // STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
new file mode 100644
index 0000000..dec844c
--- /dev/null
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -0,0 +1,179 @@
+// 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/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h"
+
+#include "starboard/audio_sink.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
+                           const SbMediaAudioHeader& audio_header)
+    : sample_type_(kSbMediaAudioSampleTypeInt16),
+      codec_context_(NULL),
+      av_frame_(NULL),
+      stream_ended_(false),
+      audio_header_(audio_header) {
+  SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
+
+  InitializeCodec();
+}
+
+AudioDecoder::~AudioDecoder() {
+  TeardownCodec();
+}
+
+void AudioDecoder::Decode(const InputBuffer& input_buffer) {
+  SB_CHECK(codec_context_ != NULL);
+
+  if (stream_ended_) {
+    SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
+    return;
+  }
+
+  AVPacket packet;
+  av_init_packet(&packet);
+  packet.data = const_cast<uint8_t*>(input_buffer.data());
+  packet.size = input_buffer.size();
+
+  avcodec_get_frame_defaults(av_frame_);
+  int frame_decoded = 0;
+  int result =
+      avcodec_decode_audio4(codec_context_, av_frame_, &frame_decoded, &packet);
+  if (result != input_buffer.size() || frame_decoded != 1) {
+    // TODO: Consider fill it with silence.
+    SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result
+                     << " with input buffer size: " << input_buffer.size()
+                     << " and frame decoded: " << frame_decoded;
+    return;
+  }
+
+  int decoded_audio_size = av_samples_get_buffer_size(
+      NULL, codec_context_->channels, av_frame_->nb_samples,
+      codec_context_->sample_fmt, 1);
+  audio_header_.samples_per_second = codec_context_->sample_rate;
+
+  if (decoded_audio_size > 0) {
+    scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+        input_buffer.pts(),
+        codec_context_->channels * av_frame_->nb_samples *
+            (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
+    SbMemoryCopy(decoded_audio->buffer(), av_frame_->extended_data,
+                 decoded_audio->size());
+    decoded_audios_.push(decoded_audio);
+  } else {
+    // TODO: Consider fill it with silence.
+    SB_LOG(ERROR) << "Decoded audio frame is empty.";
+  }
+}
+
+void AudioDecoder::WriteEndOfStream() {
+  // AAC has no dependent frames so we needn't flush the decoder.  Set the flag
+  // to ensure that Decode() is not called when the stream is ended.
+  stream_ended_ = true;
+  // Put EOS into the queue.
+  decoded_audios_.push(new DecodedAudio);
+}
+
+scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
+  scoped_refptr<DecodedAudio> result;
+  if (!decoded_audios_.empty()) {
+    result = decoded_audios_.front();
+    decoded_audios_.pop();
+  }
+  return result;
+}
+
+void AudioDecoder::Reset() {
+  stream_ended_ = false;
+  while (!decoded_audios_.empty()) {
+    decoded_audios_.pop();
+  }
+}
+
+SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
+  return sample_type_;
+}
+
+int AudioDecoder::GetSamplesPerSecond() const {
+  return audio_header_.samples_per_second;
+}
+
+void AudioDecoder::InitializeCodec() {
+  InitializeFfmpeg();
+
+  codec_context_ = avcodec_alloc_context3(NULL);
+
+  if (codec_context_ == NULL) {
+    SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
+    return;
+  }
+
+  codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
+  codec_context_->codec_id = AV_CODEC_ID_AAC;
+  // Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
+  if (sample_type_ == kSbMediaAudioSampleTypeInt16) {
+    codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
+  } else {
+    codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT;
+  }
+
+  codec_context_->channels = audio_header_.number_of_channels;
+  codec_context_->sample_rate = audio_header_.samples_per_second;
+
+  codec_context_->extradata = NULL;
+  codec_context_->extradata_size = 0;
+
+  AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
+
+  if (codec == NULL) {
+    SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
+    TeardownCodec();
+    return;
+  }
+
+  int rv = avcodec_open2(codec_context_, codec, NULL);
+  if (rv < 0) {
+    SB_LOG(ERROR) << "Unable to open codec";
+    TeardownCodec();
+    return;
+  }
+
+  av_frame_ = avcodec_alloc_frame();
+  if (av_frame_ == NULL) {
+    SB_LOG(ERROR) << "Unable to allocate audio frame";
+    TeardownCodec();
+  }
+}
+
+void AudioDecoder::TeardownCodec() {
+  if (codec_context_) {
+    avcodec_close(codec_context_);
+    av_free(codec_context_);
+    codec_context_ = NULL;
+  }
+  if (av_frame_) {
+    av_free(av_frame_);
+    av_frame_ = NULL;
+  }
+}
+
+}  // namespace ffmpeg
+
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
new file mode 100644
index 0000000..cd88d46
--- /dev/null
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -0,0 +1,65 @@
+// 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 STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
+#define STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
+
+#include <queue>
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/tizen/shared/ffmpeg/ffmpeg_common.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+class AudioDecoder : public starboard::player::filter::AudioDecoder {
+ public:
+  typedef starboard::player::DecodedAudio DecodedAudio;
+  typedef starboard::player::InputBuffer InputBuffer;
+
+  AudioDecoder(SbMediaAudioCodec audio_codec,
+               const SbMediaAudioHeader& audio_header);
+  ~AudioDecoder() SB_OVERRIDE;
+
+  void Decode(const InputBuffer& input_buffer) SB_OVERRIDE;
+  void WriteEndOfStream() SB_OVERRIDE;
+  scoped_refptr<DecodedAudio> Read() SB_OVERRIDE;
+  void Reset() SB_OVERRIDE;
+  SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE;
+  int GetSamplesPerSecond() const SB_OVERRIDE;
+
+  bool is_valid() const { return codec_context_ != NULL; }
+
+ private:
+  void InitializeCodec();
+  void TeardownCodec();
+
+  SbMediaAudioSampleType sample_type_;
+  AVCodecContext* codec_context_;
+  AVFrame* av_frame_;
+
+  bool stream_ended_;
+  std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
+  SbMediaAudioHeader audio_header_;
+};
+
+}  // namespace ffmpeg
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc
new file mode 100644
index 0000000..3667141
--- /dev/null
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc
@@ -0,0 +1,37 @@
+// 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/tizen/shared/ffmpeg/ffmpeg_common.h"
+
+#include "starboard/log.h"
+#include "starboard/once.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+namespace {
+
+SbOnceControl ffmpeg_initialization_once = SB_ONCE_INITIALIZER;
+
+}  // namespace
+
+void InitializeFfmpeg() {
+  bool initialized = SbOnce(&ffmpeg_initialization_once, av_register_all);
+  SB_DCHECK(initialized);
+}
+
+}  // namespace ffmpeg
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h
new file mode 100644
index 0000000..3bd1ea9
--- /dev/null
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h
@@ -0,0 +1,39 @@
+// 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 STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_COMMON_H_
+#define STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_COMMON_H_
+
+// Include FFmpeg header files.
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/avutil.h>
+#include <libavutil/imgutils.h>
+#include <libavutil/opt.h>
+}  // extern "C"
+
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+void InitializeFfmpeg();
+
+}  // namespace ffmpeg
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_COMMON_H_
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
new file mode 100644
index 0000000..effc33e
--- /dev/null
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -0,0 +1,317 @@
+// 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/tizen/shared/ffmpeg/ffmpeg_video_decoder.h"
+
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+namespace {
+
+// FFmpeg requires its decoding buffers to align with platform alignment.  It
+// mentions inside
+// http://ffmpeg.org/doxygen/trunk/structAVFrame.html#aa52bfc6605f6a3059a0c3226cc0f6567
+// that the alignment on most modern desktop systems are 16 or 32.
+static const int kAlignment = 32;
+
+size_t AlignUp(size_t size, int alignment) {
+  SB_DCHECK((alignment & (alignment - 1)) == 0);
+  return (size + alignment - 1) & ~(alignment - 1);
+}
+
+size_t GetYV12SizeInBytes(int32_t width, int32_t height) {
+  return width * height * 3 / 2;
+}
+
+int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame) {
+  if (codec_context->pix_fmt != PIX_FMT_YUV420P &&
+      codec_context->pix_fmt != PIX_FMT_YUVJ420P) {
+    SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt;
+    return AVERROR(EINVAL);
+  }
+
+  int ret =
+      av_image_check_size(codec_context->width, codec_context->height, 0, NULL);
+  if (ret < 0) {
+    return ret;
+  }
+
+  // Align to kAlignment * 2 as we will divide y_stride by 2 for u and v planes
+  size_t y_stride = AlignUp(codec_context->width, kAlignment * 2);
+  size_t uv_stride = y_stride / 2;
+  size_t aligned_height = AlignUp(codec_context->height, kAlignment * 2);
+  uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned(
+      kAlignment, GetYV12SizeInBytes(y_stride, aligned_height)));
+
+  // y plane
+  frame->base[0] = frame_buffer;
+  frame->data[0] = frame->base[0];
+  frame->linesize[0] = y_stride;
+  // u plane
+  frame->base[1] = frame_buffer + y_stride * aligned_height;
+  frame->data[1] = frame->base[1];
+  frame->linesize[1] = uv_stride;
+  // v plane
+  frame->base[2] = frame->base[1] + uv_stride * aligned_height / 2;
+  frame->data[2] = frame->base[2];
+  frame->linesize[2] = uv_stride;
+
+  frame->opaque = frame_buffer;
+  frame->type = FF_BUFFER_TYPE_USER;
+  frame->pkt_pts =
+      codec_context->pkt ? codec_context->pkt->pts : AV_NOPTS_VALUE;
+  frame->width = codec_context->width;
+  frame->height = codec_context->height;
+  frame->format = codec_context->pix_fmt;
+
+  frame->reordered_opaque = codec_context->reordered_opaque;
+
+  return 0;
+}
+
+void ReleaseBuffer(AVCodecContext*, AVFrame* frame) {
+  SbMemoryDeallocate(frame->opaque);
+  frame->opaque = NULL;
+
+  // The FFmpeg API expects us to zero the data pointers in this callback.
+  SbMemorySet(frame->data, 0, sizeof(frame->data));
+}
+
+}  // namespace
+
+VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec)
+    : video_codec_(video_codec),
+      host_(NULL),
+      codec_context_(NULL),
+      av_frame_(NULL),
+      stream_ended_(false),
+      error_occured_(false),
+      decoder_thread_(kSbThreadInvalid) {
+  InitializeCodec();
+}
+
+VideoDecoder::~VideoDecoder() {
+  Reset();
+  TeardownCodec();
+}
+
+void VideoDecoder::SetHost(Host* host) {
+  SB_DCHECK(host != NULL);
+  SB_DCHECK(host_ == NULL);
+  host_ = host;
+}
+
+void VideoDecoder::WriteInputBuffer(const InputBuffer& input_buffer) {
+  SB_DCHECK(queue_.Poll().type == kInvalid);
+  SB_DCHECK(host_ != NULL);
+
+  if (stream_ended_) {
+    SB_LOG(ERROR) << "WriteInputFrame() was called after WriteEndOfStream().";
+    return;
+  }
+
+  if (!SbThreadIsValid(decoder_thread_)) {
+    decoder_thread_ =
+        SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
+                       "ff_video_dec", &VideoDecoder::ThreadEntryPoint, this);
+    SB_DCHECK(SbThreadIsValid(decoder_thread_));
+  }
+
+  queue_.Put(Event(input_buffer));
+}
+
+void VideoDecoder::WriteEndOfStream() {
+  SB_DCHECK(host_ != NULL);
+
+  // We have to flush the decoder to decode the rest frames and to ensure that
+  // Decode() is not called when the stream is ended.
+  stream_ended_ = true;
+  queue_.Put(Event(kWriteEndOfStream));
+}
+
+void VideoDecoder::Reset() {
+  // Join the thread to ensure that all callbacks in process are finished.
+  if (SbThreadIsValid(decoder_thread_)) {
+    queue_.Put(Event(kReset));
+    SbThreadJoin(decoder_thread_, NULL);
+  }
+
+  if (codec_context_ != NULL) {
+    avcodec_flush_buffers(codec_context_);
+  }
+
+  decoder_thread_ = kSbThreadInvalid;
+  stream_ended_ = false;
+}
+
+// static
+void* VideoDecoder::ThreadEntryPoint(void* context) {
+  SB_DCHECK(context);
+  VideoDecoder* decoder = reinterpret_cast<VideoDecoder*>(context);
+  decoder->DecoderThreadFunc();
+  return NULL;
+}
+
+void VideoDecoder::DecoderThreadFunc() {
+  for (;;) {
+    Event event = queue_.Get();
+    if (event.type == kReset) {
+      return;
+    }
+    if (error_occured_) {
+      continue;
+    }
+    if (event.type == kWriteInputBuffer) {
+      // Send |input_buffer| to ffmpeg and try to decode one frame.
+      AVPacket packet;
+      av_init_packet(&packet);
+      packet.data = const_cast<uint8_t*>(event.input_buffer.data());
+      packet.size = event.input_buffer.size();
+      packet.pts = event.input_buffer.pts();
+      codec_context_->reordered_opaque = packet.pts;
+
+      DecodePacket(&packet);
+      host_->OnDecoderStatusUpdate(kNeedMoreInput, NULL);
+    } else {
+      SB_DCHECK(event.type == kWriteEndOfStream);
+      // Stream has ended, try to decode any frames left in ffmpeg.
+      AVPacket packet;
+      do {
+        av_init_packet(&packet);
+        packet.data = NULL;
+        packet.size = 0;
+        packet.pts = 0;
+      } while (DecodePacket(&packet));
+
+      host_->OnDecoderStatusUpdate(kBufferFull, VideoFrame::CreateEOSFrame());
+    }
+  }
+}
+
+bool VideoDecoder::DecodePacket(AVPacket* packet) {
+  SB_DCHECK(packet != NULL);
+
+  avcodec_get_frame_defaults(av_frame_);
+  int frame_decoded = 0;
+  int result =
+      avcodec_decode_video2(codec_context_, av_frame_, &frame_decoded, packet);
+  if (frame_decoded == 0) {
+    return false;
+  }
+
+  if (av_frame_->opaque == NULL) {
+    SB_DLOG(ERROR) << "Video frame was produced yet has invalid frame data.";
+    host_->OnDecoderStatusUpdate(kFatalError, NULL);
+    error_occured_ = true;
+    return false;
+  }
+
+  int pitch = AlignUp(av_frame_->width, kAlignment * 2);
+
+  scoped_refptr<VideoFrame> frame = VideoFrame::CreateYV12Frame(
+      av_frame_->width, av_frame_->height, pitch,
+      codec_context_->reordered_opaque, av_frame_->data[0], av_frame_->data[1],
+      av_frame_->data[2]);
+  host_->OnDecoderStatusUpdate(kBufferFull, frame);
+  return true;
+}
+
+void VideoDecoder::InitializeCodec() {
+  InitializeFfmpeg();
+
+  codec_context_ = avcodec_alloc_context3(NULL);
+
+  if (codec_context_ == NULL) {
+    SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
+    return;
+  }
+
+  codec_context_->codec_type = AVMEDIA_TYPE_VIDEO;
+  codec_context_->codec_id = AV_CODEC_ID_H264;
+  codec_context_->profile = FF_PROFILE_UNKNOWN;
+  codec_context_->coded_width = 0;
+  codec_context_->coded_height = 0;
+  codec_context_->pix_fmt = PIX_FMT_NONE;
+
+  codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
+  codec_context_->thread_count = 2;
+  codec_context_->opaque = this;
+  codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
+  codec_context_->get_buffer = AllocateBuffer;
+  codec_context_->release_buffer = ReleaseBuffer;
+
+  codec_context_->extradata = NULL;
+  codec_context_->extradata_size = 0;
+
+  AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
+
+  if (codec == NULL) {
+    SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
+    TeardownCodec();
+    return;
+  }
+
+  int rv = avcodec_open2(codec_context_, codec, NULL);
+  if (rv < 0) {
+    SB_LOG(ERROR) << "Unable to open codec";
+    TeardownCodec();
+    return;
+  }
+
+  av_frame_ = avcodec_alloc_frame();
+  if (av_frame_ == NULL) {
+    SB_LOG(ERROR) << "Unable to allocate audio frame";
+    TeardownCodec();
+  }
+}
+
+void VideoDecoder::TeardownCodec() {
+  if (codec_context_) {
+    avcodec_close(codec_context_);
+    av_free(codec_context_);
+    codec_context_ = NULL;
+  }
+  if (av_frame_) {
+    av_free(av_frame_);
+    av_frame_ = NULL;
+  }
+}
+
+}  // namespace ffmpeg
+
+namespace starboard {
+namespace player {
+namespace filter {
+
+#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+// static
+bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
+                                       SbMediaVideoCodec codec,
+                                       SbDrmSystem drm_system) {
+  SB_UNREFERENCED_PARAMETER(codec);
+  SB_UNREFERENCED_PARAMETER(drm_system);
+
+  return output_mode == kSbPlayerOutputModePunchOut;
+}
+#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
new file mode 100644
index 0000000..09ad253
--- /dev/null
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -0,0 +1,98 @@
+// 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 STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
+#define STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
+
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/queue.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/video_frame_internal.h"
+#include "starboard/thread.h"
+#include "starboard/tizen/shared/ffmpeg/ffmpeg_common.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+class VideoDecoder : public starboard::player::filter::VideoDecoder {
+ public:
+  typedef starboard::player::InputBuffer InputBuffer;
+  typedef starboard::player::VideoFrame VideoFrame;
+
+  explicit VideoDecoder(SbMediaVideoCodec video_codec);
+  ~VideoDecoder() SB_OVERRIDE;
+
+  void SetHost(Host* host) SB_OVERRIDE;
+  void WriteInputBuffer(const InputBuffer& input_buffer) SB_OVERRIDE;
+  void WriteEndOfStream() SB_OVERRIDE;
+  void Reset() SB_OVERRIDE;
+
+  bool is_valid() const { return codec_context_ != NULL; }
+
+ private:
+  enum EventType {
+    kInvalid,
+    kReset,
+    kWriteInputBuffer,
+    kWriteEndOfStream,
+  };
+
+  struct Event {
+    EventType type;
+    // |input_buffer| is only used when |type| is kWriteInputBuffer.
+    InputBuffer input_buffer;
+
+    explicit Event(EventType type = kInvalid) : type(type) {
+      SB_DCHECK(type != kWriteInputBuffer);
+    }
+
+    explicit Event(InputBuffer input_buffer)
+        : type(kWriteInputBuffer), input_buffer(input_buffer) {}
+  };
+
+  static void* ThreadEntryPoint(void* context);
+  void DecoderThreadFunc();
+
+  bool DecodePacket(AVPacket* packet);
+  void InitializeCodec();
+  void TeardownCodec();
+
+  // These variables will be initialized inside ctor or SetHost() and will not
+  // be changed during the life time of this class.
+  const SbMediaVideoCodec video_codec_;
+  Host* host_;
+
+  Queue<Event> queue_;
+
+  // The AV related classes will only be created and accessed on the decoder
+  // thread.
+  AVCodecContext* codec_context_;
+  AVFrame* av_frame_;
+
+  bool stream_ended_;
+  bool error_occured_;
+
+  // Working thread to avoid lengthy decoding work block the player thread.
+  SbThread decoder_thread_;
+};
+
+}  // namespace ffmpeg
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
diff --git a/src/starboard/tizen/shared/get_home_directory.cc b/src/starboard/tizen/shared/get_home_directory.cc
new file mode 100644
index 0000000..fd5c728
--- /dev/null
+++ b/src/starboard/tizen/shared/get_home_directory.cc
@@ -0,0 +1,44 @@
+// 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 <app_common.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/nouser/user_internal.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace nouser {
+
+bool GetHomeDirectory(SbUser user, char* out_path, int path_size) {
+  if (user != SbUserGetCurrent()) {
+    return false;
+  }
+
+  const char* home_directory = app_get_data_path();
+
+  if (!home_directory) {
+    SB_DLOG(WARNING) << "No HOME environment variable.";
+    return false;
+  }
+
+  SB_DLOG(INFO) << "Tizen Home Directory[" << home_directory << "]";
+  SbStringCopy(out_path, home_directory, path_size);
+  return true;
+}
+
+}  // namespace nouser
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/tizen/shared/log/log.cc b/src/starboard/tizen/shared/log/log.cc
new file mode 100644
index 0000000..0bc3243
--- /dev/null
+++ b/src/starboard/tizen/shared/log/log.cc
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2016 Samsung Electronics Co., Ltd 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/log.h"
+
+#include <dlog/dlog.h>
+#include <string.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "COBALT"
+
+void SbLog(SbLogPriority priority, const char* message) {
+  SB_UNREFERENCED_PARAMETER(priority);
+  log_priority dlog_priority = DLOG_UNKNOWN;
+
+  switch (priority) {
+    case kSbLogPriorityInfo:
+      dlog_priority = DLOG_INFO;
+      break;
+    case kSbLogPriorityWarning:
+      dlog_priority = DLOG_WARN;
+      break;
+    case kSbLogPriorityError:
+      dlog_priority = DLOG_ERROR;
+      break;
+    case kSbLogPriorityFatal:
+      dlog_priority = DLOG_FATAL;
+      break;
+    default:
+      break;
+  }
+
+  dlog_print(dlog_priority, LOG_TAG, "%s", message);
+}
diff --git a/src/starboard/tizen/shared/main.cc b/src/starboard/tizen/shared/main.cc
new file mode 100644
index 0000000..0f658fa
--- /dev/null
+++ b/src/starboard/tizen/shared/main.cc
@@ -0,0 +1,81 @@
+// Copyright 2016 Samsung Electronics. 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