Merge "override EGL_DEFAULT_DISPLAY for wayland"
diff --git a/src/base/base.gyp b/src/base/base.gyp
index 47f7437..23ef689 100644
--- a/src/base/base.gyp
+++ b/src/base/base.gyp
@@ -381,7 +381,7 @@
       'variables': {
         'executable_name': 'base_unittests',
       },
-      'includes': [ '../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/base/json/json_parser.cc b/src/base/json/json_parser.cc
index 54433c1..f7533ea 100644
--- a/src/base/json/json_parser.cc
+++ b/src/base/json/json_parser.cc
@@ -186,6 +186,9 @@
 
 }  // namespace
 
+// This is U+FFFD.
+const char kUnicodeReplacementString[] = "\xEF\xBF\xBD";
+
 JSONParser::JSONParser(int options)
     : options_(options),
       start_pos_(NULL),
@@ -612,11 +615,18 @@
   int32 next_char = 0;
 
   while (CanConsume(1)) {
+    int start_index = index_;
     pos_ = start_pos_ + index_;  // CBU8_NEXT is postcrement.
     CBU8_NEXT(start_pos_, index_, length, next_char);
     if (next_char < 0 || !IsValidCharacter(next_char)) {
-      ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
-      return false;
+      if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
+        ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
+        return false;
+      }
+      CBU8_NEXT(start_pos_, start_index, length, next_char);
+      string.Convert();
+      string.AppendString(kUnicodeReplacementString);
+      continue;
     }
 
     // If this character is an escape sequence...
diff --git a/src/base/json/json_parser.h b/src/base/json/json_parser.h
index 020ac25..59ab0b8 100644
--- a/src/base/json/json_parser.h
+++ b/src/base/json/json_parser.h
@@ -261,10 +261,14 @@
   FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals);
   FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers);
   FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages);
+  FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidCharacters);
 
   DISALLOW_COPY_AND_ASSIGN(JSONParser);
 };
 
+// Used when decoding and an invalid utf-8 sequence is encountered.
+BASE_EXPORT extern const char kUnicodeReplacementString[];
+
 }  // namespace internal
 }  // namespace base
 
diff --git a/src/base/json/json_parser_unittest.cc b/src/base/json/json_parser_unittest.cc
index 8ee886b..b34e462 100644
--- a/src/base/json/json_parser_unittest.cc
+++ b/src/base/json/json_parser_unittest.cc
@@ -14,8 +14,9 @@
 
 class JSONParserTest : public testing::Test {
  public:
-  JSONParser* NewTestParser(const std::string& input) {
-    JSONParser* parser = new JSONParser(JSON_PARSE_RFC);
+  JSONParser* NewTestParser(const std::string& input,
+                            int options = JSON_PARSE_RFC) {
+    JSONParser* parser = new JSONParser(options);
     parser->start_pos_ = input.data();
     parser->pos_ = parser->start_pos_;
     parser->end_pos_ = parser->start_pos_ + input.length();
@@ -302,5 +303,18 @@
   EXPECT_TRUE(root.get()) << error_message;
 }
 
+// Verifies invalid utf-8 characters are replaced.
+TEST_F(JSONParserTest, ReplaceInvalidCharacters) {
+  const std::string bogus_char = "󿿿";
+  const std::string quoted_bogus_char = "\"" + bogus_char + "\"";
+  scoped_ptr<JSONParser> parser(
+      NewTestParser(quoted_bogus_char, JSON_REPLACE_INVALID_CHARACTERS));
+  scoped_ptr<Value> value(parser->ConsumeString());
+  ASSERT_TRUE(value.get());
+  std::string str;
+  EXPECT_TRUE(value->GetAsString(&str));
+  EXPECT_EQ(kUnicodeReplacementString, str);
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/src/base/json/json_reader.h b/src/base/json/json_reader.h
index 86b2612..a128bb8 100644
--- a/src/base/json/json_reader.h
+++ b/src/base/json/json_reader.h
@@ -58,6 +58,11 @@
   // if the child is Remove()d from root, it would result in use-after-free
   // unless it is DeepCopy()ed or this option is used.
   JSON_DETACHABLE_CHILDREN = 1 << 1,
+
+  // If set the parser replaces invalid characters with the Unicode replacement
+  // character (U+FFFD). If not set, invalid characters trigger a hard error
+  // and parsing fails.
+  JSON_REPLACE_INVALID_CHARACTERS = 1 << 2,
 };
 
 class BASE_EXPORT JSONReader {
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index b6ba91c..f300b74 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -3,6 +3,22 @@
 This document records all notable changes made to Cobalt since the last release.
 
 ## Version 16
+ - **Move ``javascript_engine`` and ``cobalt_enable_jit`` build variables**
+
+   Move gyp variables ``javascript_engine`` and ``cobalt_enable_jit``, which
+   were previously defined in ``cobalt_configuration.gypi``, to
+   ``$PLATFORM/gyp_configuration.py``.  This was done in order to work around
+   bindings gyp files' complex usage of gyp variables, which prevented us from
+   having a default JavaScript engine at the gyp variable level.  Now, platforms
+   will by default use the JavaScript engine selected by
+   ``starboard/build/platform_configuration.py``, and can override it by
+   providing a different one in their ``GetVariables`` implementation.  See the
+   ``linux-x64x11-mozjs`` platform for an override example.
+
+   **IMPORTANT**: While existing gyp files that define ``javascript_engine`` and
+   ``cobalt_enable_jit`` may continue to work by chance, it is *strongly*
+   preferred to move all declarations of these variables to python instead.
+
  - **Move test data**
 
    Static test data is now copied to `content/data/test` instead of
@@ -61,6 +77,10 @@
    of an array of MediaDeviceInfo objects, each partially implemented to have
    valid `label` and `kind` attributes.
 
+- **Improvements and Bug Fixes**
+  - Fix for pseudo elements not visually updating when their CSS is modified
+    (e.g. by switching their classes).
+
 ## Version 14
  - **Add support for document.hasFocus()**
 
diff --git a/src/cobalt/accessibility/accessibility_test.gyp b/src/cobalt/accessibility/accessibility_test.gyp
index 3da8086..16ab6f4 100644
--- a/src/cobalt/accessibility/accessibility_test.gyp
+++ b/src/cobalt/accessibility/accessibility_test.gyp
@@ -51,7 +51,7 @@
       'variables': {
         'executable_name': 'accessibility_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ]
 }
diff --git a/src/cobalt/audio/audio.gyp b/src/cobalt/audio/audio.gyp
index f7065ff..60bdcf6 100644
--- a/src/cobalt/audio/audio.gyp
+++ b/src/cobalt/audio/audio.gyp
@@ -86,7 +86,7 @@
       'variables': {
         'executable_name': 'audio_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 3e68fe5..8404a2e 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -60,7 +60,11 @@
   static void UpdateSourceStatusFunc(int* frames_in_buffer,
                                      int* offset_in_frames, bool* is_playing,
                                      bool* is_eos_reached, void* context);
-  static void ConsumeFramesFunc(int frames_consumed, void* context);
+  static void ConsumeFramesFunc(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                void* context);
 
   void UpdateSourceStatus(int* frames_in_buffer, int* offset_in_frames,
                           bool* is_playing, bool* is_eos_reached);
@@ -150,7 +154,15 @@
 }
 
 // static
-void AudioDevice::Impl::ConsumeFramesFunc(int frames_consumed, void* context) {
+void AudioDevice::Impl::ConsumeFramesFunc(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                          SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                          void* context) {
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  UNREFERENCED_PARAMETER(frames_consumed_at);
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+
   AudioDevice::Impl* impl = reinterpret_cast<AudioDevice::Impl*>(context);
   DCHECK(impl);
 
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index c5eadee..12cc270 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -129,7 +129,7 @@
       'variables': {
         'executable_name': 'base_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/base/wrap_main_starboard.h b/src/cobalt/base/wrap_main_starboard.h
index 15ababb..91d1022 100644
--- a/src/cobalt/base/wrap_main_starboard.h
+++ b/src/cobalt/base/wrap_main_starboard.h
@@ -93,7 +93,33 @@
       g_at_exit = NULL;
       break;
     }
-    default:
+    case kSbEventTypePause:
+    case kSbEventTypeUnpause:
+    case kSbEventTypeSuspend:
+    case kSbEventTypeResume:
+    case kSbEventTypeInput:
+    case kSbEventTypeUser:
+    case kSbEventTypeLink:
+    case kSbEventTypeVerticalSync:
+    case kSbEventTypeNetworkDisconnect:
+    case kSbEventTypeNetworkConnect:
+    case kSbEventTypeScheduled:
+    case kSbEventTypeAccessiblitySettingsChanged:
+#if SB_API_VERSION >= 6
+    case kSbEventTypeLowMemory:
+#endif  // SB_API_VERSION >= 6
+#if SB_API_VERSION >= 8
+    case kSbEventTypeWindowSizeChanged:
+#endif  // SB_API_VERSION >= 8
+#if SB_HAS(ON_SCREEN_KEYBOARD)
+    case kSbEventTypeOnScreenKeyboardShown:
+    case kSbEventTypeOnScreenKeyboardHidden:
+    case kSbEventTypeOnScreenKeyboardFocused:
+    case kSbEventTypeOnScreenKeyboardBlurred:
+#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_HAS(CAPTIONS)
+    case kSbEventTypeAccessibilityCaptionSettingsChanged:
+#endif  // SB_HAS(CAPTIONS)
       event_function(event);
       break;
   }
diff --git a/src/cobalt/bindings/bindings.gypi b/src/cobalt/bindings/bindings.gypi
index 0db0d57..a54745c 100644
--- a/src/cobalt/bindings/bindings.gypi
+++ b/src/cobalt/bindings/bindings.gypi
@@ -65,14 +65,6 @@
       '../../third_party/blink/Source/bindings/scripts/scripts.gypi',
     ],
     'variables': {
-      # Legend has it that experienced Chrome developers can actually nest
-      # variables up to four levels.  Here, we keep things simple and only do
-      # three.  We do this because "engine_variables.gypi" will create another
-      # variables scope and then branch on |javascript_engine|, which requires
-      # a default value to be provided one level lower.
-      'variables': {
-        'javascript_engine%': '<(default_javascript_engine)',
-      },
       # Specify a default component for generated window IDL. This should be
       # removed when the concept of 'components' in the blink IDL parsing scripts
       # is refactored out, since it doesn't really apply to Cobalt.
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc
index cca4db3..5fe4b1c 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc
@@ -58,8 +58,14 @@
                  int conversion_flags, ExceptionState* exception_state,
                  DerivedDictionary* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  MozjsExceptionState* mozjs_exception_state = base::polymorphic_downcast<MozjsExceptionState*>(exception_state);
+  DCHECK(!mozjs_exception_state->is_exception_set());
+
   FromJSValue(context, value, conversion_flags, exception_state,
       static_cast<cobalt::bindings::testing::TestDictionary*>(out_dictionary));
+  if (mozjs_exception_state->is_exception_set()) {
+    return;
+  }
   // https://heycam.github.io/webidl/#es-dictionary
 
   if (value.isUndefined() || value.isNull()) {
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
index 438e531..2fec75f 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
@@ -58,6 +58,9 @@
                  int conversion_flags, ExceptionState* exception_state,
                  DictionaryWithDictionaryMember* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  MozjsExceptionState* mozjs_exception_state = base::polymorphic_downcast<MozjsExceptionState*>(exception_state);
+  DCHECK(!mozjs_exception_state->is_exception_set());
+
   // https://heycam.github.io/webidl/#es-dictionary
 
   if (value.isUndefined() || value.isNull()) {
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
index 2fb1b7d..8d3599b 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -159,6 +159,9 @@
                  int conversion_flags, ExceptionState* exception_state,
                  TestDictionary* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  MozjsExceptionState* mozjs_exception_state = base::polymorphic_downcast<MozjsExceptionState*>(exception_state);
+  DCHECK(!mozjs_exception_state->is_exception_set());
+
   // https://heycam.github.io/webidl/#es-dictionary
 
   if (value.isUndefined() || value.isNull()) {
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
index 1f72f1a..9f59bcd 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
@@ -118,7 +118,6 @@
     return;
   }
   info.GetReturnValue().Set(result_value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 void IndexedPropertyGetterCallback(
@@ -211,8 +210,10 @@
 
   impl->AnonymousNamedSetter(property_name, native_value);
   result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
   info.GetReturnValue().Set(value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 void IndexedPropertySetterCallback(
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
index ae2eaf5..904b470 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
@@ -118,7 +118,6 @@
     return;
   }
   info.GetReturnValue().Set(result_value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 
@@ -199,8 +198,10 @@
 
   impl->AnonymousNamedSetter(property_name, native_value);
   result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
   info.GetReturnValue().Set(value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc
index 6b0a824..cfc2d55 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc
@@ -62,8 +62,13 @@
                  int conversion_flags, ExceptionState* exception_state,
                  DerivedDictionary* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  V8cExceptionState* v8c_exception_state = base::polymorphic_downcast<V8cExceptionState*>(exception_state);
+  DCHECK(!v8c_exception_state->is_exception_set());
 
   FromJSValue(isolate, value, conversion_flags, exception_state, static_cast<cobalt::bindings::testing::TestDictionary*>(out_dictionary));
+  if (v8c_exception_state->is_exception_set()) {
+    return;
+  }
 
   // https://heycam.github.io/webidl/#es-dictionary
   if (value->IsNullOrUndefined()) {
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
index 7d7dd42..9b0252e 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
@@ -122,7 +122,6 @@
     return;
   }
   info.GetReturnValue().Set(result_value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 
@@ -203,8 +202,10 @@
 
   impl->AnonymousNamedSetter(property_name, native_value);
   result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
   info.GetReturnValue().Set(value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc
index 40932e7..da0b220 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc
@@ -62,6 +62,8 @@
                  int conversion_flags, ExceptionState* exception_state,
                  DictionaryWithDictionaryMember* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  V8cExceptionState* v8c_exception_state = base::polymorphic_downcast<V8cExceptionState*>(exception_state);
+  DCHECK(!v8c_exception_state->is_exception_set());
 
 
   // https://heycam.github.io/webidl/#es-dictionary
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
index 72017ae..c7449e4 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
@@ -118,7 +118,6 @@
     return;
   }
   info.GetReturnValue().Set(result_value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 void IndexedPropertyGetterCallback(
@@ -211,8 +210,10 @@
 
   impl->NamedSetter(property_name, native_value);
   result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
   info.GetReturnValue().Set(value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 void IndexedPropertySetterCallback(
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
index 496da3d..6e0b586 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
@@ -118,7 +118,6 @@
     return;
   }
   info.GetReturnValue().Set(result_value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 
@@ -199,8 +198,10 @@
 
   impl->NamedSetter(property_name, native_value);
   result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
   info.GetReturnValue().Set(value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc
index 41aaf2c..67c412d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc
@@ -190,6 +190,8 @@
                  int conversion_flags, ExceptionState* exception_state,
                  TestDictionary* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  V8cExceptionState* v8c_exception_state = base::polymorphic_downcast<V8cExceptionState*>(exception_state);
+  DCHECK(!v8c_exception_state->is_exception_set());
 
 
   // https://heycam.github.io/webidl/#es-dictionary
diff --git a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
index cae17a7..490038c 100644
--- a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
@@ -91,9 +91,15 @@
                  int conversion_flags, ExceptionState* exception_state,
                  {{class_name}}* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  MozjsExceptionState* mozjs_exception_state = base::polymorphic_downcast<MozjsExceptionState*>(exception_state);
+  DCHECK(!mozjs_exception_state->is_exception_set());
+
   {% if parent %}
   FromJSValue(context, value, conversion_flags, exception_state,
       static_cast<{{parent}}*>(out_dictionary));
+  if (mozjs_exception_state->is_exception_set()) {
+    return;
+  }
   {% endif %}
   // https://heycam.github.io/webidl/#es-dictionary
 
diff --git a/src/cobalt/bindings/testing/array_buffers_test.cc b/src/cobalt/bindings/testing/array_buffers_test.cc
new file mode 100644
index 0000000..1dd1e16
--- /dev/null
+++ b/src/cobalt/bindings/testing/array_buffers_test.cc
@@ -0,0 +1,149 @@
+// Copyright 2018 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/arbitrary_interface.h"
+#include "cobalt/bindings/testing/bindings_test_base.h"
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/data_view.h"
+#include "cobalt/script/typed_arrays.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+namespace {
+
+// TODO: Change to an idl that uses array buffers once bindings supports array
+// buffers.
+using ArrayBufferTest = InterfaceBindingsTest<ArbitraryInterface>;
+
+void* IncrementPointer(void* pointer, size_t length) {
+  return reinterpret_cast<void*>(reinterpret_cast<uint8_t*>(pointer) + length);
+}
+
+}  // namespace
+
+TEST_F(ArrayBufferTest, ArrayBufferTest) {
+  {
+    auto array_buffer = script::ArrayBuffer::New(global_environment_, 1024);
+    EXPECT_EQ(1024, array_buffer->ByteLength());
+    EXPECT_NE(nullptr, array_buffer->Data());
+  }
+
+  {
+    scoped_array<uint8_t> data(new uint8_t[256]);
+    for (int i = 0; i < 256; i++) {
+      data[i] = i;
+    }
+
+    auto array_buffer =
+        script::ArrayBuffer::New(global_environment_, data.get(), 256);
+    EXPECT_EQ(256, array_buffer->ByteLength());
+    for (int i = 0; i < 256; i++) {
+      EXPECT_EQ(i, reinterpret_cast<uint8_t*>(array_buffer->Data())[i]);
+    }
+  }
+
+  {
+    script::PreallocatedArrayBufferData preallocated_data(256);
+    EXPECT_EQ(256, preallocated_data.byte_length());
+    for (int i = 0; i < preallocated_data.byte_length(); i++) {
+      reinterpret_cast<uint8_t*>(preallocated_data.data())[i] = i;
+    }
+
+    void* data_pointer = preallocated_data.data();
+
+    auto array_buffer =
+        script::ArrayBuffer::New(global_environment_, &preallocated_data);
+    EXPECT_EQ(256, array_buffer->ByteLength());
+    EXPECT_EQ(data_pointer, array_buffer->Data());
+    for (int i = 0; i < 256; i++) {
+      EXPECT_EQ(i, reinterpret_cast<uint8_t*>(array_buffer->Data())[i]);
+    }
+
+    EXPECT_EQ(nullptr, preallocated_data.data());
+  }
+}
+
+TEST_F(ArrayBufferTest, UnusedPreallocatedDataReleases) {
+  script::PreallocatedArrayBufferData preallocated_data(256);
+  // Expect |my_data| to be properly freed. Rely on ASan to catch anything
+  // going wrong here.
+}
+
+TEST_F(ArrayBufferTest, DataViewTest) {
+  auto array_buffer = script::ArrayBuffer::New(global_environment_, 1024);
+  auto data_view =
+      script::DataView::New(global_environment_, array_buffer, 8, 16);
+
+  EXPECT_EQ(8, data_view->ByteOffset());
+  EXPECT_EQ(16, data_view->ByteLength());
+  EXPECT_EQ(IncrementPointer(array_buffer->Data(), 8), data_view->RawData());
+
+  constexpr int64_t value1 = 0xc0ba17;
+  reinterpret_cast<int64_t*>(IncrementPointer(array_buffer->Data(), 8))[0] =
+      value1;
+  EXPECT_EQ(value1, reinterpret_cast<int64_t*>(data_view->RawData())[0]);
+
+  constexpr int64_t value2 = 0x15c001;
+  reinterpret_cast<int64_t*>(data_view->RawData())[1] = value2;
+  EXPECT_EQ(value2, reinterpret_cast<int64_t*>(
+                        IncrementPointer(array_buffer->Data(), 8))[1]);
+}
+
+template <typename TypedArrayType, typename CType>
+void TypedArrayTest(script::GlobalEnvironment* global_environment) {
+  {
+    auto array_buffer = script::ArrayBuffer::New(global_environment, 1024);
+    auto typed_array =
+        TypedArrayType::New(global_environment, array_buffer, 8, 16);
+
+    EXPECT_EQ(typed_array->Buffer()->ByteLength(), 1024);
+    EXPECT_EQ(typed_array->ByteOffset(), 8);
+    EXPECT_EQ(typed_array->ByteLength(), 16 * sizeof(CType));
+    EXPECT_EQ(typed_array->RawData(), reinterpret_cast<void*>(IncrementPointer(
+                                          array_buffer->Data(), 8)));
+
+    EXPECT_EQ(typed_array->Length(), 16);
+
+    EXPECT_EQ(typed_array->Data(), reinterpret_cast<CType*>(IncrementPointer(
+                                       array_buffer->Data(), 8)));
+  }
+
+  {
+    auto typed_array = TypedArrayType::New(global_environment, 1024);
+
+    EXPECT_EQ(typed_array->Buffer()->ByteLength(), 1024 * sizeof(CType));
+    EXPECT_EQ(typed_array->ByteOffset(), 0);
+    EXPECT_EQ(typed_array->ByteLength(), 1024 * sizeof(CType));
+
+    EXPECT_EQ(typed_array->Length(), 1024);
+
+    EXPECT_EQ(typed_array->Data(),
+              reinterpret_cast<CType*>(typed_array->RawData()));
+  }
+}
+
+TEST_F(ArrayBufferTest, TypedArrays) {
+#define CALL_TYPED_ARRAY_TEST(array, ctype) \
+  TypedArrayTest<script::array, ctype>(global_environment_);
+  COBALT_SCRIPT_TYPED_ARRAY_LIST(CALL_TYPED_ARRAY_TEST)
+#undef CALL_TYPED_ARRAY_TEST
+}
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/testing/bindings_test_base.h b/src/cobalt/bindings/testing/bindings_test_base.h
index e32c87a..572f9bf 100644
--- a/src/cobalt/bindings/testing/bindings_test_base.h
+++ b/src/cobalt/bindings/testing/bindings_test_base.h
@@ -67,8 +67,7 @@
     scoped_refptr<script::SourceCode> source =
         script::SourceCode::CreateSourceCode(
             script, base::SourceLocation("[object BindingsTestBase]", 1, 1));
-    return global_environment_->EvaluateScript(source, false /*mute_errors*/,
-                                               out_result);
+    return global_environment_->EvaluateScript(source, out_result);
   }
 
   bool EvaluateScript(const std::string& script,
@@ -78,8 +77,8 @@
     scoped_refptr<script::SourceCode> source =
         script::SourceCode::CreateSourceCode(
             script, base::SourceLocation("[object BindingsTestBase]", 1, 1));
-    return global_environment_->EvaluateScript(
-        source, owning_object, false /*mute_errors*/, out_value_handle);
+    return global_environment_->EvaluateScript(source, owning_object,
+                                               out_value_handle);
   }
 
   void CollectGarbage() { engine_->CollectGarbage(); }
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp
index b7808dc..5294ddd 100644
--- a/src/cobalt/bindings/testing/testing.gyp
+++ b/src/cobalt/bindings/testing/testing.gyp
@@ -145,6 +145,7 @@
       'target_name': 'bindings_test',
       'type': '<(gtest_target_type)',
       'sources': [
+        'array_buffers_test.cc',
         'any_bindings_test.cc',
         'any_dictionary_bindings_test.cc',
         'boolean_type_bindings_test.cc',
@@ -203,7 +204,7 @@
       'variables': {
         'executable_name': 'bindings_test',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
@@ -230,7 +231,7 @@
       'variables': {
         'executable_name': 'bindings_sandbox',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template b/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template
index 79a6222..61fc652 100644
--- a/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template
+++ b/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template
@@ -94,9 +94,14 @@
                  int conversion_flags, ExceptionState* exception_state,
                  {{class_name}}* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  V8cExceptionState* v8c_exception_state = base::polymorphic_downcast<V8cExceptionState*>(exception_state);
+  DCHECK(!v8c_exception_state->is_exception_set());
 
 {% if parent %}
   FromJSValue(isolate, value, conversion_flags, exception_state, static_cast<{{parent}}*>(out_dictionary));
+  if (v8c_exception_state->is_exception_set()) {
+    return;
+  }
 {% endif %}
 
   // https://heycam.github.io/webidl/#es-dictionary
diff --git a/src/cobalt/bindings/v8c/templates/interface.cc.template b/src/cobalt/bindings/v8c/templates/interface.cc.template
index 6f3a9de..5914f60 100644
--- a/src/cobalt/bindings/v8c/templates/interface.cc.template
+++ b/src/cobalt/bindings/v8c/templates/interface.cc.template
@@ -117,7 +117,6 @@
     return;
   }
   info.GetReturnValue().Set(result_value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 {% if not indexed_property_getter %}
@@ -202,8 +201,10 @@
                         named_property_setter.name, ["property_name", "native_value"],
                         named_property_setter.raises_exception,
                         named_property_setter.call_with) }}
+  if (exception_state.is_exception_set()) {
+    return;
+  }
   info.GetReturnValue().Set(value);
-  DCHECK(!exception_state.is_exception_set());
 }
 
 {% if not indexed_property_setter %}
diff --git a/src/cobalt/black_box_tests/README.md b/src/cobalt/black_box_tests/README.md
index 335028f..afb4cef 100644
--- a/src/cobalt/black_box_tests/README.md
+++ b/src/cobalt/black_box_tests/README.md
@@ -47,15 +47,17 @@
 
 A wrapper around the app launcher. BlackBoxCobaltRunner includes a webdriver
 module attached to the app launcher's Cobalt instance after it starts running.
-Includes a method(HTMLTestsSucceeded()) to check test result on the JavaScript
+Includes a method(JSTestsSucceeded()) to check test result on the JavaScript
 side. Call this method to wait for JavaScript test result.
 black_box_test_js_util.js provides some utility functions that are meant to
-work with runner.HTMLTestsSucceeded() in the python test scripts. Together,
+work with runner.JSTestsSucceeded() in the python test scripts. Together,
 they allow for test logic to exist in either the python test scripts or
 JavaScript test data.
 e.g. Call OnEndTest() to signal test completion in the JavaScripts,
-HTMLTestsSucceeded() will react to the signal and return the test status of
-JavaScript test logic.
+JSTestsSucceeded() will react to the signal and return the test status of
+JavaScript test logic; another example is that when python script wants to wait
+for some setup steps on JavaScript, call runner.WaitForJSTestsSetup(). Calling
+setupFinished() in JavaScript whenever ready will unblock the wait.
 
 
 ## Test Data
diff --git a/src/cobalt/black_box_tests/black_box_cobalt_runner.py b/src/cobalt/black_box_tests/black_box_cobalt_runner.py
index 97f838f..3810081 100644
--- a/src/cobalt/black_box_tests/black_box_cobalt_runner.py
+++ b/src/cobalt/black_box_tests/black_box_cobalt_runner.py
@@ -16,10 +16,13 @@
 
 
 class BlackBoxCobaltRunner(cobalt_runner.CobaltRunner):
+  """Custom CobaltRunner made for BlackBoxTests' need."""
 
   def JSTestsSucceeded(self):
     """Check test assertions in HTML page."""
 
+    # Call onTestEnd() in black_box_js_test_utils.js to unblock the waiting for
+    # JavaScript test logic completion.
     self.PollUntilFound('[' + _TEST_STATUS_ELEMENT_NAME + ']')
     body_element = self.UniqueFind('body')
     return body_element.get_attribute(
@@ -27,4 +30,7 @@
 
   def WaitForJSTestsSetup(self):
     """Poll setup status until JavaScript gives green light."""
+
+    # Calling setupFinished() in black_box_js_test_utils.js to unblock the
+    # waiting logic here.
     self.PollUntilFound('#{}'.format(_JS_TEST_SETUP_DONE_MESSAGE))
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py
index 90171b9..41d9f03 100644
--- a/src/cobalt/black_box_tests/black_box_tests.py
+++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -114,7 +114,8 @@
 
   def Run(self):
 
-    self._StartTestdataServer()
+    if not self._StartTestdataServer():
+      return 1
     logging.basicConfig(level=logging.DEBUG)
     GetDeviceParams()
     if self.test_name:
@@ -141,8 +142,14 @@
             os.path.dirname(os.path.realpath(__file__)), 'testdata'),
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT)
-    print('Starting HTTP server on port: {}'.format(
-        _DEFAULT_TEST_DATA_SERVER_PORT))
+    if self.default_test_data_server_process.returncode is not None:
+      # If the return code is not None now, server is not running normally.
+      print('can not start default test data server.')
+      return False
+    else:
+      print('Starting HTTP server on port: {}'.format(
+          _DEFAULT_TEST_DATA_SERVER_PORT))
+      return True
 
   def _KillTestdataServer(self):
     """Exit black_box_test_runner with test result."""
diff --git a/src/cobalt/black_box_tests/testdata/allow_eval.html b/src/cobalt/black_box_tests/testdata/allow_eval.html
index f0eeab3..c7ad9e4 100644
--- a/src/cobalt/black_box_tests/testdata/allow_eval.html
+++ b/src/cobalt/black_box_tests/testdata/allow_eval.html
@@ -7,12 +7,19 @@
 
 <body>
   <h1>
-    <span id="unique_id">ID element</span>
+    <span>ID element</span>
   </h1>
   <script>
     // When Content Security Policy is missing, JavaScript eval() should be
     // allowed.
-    assertEqual(4, eval('1+3'));
+    try {
+      assertEqual(4, eval('1+3'));
+    } catch(error) {
+      // Catch the error if any, otherwise the test will exit after timeout.
+      console.log("Calling eval without CSP raised unexpected exception:");
+      console.log(error);
+      notReached();
+    }
     onEndTest();
   </script>
 </body>
\ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/disable_eval_with_csp.html b/src/cobalt/black_box_tests/testdata/disable_eval_with_csp.html
index 402e2c3..447a3bd 100644
--- a/src/cobalt/black_box_tests/testdata/disable_eval_with_csp.html
+++ b/src/cobalt/black_box_tests/testdata/disable_eval_with_csp.html
@@ -9,7 +9,7 @@
 
 <body>
   <h1>
-    <span id="unique_id">ID element</span>
+    <span>ID element</span>
   </h1>
   <script>
     document.addEventListener("securitypolicyviolation", (e) => {
diff --git a/src/cobalt/black_box_tests/testdata/persistent_cookie.html b/src/cobalt/black_box_tests/testdata/persistent_cookie.html
index 25cec14..dd4c75f 100644
--- a/src/cobalt/black_box_tests/testdata/persistent_cookie.html
+++ b/src/cobalt/black_box_tests/testdata/persistent_cookie.html
@@ -13,7 +13,7 @@
 
 <body>
   <h1>
-    <span id="unique_id">Test cookies between sessions.</span>
+    <span>Test cookies between sessions.</span>
   </h1>
   <script>
     document.body.style.backgroundColor = "green";
diff --git a/src/cobalt/black_box_tests/testdata/preload_visibility.html b/src/cobalt/black_box_tests/testdata/preload_visibility.html
index 19f2ef2..7793e3d 100644
--- a/src/cobalt/black_box_tests/testdata/preload_visibility.html
+++ b/src/cobalt/black_box_tests/testdata/preload_visibility.html
@@ -7,7 +7,7 @@
 
 <body>
   <h1>
-    <span id="unique_id">ID element</span>
+    <span>ID element</span>
   </h1>
   <script>
     // In preload mode, visibility should be "prerender" and window/document
diff --git a/src/cobalt/black_box_tests/testdata/suspend_visibility.html b/src/cobalt/black_box_tests/testdata/suspend_visibility.html
index 50c9197..092e817 100644
--- a/src/cobalt/black_box_tests/testdata/suspend_visibility.html
+++ b/src/cobalt/black_box_tests/testdata/suspend_visibility.html
@@ -7,7 +7,7 @@
 
 <body>
   <h1>
-    <span id="unique_id">ID element</span>
+    <span>ID element</span>
   </h1>
   <script>
     // In started mode, visibility should be "visible" and window/document
@@ -28,7 +28,7 @@
         onEndTest();
       } else {
         // By now, it is in Started state and Suspend state behavior
-        // verification will never secceed.
+        // verification will never succeed.
         notReached();
       }
     }
diff --git a/src/cobalt/black_box_tests/testdata/timer_hit_after_preload.html b/src/cobalt/black_box_tests/testdata/timer_hit_after_preload.html
index f43854c..b732ed1 100644
--- a/src/cobalt/black_box_tests/testdata/timer_hit_after_preload.html
+++ b/src/cobalt/black_box_tests/testdata/timer_hit_after_preload.html
@@ -12,7 +12,7 @@
 
 <body>
   <h1>
-    <span id="unique_id">To test timer behavior after preload and continue</span>
+    <span>To test timer behavior after preload and continue</span>
   </h1>
   <script>
 
diff --git a/src/cobalt/black_box_tests/testdata/timer_hit_in_preload.html b/src/cobalt/black_box_tests/testdata/timer_hit_in_preload.html
index a54cc6b..a8b8510 100644
--- a/src/cobalt/black_box_tests/testdata/timer_hit_in_preload.html
+++ b/src/cobalt/black_box_tests/testdata/timer_hit_in_preload.html
@@ -12,7 +12,7 @@
 
 <body>
   <h1>
-    <span id="unique_id">This test is for timer callback while preloading</span>
+    <span>This test is for timer callback while preloading</span>
   </h1>
   <script>
     let setTimeoutMethod = new TimerTestCase('setTimeout', 1);
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index c454683..ccce8cb 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -693,8 +693,10 @@
         GetWebDriverPort(), GetWebDriverListenIp(),
         base::Bind(&BrowserModule::CreateSessionDriver,
                    base::Unretained(browser_module_.get())),
+        // Webdriver spec requires us to encode to PNG format.
         base::Bind(&BrowserModule::RequestScreenshotToBuffer,
-                   base::Unretained(browser_module_.get())),
+                   base::Unretained(browser_module_.get()),
+                   loader::image::EncodedStaticImage::ImageFormat::kPNG),
         base::Bind(&BrowserModule::SetProxy,
                    base::Unretained(browser_module_.get())),
         base::Bind(&Application::Quit, base::Unretained(this))));
@@ -916,6 +918,9 @@
       DLOG(INFO) << "Finished suspending.";
       break;
     case kSbEventTypeResume:
+#if SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
+      DCHECK(SbSystemSupportsResume());
+#endif  // SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
       DLOG(INFO) << "Got resume event.";
       app_status_ = kPausedAppStatus;
       ++app_resume_count_;
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 9869893..5545333 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -60,6 +60,8 @@
         'memory_tracker/tool/compressed_time_series_tool.cc',
         'memory_tracker/tool/compressed_time_series_tool.h',
         'memory_tracker/tool/histogram_table_csv_base.h',
+        'memory_tracker/tool/internal_fragmentation_tool.cc',
+        'memory_tracker/tool/internal_fragmentation_tool.h',
         'memory_tracker/tool/leak_finder_tool.cc',
         'memory_tracker/tool/leak_finder_tool.h',
         'memory_tracker/tool/log_writer_tool.cc',
@@ -86,6 +88,8 @@
         'on_screen_keyboard_starboard_bridge.h',
         'render_tree_combiner.cc',
         'render_tree_combiner.h',
+        'screen_shot_writer.cc',
+        'screen_shot_writer.h',
         'splash_screen.cc',
         'splash_screen.h',
         'splash_screen_cache.cc',
@@ -102,6 +106,8 @@
         'trace_manager.h',
         'url_handler.cc',
         'url_handler.h',
+        'user_agent_string.cc',
+        'user_agent_string.h',
         'web_module.cc',
         'web_module.h',
         'web_module_stat_tracker.cc',
@@ -141,8 +147,10 @@
         '<(DEPTH)/cobalt/media_capture/media_capture.gyp:*',
         '<(DEPTH)/cobalt/media_session/media_session.gyp:media_session',
         '<(DEPTH)/cobalt/network/network.gyp:network',
+        '<(DEPTH)/cobalt/overlay_info/overlay_info.gyp:overlay_info',
         '<(DEPTH)/cobalt/page_visibility/page_visibility.gyp:page_visibility',
         '<(DEPTH)/cobalt/renderer/renderer.gyp:renderer',
+        '<(DEPTH)/cobalt/renderer/test/png_utils/png_utils.gyp:png_utils',
         '<(DEPTH)/cobalt/script/engine.gyp:engine',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/sso/sso.gyp:sso',
@@ -154,7 +162,6 @@
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
         '<(DEPTH)/nb/nb.gyp:nb',
         'browser_bindings.gyp:bindings',
-        'screen_shot_writer',
         '<(cobalt_webapi_extension_gyp_target)',
       ],
       # This target doesn't generate any headers, but it exposes generated
@@ -169,6 +176,10 @@
         # on this one.
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
       ],
+      'include_dirs': [
+        # For cobalt_build_id.h
+        '<(SHARED_INTERMEDIATE_DIR)',
+      ],
       'conditions': [
         ['enable_about_scheme == 1', {
           'defines': [ 'ENABLE_ABOUT_SCHEME' ],
@@ -185,6 +196,9 @@
             '<(DEPTH)/cobalt/media/media.gyp:media',
           ],
         }],
+        ['cobalt_enable_lib == 1', {
+          'defines' : ['COBALT_ENABLE_LIB'],
+        }],
         ['mesh_cache_size_in_bytes == "auto"', {
           'conditions': [
             ['enable_map_to_mesh==1', {
@@ -206,34 +220,6 @@
     },
 
     {
-      # This target provides functionality for creating screenshots of a
-      # render tree and writing it to disk.
-      'target_name': 'screen_shot_writer',
-      'type': 'static_library',
-      'variables': {
-        # This target includes non-Cobalt code that produces pendantic
-        # warnings as errors.
-        'sb_pedantic_warnings': 0,
-      },
-      'conditions': [
-        ['enable_screenshot==1', {
-          'sources': [
-            'screen_shot_writer.h',
-            'screen_shot_writer.cc',
-          ],
-          'dependencies': [
-            '<(DEPTH)/cobalt/base/base.gyp:base',
-            '<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
-            '<(DEPTH)/cobalt/renderer/test/png_utils/png_utils.gyp:png_utils',
-          ],
-          'all_dependent_settings': {
-            'defines': [ 'ENABLE_SCREENSHOT', ],
-          },
-        }],
-      ],
-    },
-
-    {
       'target_name': 'browser_test',
       'type': '<(gtest_target_type)',
       'sources': [
@@ -248,6 +234,7 @@
         'memory_settings/test_common.h',
         'memory_tracker/tool/tool_impl_test.cc',
         'memory_tracker/tool/util_test.cc',
+        'user_agent_string_test.cc',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
@@ -272,7 +259,7 @@
       'variables': {
         'executable_name': 'browser_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index ef44a7d..c4c63e2 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -136,6 +136,7 @@
         '../dom/pointer_event.idl',
         '../dom/progress_event.idl',
         '../dom/screen.idl',
+        '../dom/screenshot.idl',
         '../dom/security_policy_violation_event.idl',
         '../dom/storage.idl',
         '../dom/storage_event.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 2115273..7911067 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -36,6 +36,7 @@
 #include "cobalt/browser/screen_shot_writer.h"
 #include "cobalt/browser/storage_upgrade_handler.h"
 #include "cobalt/browser/switches.h"
+#include "cobalt/browser/user_agent_string.h"
 #include "cobalt/browser/webapi_extension.h"
 #include "cobalt/dom/csp_delegate_factory.h"
 #include "cobalt/dom/input_event_init.h"
@@ -45,6 +46,7 @@
 #include "cobalt/dom/window.h"
 #include "cobalt/h5vcc/h5vcc.h"
 #include "cobalt/input/input_device_manager_fuzzer.h"
+#include "cobalt/overlay_info/overlay_info_registry.h"
 #include "nb/memory_scope.h"
 #include "starboard/atomic.h"
 #include "starboard/configuration.h"
@@ -116,11 +118,13 @@
 
 const int kMainWebModuleZIndex = 1;
 const int kSplashScreenZIndex = 2;
+#if defined(ENABLE_DEBUG_CONSOLE)
+const int kDebugConsoleZIndex = 3;
+#endif  // defined(ENABLE_DEBUG_CONSOLE)
+const int kOverlayInfoZIndex = 4;
 
 #if defined(ENABLE_DEBUG_CONSOLE)
 
-const int kDebugConsoleZIndex = 3;
-
 const char kFuzzerToggleCommand[] = "fuzzer_toggle";
 const char kFuzzerToggleCommandShortHelp[] = "Toggles the input fuzzer on/off.";
 const char kFuzzerToggleCommandLongHelp[] =
@@ -137,7 +141,6 @@
     "MediaModule::SetConfiguration() on individual platform for settings "
     "supported on the particular platform.";
 
-#if defined(ENABLE_SCREENSHOT)
 // Command to take a screenshot.
 const char kScreenshotCommand[] = "screenshot";
 
@@ -146,9 +149,7 @@
 const char kScreenshotCommandLongHelp[] =
     "Creates a screenshot of the most recent layout tree and writes it "
     "to disk. Logs the filename of the screenshot to the console when done.";
-#endif  // defined(ENABLE_SCREENSHOT)
 
-#if defined(ENABLE_SCREENSHOT)
 void ScreenshotCompleteCallback(const FilePath& output_path) {
   DLOG(INFO) << "Screenshot written to " << output_path.value();
 }
@@ -171,9 +172,9 @@
 
   FilePath output_path = dir.Append(screenshot_file_name);
   browser_module->RequestScreenshotToFile(
-      output_path, base::Bind(&ScreenshotCompleteCallback, output_path));
+      output_path, loader::image::EncodedStaticImage::ImageFormat::kPNG,
+      base::Bind(&ScreenshotCompleteCallback, output_path));
 }
-#endif  // defined(ENABLE_SCREENSHOT)
 
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
@@ -232,8 +233,10 @@
                        options_.storage_manager_options),
       is_rendered_(false),
       can_play_type_handler_(media::MediaModule::CreateCanPlayTypeHandler()),
-      network_module_(&storage_manager_, event_dispatcher_,
-                      options_.network_module_options),
+      network_module_(
+          CreateUserAgentString(GetUserAgentPlatformInfoFromSystem()),
+          &storage_manager_, event_dispatcher_,
+          options_.network_module_options),
       splash_screen_cache_(new SplashScreenCache()),
 #if SB_HAS(ON_SCREEN_KEYBOARD)
       on_screen_keyboard_bridge_(new OnScreenKeyboardStarboardBridge(
@@ -261,12 +264,10 @@
           kSetMediaConfigCommand,
           base::Bind(&BrowserModule::OnSetMediaConfig, base::Unretained(this)),
           kSetMediaConfigCommandShortHelp, kSetMediaConfigCommandLongHelp)),
-#if defined(ENABLE_SCREENSHOT)
       ALLOW_THIS_IN_INITIALIZER_LIST(screenshot_command_handler_(
           kScreenshotCommand,
           base::Bind(&OnScreenshotMessage, base::Unretained(this)),
           kScreenshotCommandShortHelp, kScreenshotCommandLongHelp)),
-#endif  // defined(ENABLE_SCREENSHOT)
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
       has_resumed_(true, false),
 #if defined(COBALT_CHECK_RENDER_TIMEOUT)
@@ -281,6 +282,11 @@
       next_timeline_id_(1),
       current_splash_screen_timeline_id_(-1),
       current_main_web_module_timeline_id_(-1) {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::BrowserModule()");
+
+  // Apply platform memory setting adjustments and defaults.
+  ApplyAutoMemSettings();
+
   h5vcc_url_handler_.reset(new H5vccURLHandler(this));
 
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
@@ -300,7 +306,8 @@
                  url),
       base::TimeDelta::FromSeconds(kRenderTimeOutPollingDelaySeconds));
 #endif
-  TRACE_EVENT0("cobalt::browser", "BrowserModule::BrowserModule()");
+
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
 
   // Create the main web module layer.
   main_web_module_layer_ =
@@ -311,6 +318,12 @@
 #if defined(ENABLE_DEBUG_CONSOLE)
   debug_console_layer_ = render_tree_combiner_.CreateLayer(kDebugConsoleZIndex);
 #endif
+  if (command_line->HasSwitch(browser::switches::kQrCodeOverlay)) {
+    qr_overlay_info_layer_ =
+        render_tree_combiner_.CreateLayer(kOverlayInfoZIndex);
+  } else {
+    overlay_info::OverlayInfoRegistry::Disable();
+  }
 
   // Setup our main web module to have the H5VCC API injected into it.
   DCHECK(!ContainsKey(options_.web_module_options.injected_window_attributes,
@@ -332,7 +345,6 @@
         base::Bind(&CreateExtensionInterface);
   }
 
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
 #if defined(ENABLE_DEBUG_CONSOLE) && defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   if (command_line->HasSwitch(switches::kInputFuzzer)) {
     OnFuzzerToggle(std::string());
@@ -364,7 +376,15 @@
     options_.web_module_options.enable_map_to_mesh_rectangular = true;
   }
 
+  if (qr_overlay_info_layer_) {
+    qr_code_overlay_.reset(new overlay_info::QrCodeOverlay(
+        GetViewportSize(), GetResourceProvider(),
+        base::Bind(&BrowserModule::QueueOnQrCodeOverlayRenderTreeProduced,
+                   base::Unretained(this))));
+  }
+
   fallback_splash_screen_url_ = options.fallback_splash_screen_url;
+
   // Synchronously construct our WebModule object.
   Navigate(url);
   DCHECK(web_module_);
@@ -508,6 +528,14 @@
     video_pixel_ratio = system_window_->GetVideoPixelRatio();
   }
 
+  // Make sure that automem has been run before creating the WebModule, so that
+  // we use properly configured options for all parameters.
+  DCHECK(auto_mem_);
+
+  options.provide_screenshot_function =
+      base::Bind(&ScreenShotWriter::RequestScreenshotToMemoryUnencoded,
+                 base::Unretained(screen_shot_writer_.get()));
+
   web_module_.reset(new WebModule(
       url, application_state_,
       base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
@@ -576,22 +604,38 @@
   return web_module_loaded_.TimedWait(timeout);
 }
 
-#if defined(ENABLE_SCREENSHOT)
-void BrowserModule::RequestScreenshotToFile(const FilePath& path,
-                                            const base::Closure& done_cb) {
-  if (screen_shot_writer_) {
-    screen_shot_writer_->RequestScreenshot(path, done_cb);
+void BrowserModule::RequestScreenshotToFile(
+    const FilePath& path,
+    loader::image::EncodedStaticImage::ImageFormat image_format,
+    const base::Closure& done_callback) {
+  DCHECK(screen_shot_writer_);
+  DCHECK(main_web_module_layer_);
+  base::optional<renderer::Submission> last_submission =
+      main_web_module_layer_->GetCurrentSubmission();
+  if (!last_submission) {
+    LOG(WARNING) << "Unable to find last submission.";
+    return;
   }
+  DCHECK(last_submission->render_tree);
+  screen_shot_writer_->RequestScreenshotToFile(
+      image_format, path, last_submission->render_tree, done_callback);
 }
 
 void BrowserModule::RequestScreenshotToBuffer(
-    const ScreenShotWriter::PNGEncodeCompleteCallback&
-        encode_complete_callback) {
-  if (screen_shot_writer_) {
-    screen_shot_writer_->RequestScreenshotToMemory(encode_complete_callback);
+    loader::image::EncodedStaticImage::ImageFormat image_format,
+    const ScreenShotWriter::ImageEncodeCompleteCallback& screenshot_ready) {
+  DCHECK(screen_shot_writer_);
+  DCHECK(main_web_module_layer_);
+  base::optional<renderer::Submission> last_submission =
+      main_web_module_layer_->GetCurrentSubmission();
+  if (!last_submission) {
+    LOG(WARNING) << "Unable to find last submission.";
+    return;
   }
+  DCHECK(last_submission->render_tree);
+  screen_shot_writer_->RequestScreenshotToMemory(
+      image_format, last_submission->render_tree, screenshot_ready);
 }
-#endif
 
 void BrowserModule::ProcessRenderTreeSubmissionQueue() {
   TRACE_EVENT0("cobalt::browser",
@@ -666,12 +710,6 @@
   }
   main_web_module_layer_->Submit(renderer_submission);
 
-#if defined(ENABLE_SCREENSHOT)
-  if (screen_shot_writer_) {
-    screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
-        layout_results.render_tree, layout_results.layout_time));
-  }
-#endif
   SubmitCurrentRenderTreeToRenderer();
 }
 
@@ -713,13 +751,39 @@
   render_tree_combiner_.SetTimelineLayer(splash_screen_layer_.get());
   splash_screen_layer_->Submit(renderer_submission);
 
-#if defined(ENABLE_SCREENSHOT)
 // TODO: write screen shot using render_tree_combiner_ (to combine
 // splash screen and main web_module). Consider when the splash
 // screen is overlaid on top of the main web module render tree, and
 // a screenshot is taken : there will be a race condition on which
 // web module update their render tree last.
-#endif
+
+  SubmitCurrentRenderTreeToRenderer();
+}
+
+void BrowserModule::QueueOnQrCodeOverlayRenderTreeProduced(
+    const scoped_refptr<render_tree::Node>& render_tree) {
+  TRACE_EVENT0("cobalt::browser",
+               "BrowserModule::QueueOnQrCodeOverlayRenderTreeProduced()");
+  render_tree_submission_queue_.AddMessage(
+      base::Bind(&BrowserModule::OnQrCodeOverlayRenderTreeProduced,
+                 base::Unretained(this), render_tree));
+  self_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&BrowserModule::ProcessRenderTreeSubmissionQueue, weak_this_));
+}
+
+void BrowserModule::OnQrCodeOverlayRenderTreeProduced(
+    const scoped_refptr<render_tree::Node>& render_tree) {
+  TRACE_EVENT0("cobalt::browser",
+               "BrowserModule::OnQrCodeOverlayRenderTreeProduced()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+  DCHECK(qr_overlay_info_layer_);
+
+  if (application_state_ == base::kApplicationStatePreloading) {
+    return;
+  }
+
+  qr_overlay_info_layer_->Submit(renderer::Submission(render_tree));
 
   SubmitCurrentRenderTreeToRenderer();
 }
@@ -1375,13 +1439,12 @@
 }
 
 void BrowserModule::InitializeSystemWindow() {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::InitializeSystemWindow()");
   resource_provider_stub_ = base::nullopt;
   DCHECK(!system_window_);
   system_window_.reset(new system_window::SystemWindow(
       event_dispatcher_, options_.requested_viewport_size));
-  auto_mem_.reset(new memory_settings::AutoMem(
-      GetViewportSize(), options_.command_line_auto_mem_settings,
-      options_.build_auto_mem_settings));
+  // Reapply automem settings now that we may have a different viewport size.
   ApplyAutoMemSettings();
 
   input_device_manager_ =
@@ -1400,6 +1463,10 @@
           .Pass();
   InstantiateRendererModule();
 
+#if SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
+  options_.media_module_options.allow_resume_after_suspend =
+      SbSystemSupportsResume();
+#endif  // SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
   media_module_ =
       media::MediaModule::Create(system_window_.get(), GetResourceProvider(),
                                  options_.media_module_options);
@@ -1419,18 +1486,14 @@
       system_window_.get(),
       RendererModuleWithCameraOptions(options_.renderer_module_options,
                                       input_device_manager_->camera_3d())));
-#if defined(ENABLE_SCREENSHOT)
   screen_shot_writer_.reset(new ScreenShotWriter(renderer_module_->pipeline()));
-#endif  // defined(ENABLE_SCREENSHOT)
 }
 
 void BrowserModule::DestroyRendererModule() {
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   DCHECK(renderer_module_);
 
-#if defined(ENABLE_SCREENSHOT)
   screen_shot_writer_.reset();
-#endif  // defined(ENABLE_SCREENSHOT)
   renderer_module_.reset();
 }
 
@@ -1450,6 +1513,10 @@
   if (web_module_) {
     web_module_->SetSize(size, video_pixel_ratio);
   }
+
+  if (qr_code_overlay_) {
+    qr_code_overlay_->SetSize(size);
+  }
 }
 
 void BrowserModule::SuspendInternal(bool is_start) {
@@ -1463,19 +1530,14 @@
     FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Suspend());
   }
 
+  if (qr_code_overlay_) {
+    qr_code_overlay_->SetResourceProvider(NULL);
+  }
+
   // Flush out any submitted render trees pushed since we started shutting down
   // the web modules above.
   render_tree_submission_queue_.ProcessAll();
 
-#if defined(ENABLE_SCREENSHOT)
-  // The screenshot writer may be holding on to a reference to a render tree
-  // which could in turn be referencing resources like images, so clear that
-  // out.
-  if (screen_shot_writer_) {
-    screen_shot_writer_->ClearLastPipelineSubmission();
-  }
-#endif
-
   // Clear out the render tree combiner so that it doesn't hold on to any
   // render tree resources either.
   main_web_module_layer_->Reset();
@@ -1483,6 +1545,9 @@
 #if defined(ENABLE_DEBUG_CONSOLE)
   debug_console_layer_->Reset();
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
+  if (qr_overlay_info_layer_) {
+    qr_overlay_info_layer_->Reset();
+  }
 
   if (media_module_) {
     media_module_->Suspend();
@@ -1516,6 +1581,9 @@
     FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_,
                       Resume(GetResourceProvider()));
   }
+  if (qr_code_overlay_) {
+    qr_code_overlay_->SetResourceProvider(GetResourceProvider());
+  }
 }
 
 void BrowserModule::StartOrResumeInternalPostStateUpdate() {
@@ -1553,8 +1621,21 @@
 }
 
 void BrowserModule::ApplyAutoMemSettings() {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::ApplyAutoMemSettings()");
+  auto_mem_.reset(new memory_settings::AutoMem(
+      GetViewportSize(), options_.command_line_auto_mem_settings,
+      options_.build_auto_mem_settings));
+
   LOG(INFO) << "\n\n" << auto_mem_->ToPrettyPrintString(SbLogIsTty()) << "\n\n";
 
+  if (javascript_gc_threshold_in_bytes_) {
+    DCHECK_EQ(*javascript_gc_threshold_in_bytes_,
+              auto_mem_->javascript_gc_threshold_in_bytes()->value());
+  } else {
+    javascript_gc_threshold_in_bytes_ =
+        auto_mem_->javascript_gc_threshold_in_bytes()->value();
+  }
+
   // Web Module options.
   options_.web_module_options.image_cache_capacity =
       static_cast<int>(auto_mem_->image_cache_size_in_bytes()->value());
@@ -1568,8 +1649,6 @@
         auto_mem_->image_cache_size_in_bytes()->value());
     web_module_->SetRemoteTypefaceCacheCapacity(
         auto_mem_->remote_typeface_cache_size_in_bytes()->value());
-    web_module_->SetJavascriptGcThreshold(
-        auto_mem_->javascript_gc_threshold_in_bytes()->value());
   }
 
   // Renderer Module options.
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 1086759..90bc287 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -54,6 +54,8 @@
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/media/media_module.h"
 #include "cobalt/network/network_module.h"
+#include "cobalt/overlay_info/qr_code_overlay.h"
+#include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "cobalt/render_tree/resource_provider_stub.h"
 #include "cobalt/renderer/renderer_module.h"
@@ -130,16 +132,17 @@
   void AddURLHandler(const URLHandler::URLHandlerCallback& callback);
   void RemoveURLHandler(const URLHandler::URLHandlerCallback& callback);
 
-#if defined(ENABLE_SCREENSHOT)
   // Request a screenshot to be written to the specified path. Callback will
   // be fired after the screenshot has been written to disk.
-  void RequestScreenshotToFile(const FilePath& path,
-                               const base::Closure& done_cb);
+  void RequestScreenshotToFile(
+      const FilePath& path,
+      loader::image::EncodedStaticImage::ImageFormat image_format,
+      const base::Closure& done_cb);
 
   // Request a screenshot to an in-memory buffer.
   void RequestScreenshotToBuffer(
-      const ScreenShotWriter::PNGEncodeCompleteCallback& screenshot_ready);
-#endif
+      loader::image::EncodedStaticImage::ImageFormat image_format,
+      const ScreenShotWriter::ImageEncodeCompleteCallback& screenshot_ready);
 
 #if defined(ENABLE_WEBDRIVER)
   scoped_ptr<webdriver::SessionDriver> CreateSessionDriver(
@@ -225,6 +228,13 @@
   void OnSplashScreenRenderTreeProduced(
       const browser::WebModule::LayoutResults& layout_results);
 
+  // Glue function to deal with the production of the qr code overlay render
+  // tree, and will manage handing it off to the renderer.
+  void QueueOnQrCodeOverlayRenderTreeProduced(
+      const scoped_refptr<render_tree::Node>& render_tree);
+  void OnQrCodeOverlayRenderTreeProduced(
+      const scoped_refptr<render_tree::Node>& render_tree);
+
   // Saves/loads the debug console mode to/from local storage so we can
   // persist the user's preference.
   void SaveDebugConsoleMode();
@@ -456,11 +466,10 @@
 #if defined(ENABLE_DEBUG_CONSOLE)
   scoped_ptr<RenderTreeCombiner::Layer> debug_console_layer_;
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
+  scoped_ptr<RenderTreeCombiner::Layer> qr_overlay_info_layer_;
 
-#if defined(ENABLE_SCREENSHOT)
   // Helper object to create screen shots of the last layout tree.
   scoped_ptr<ScreenShotWriter> screen_shot_writer_;
-#endif  // defined(ENABLE_SCREENSHOT)
 
   // Keeps track of all messages containing render tree submissions that will
   // ultimately reference the |render_tree_combiner_| and the
@@ -520,10 +529,8 @@
   // Command handler object for setting media module config.
   base::ConsoleCommandManager::CommandHandler set_media_config_command_handler_;
 
-#if defined(ENABLE_SCREENSHOT)
   // Command handler object for screenshot command from the debug console.
   base::ConsoleCommandManager::CommandHandler screenshot_command_handler_;
-#endif  // defined(ENABLE_SCREENSHOT)
 
   base::optional<SuspendFuzzer> suspend_fuzzer_;
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
@@ -535,6 +542,9 @@
   // the splash screen is currently displayed.
   scoped_ptr<SplashScreen> splash_screen_;
 
+  // The qr code overlay to display qr codes on top of all layers.
+  scoped_ptr<overlay_info::QrCodeOverlay> qr_code_overlay_;
+
   // Reset when the browser is paused, signalled to resume.
   base::WaitableEvent has_resumed_;
 
@@ -606,6 +616,11 @@
   // to another (in which case it may need to clear its submission queue).
   int current_splash_screen_timeline_id_;
   int current_main_web_module_timeline_id_;
+
+  // Remember the first set value for JavaScript's GC threshold setting computed
+  // by automem.  We want this so that we can check that it never changes, since
+  // we do not have the ability to modify it after startup.
+  base::optional<int64_t> javascript_gc_threshold_in_bytes_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp
index fdaca10..1b8962f 100644
--- a/src/cobalt/browser/cobalt.gyp
+++ b/src/cobalt/browser/cobalt.gyp
@@ -56,7 +56,7 @@
       'variables': {
         'executable_name': 'cobalt',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
@@ -93,7 +93,7 @@
           'variables': {
             'executable_name': 'snapshot_app_stats',
           },
-          'includes': [ '../../starboard/build/deploy.gypi' ],
+          'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
         },
       ]
     }],
diff --git a/src/cobalt/browser/lib/cobalt.def b/src/cobalt/browser/lib/cobalt.def
index f94b88a..3ee0f29 100644
--- a/src/cobalt/browser/lib/cobalt.def
+++ b/src/cobalt/browser/lib/cobalt.def
@@ -8,7 +8,7 @@
     ; From starboard/shared/lib/exported/starboard_main.h:
     StarboardMain
 
-    ; From cobalt/network/lib/user_agent.h:
+    ; From cobalt/browser/lib/exported/user_agent.h:
     CbLibUserAgentSetPlatformNameSuffix
     CbLibUserAgentSetDeviceTypeOverride
     CbLibUserAgentSetBrandNameOverride
diff --git a/src/cobalt/network/lib/exported/user_agent.h b/src/cobalt/browser/lib/exported/user_agent.h
similarity index 90%
rename from src/cobalt/network/lib/exported/user_agent.h
rename to src/cobalt/browser/lib/exported/user_agent.h
index 0b72f83..ea79f94 100644
--- a/src/cobalt/network/lib/exported/user_agent.h
+++ b/src/cobalt/browser/lib/exported/user_agent.h
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,8 +15,8 @@
 // This file provides a simple API for exposing Starboard as a
 // library to another app.
 
-#ifndef COBALT_NETWORK_LIB_EXPORTED_USER_AGENT_H_
-#define COBALT_NETWORK_LIB_EXPORTED_USER_AGENT_H_
+#ifndef COBALT_BROWSER_LIB_EXPORTED_USER_AGENT_H_
+#define COBALT_BROWSER_LIB_EXPORTED_USER_AGENT_H_
 
 #include "starboard/export.h"
 #include "starboard/system.h"
@@ -49,4 +49,4 @@
 }  // extern "C"
 #endif
 
-#endif  // COBALT_NETWORK_LIB_EXPORTED_USER_AGENT_H_
+#endif  // COBALT_BROWSER_LIB_EXPORTED_USER_AGENT_H_
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc
index 889315d..8b93f5e 100644
--- a/src/cobalt/browser/memory_settings/auto_mem.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include "base/debug/trace_event.h"
 #include "base/optional.h"
 #include "base/stl_util.h"
 #include "base/string_number_conversions.h"
@@ -270,6 +271,7 @@
 AutoMem::AutoMem(const math::Size& ui_resolution,
                  const AutoMemSettings& command_line_settings,
                  const AutoMemSettings& build_settings) {
+  TRACE_EVENT0("cobalt::browser", "AutoMem::AutoMem()");
   ConstructSettings(ui_resolution, command_line_settings, build_settings);
 
   const int64_t target_cpu_memory =
diff --git a/src/cobalt/browser/memory_tracker/tool.cc b/src/cobalt/browser/memory_tracker/tool.cc
index 6789df7..0c57fe2 100644
--- a/src/cobalt/browser/memory_tracker/tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool.cc
@@ -22,6 +22,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/string_number_conversions.h"
 #include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h"
+#include "cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.h"
 #include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h"
 #include "cobalt/browser/memory_tracker/tool/log_writer_tool.h"
 #include "cobalt/browser/memory_tracker/tool/malloc_logger_tool.h"
@@ -31,7 +32,6 @@
 #include "cobalt/browser/memory_tracker/tool/print_tool.h"
 #include "cobalt/browser/memory_tracker/tool/tool_impl.h"
 #include "cobalt/browser/memory_tracker/tool/tool_thread.h"
-
 #include "nb/analytics/memory_tracker_helpers.h"
 #include "starboard/double.h"
 #include "starboard/log.h"
@@ -66,6 +66,7 @@
   kAllocationLogger,
   kLeakTracer,
   kJavascriptLeakTracer,
+  kInternalFragmentationTracer,
   kMallocStats,
   kMallocLogger,
 };
@@ -210,6 +211,11 @@
       "  format.\n",
       kJavascriptLeakTracer);
 
+  SwitchVal internal_fragmentation_tracer_tool(
+      "internal_fragmentation_tracer",
+      "  Traces internal fragmentation and stores it in CSV format.\n",
+      kInternalFragmentationTracer);
+
   SwitchVal malloc_stats_tool(
       "malloc_stats",
       "  Queries the allocation system for memory usage. This is the most\n"
@@ -236,6 +242,8 @@
   switch_map[ParseToolName(leak_tracing_tool.tool_name)] = leak_tracing_tool;
   switch_map[ParseToolName(js_leak_tracing_tool.tool_name)] =
       js_leak_tracing_tool;
+  switch_map[ParseToolName(internal_fragmentation_tracer_tool.tool_name)] =
+      internal_fragmentation_tracer_tool;
   switch_map[ParseToolName(malloc_logger_tool.tool_name)] =
       malloc_logger_tool;
 
@@ -394,6 +402,12 @@
       tool_ptr.reset(leak_finder.release());
       break;
     }
+    case kInternalFragmentationTracer: {
+      memory_tracker = MemoryTracker::Get();
+      memory_tracker->InstallGlobalTrackingHooks();
+      tool_ptr.reset(new InternalFragmentationTool());
+      break;
+    }
     case kMallocStats: {
       tool_ptr.reset(new MallocStatsTool);
       break;
@@ -408,10 +422,6 @@
       tool_ptr.reset(malloc_logger.release());
       break;
     }
-    default: {
-      SB_NOTREACHED() << "Unhandled case.";
-      break;
-    }
   }
 
   if (tool_ptr.get()) {
diff --git a/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h b/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h
index f901ff6..4d9e140 100644
--- a/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h
+++ b/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h
@@ -171,6 +171,22 @@
   TableData table_data_;
 };
 
+// Useful for tracking values in megabytes.
+class MemoryBytesHistogramCSV : public HistogramTableCSVBase<int64_t> {
+ public:
+  MemoryBytesHistogramCSV() : HistogramTableCSVBase<int64_t>(0) {}
+  std::string ValueToString(const int64_t& bytes) const override {
+    return ToMegabyteString(bytes);
+  }
+
+  static std::string ToMegabyteString(int64_t bytes) {
+    double megabytes = static_cast<double>(bytes) / (1024.0 * 1024.0);
+    char buff[128];
+    SbStringFormatF(buff, sizeof(buff), "%.1f", megabytes);
+    return std::string(buff);
+  }
+};
+
 }  // namespace memory_tracker
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.cc b/src/cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.cc
new file mode 100644
index 0000000..aa7c85a
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.cc
@@ -0,0 +1,205 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.h"
+// C++ headers.
+#include <algorithm>
+#include <map>
+#include <set>
+#include <vector>
+// Cobalt headers.
+#include "cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "nb/bit_cast.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+namespace {
+
+using nb::analytics::AllocationRecord;
+using nb::analytics::AllocationVisitor;
+using nb::analytics::MemoryTracker;
+
+class FragmentationProcessor : private AllocationVisitor {
+ public:
+  explicit FragmentationProcessor(uintptr_t page_size)
+      : page_size_(page_size) {}
+
+  void Process(MemoryTracker* tracker,
+               size_t* allocated_memory_in_pages,
+               size_t* used_memory_in_segments) {
+    memory_segments_.erase(memory_segments_.begin(), memory_segments_.end());
+    *allocated_memory_in_pages = 0;
+    *used_memory_in_segments = 0;
+    tracker->Accept(this);
+
+    // Note that because of the fine course locking used for
+    // the memory tracker, it's accepted that there can be
+    // an occasional overlapping memory segment, where the memory region
+    // was deallocated and then recycled during the segment collection phase.
+    // This will then show up as an overlapping region. This is resolved
+    // by selecting the first memory segment, and discarding any
+    // memory segments that overlap it.
+    // While this can skew results, the rate of overlaps in practice is so low
+    // that we can essentially ignore it without warning the user.
+    std::sort(memory_segments_.begin(), memory_segments_.end());
+    std::vector<Segment> copy_no_overlaps;
+    copy_no_overlaps.reserve(memory_segments_.size());
+    for (const Segment& seg : memory_segments_) {
+      if (copy_no_overlaps.empty() ||
+          !seg.Intersects(copy_no_overlaps.back())) {
+        copy_no_overlaps.push_back(seg);
+      }
+    }
+    memory_segments_.swap(copy_no_overlaps);
+
+    if (!memory_segments_.empty()) {
+      std::set<uintptr_t> page_ids;
+
+      std::vector<Segment> sub_segments;
+      for (const Segment& seg : memory_segments_) {
+        if (!seg.size()) {  // Ignore 0-size segments.
+          continue;
+        }
+        // Use erase() instead of clear(), because it has stronger
+        // guarantees about recycling memory in the vector.
+        sub_segments.erase(sub_segments.begin(), sub_segments.end());
+        // Memory allocation segments may span multiple pages. In this
+        // case we will break them up and store them in sub_segments.
+        seg.SplitAcrossPageBoundaries(page_size_, &sub_segments);
+
+        for (const Segment& sub_seg : sub_segments) {
+          uintptr_t page_id = GetPageId(sub_seg.begin());
+          page_ids.insert(page_id);
+        }
+      }
+      *allocated_memory_in_pages = page_ids.size() * page_size_;
+    }
+    *used_memory_in_segments = CountUsedMemoryInSegments();
+  }
+
+ private:
+  // Implements AllocationVisitor::Visitor().
+  bool Visit(const void* memory,
+             const AllocationRecord& alloc_record) override {
+    const char* memory_begin = static_cast<const char*>(memory);
+    const char* memory_end = memory_begin + alloc_record.size;
+    memory_segments_.push_back(Segment(nullptr, memory_begin, memory_end));
+    return true;
+  }
+  uintptr_t GetPageId(const char* ptr) const {
+    return nb::bit_cast<uintptr_t>(ptr) / page_size_;
+  }
+  size_t CountUsedMemoryInSegments() const {
+    if (memory_segments_.empty()) {
+      return 0;
+    }
+    size_t total_bytes = 0;
+    const Segment* prev_segment = nullptr;
+    for (const Segment& seg : memory_segments_) {
+      size_t size = seg.size();
+      if (prev_segment && prev_segment->Intersects(seg)) {
+        // Sanity check - FragmentationProcessor::Process() is expected
+        // to resolve overlapping memory segments that occur from the
+        // concurrent nature of memory collection.
+        SB_NOTREACHED() << "Memory segments intersected!";
+        size = 0;
+      }
+      prev_segment = &seg;
+      total_bytes += size;
+    }
+    return total_bytes;
+  }
+
+  std::vector<Segment> memory_segments_;
+  const uintptr_t page_size_;
+};
+
+}  // namespace.
+
+InternalFragmentationTool::InternalFragmentationTool() {
+}
+
+InternalFragmentationTool::~InternalFragmentationTool() {
+}
+
+std::string InternalFragmentationTool::tool_name() const  {
+  return "InternalFragmentationTool";
+}
+
+void InternalFragmentationTool::Run(Params* params) {
+  // Run function does almost nothing.
+  params->logger()->Output("InternalFragmentationTool running...\n");
+
+  Timer output_timer(base::TimeDelta::FromSeconds(30));
+  Timer sample_timer(base::TimeDelta::FromMilliseconds(50));
+
+  MemoryBytesHistogramCSV histogram_table;
+  histogram_table.set_title("Internal Fragmentation - Probably undercounts");
+
+  FragmentationProcessor visitor(SB_MEMORY_PAGE_SIZE);
+
+  // If we get a finish signal then this will break out of the loop.
+  while (!params->wait_for_finish_signal(250 * kSbTimeMillisecond)) {
+    // LOG CSV.
+    if (output_timer.UpdateAndIsExpired()) {
+      std::stringstream ss;
+      ss << kNewLine << histogram_table.ToString() << kNewLine << kNewLine;
+      params->logger()->Output(ss.str());
+    }
+
+    // ADD A HISTOGRAM SAMPLE.
+    if (sample_timer.UpdateAndIsExpired()) {
+      histogram_table.BeginRow(params->time_since_start());
+
+      size_t allocated_memory_in_pages = 0;
+      size_t used_memory = 0;
+      visitor.Process(params->memory_tracker(),
+                      &allocated_memory_in_pages,
+                      &used_memory);
+
+      SB_DCHECK(allocated_memory_in_pages > used_memory)
+          << "allocated_memory_in_pages: " << allocated_memory_in_pages << ","
+          << "used_memory: " << used_memory;
+
+      histogram_table.AddRowValue("TotalReservedCpuMemoryInPages(MB)",
+                                  allocated_memory_in_pages);
+      histogram_table.AddRowValue("TotalCpuMemoryInUse(MB)",
+                                  used_memory);
+      histogram_table.FinalizeRow();
+    }
+
+    // COMPRESS TABLE WHEN FULL.
+    //
+    // Table is full, therefore eliminate half of the elements.
+    // Reduce sample frequency to match.
+    if (histogram_table.NumberOfRows() >= 100) {
+      // Compression step.
+      histogram_table.RemoveOddElements();
+
+      // By doubling the sampling time this keeps the table linear with
+      // respect to time. If sampling time was not doubled then there
+      // would be time distortion in the graph.
+      sample_timer.ScaleTriggerTime(2.0);
+      sample_timer.Restart();
+    }
+  }
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.h b/src/cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.h
new file mode 100644
index 0000000..1e14d84
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/internal_fragmentation_tool.h
@@ -0,0 +1,45 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_INTERNAL_FRAGMENTATION_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_INTERNAL_FRAGMENTATION_TOOL_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class InternalFragmentationTool : public AbstractTool {
+ public:
+  InternalFragmentationTool();
+  ~InternalFragmentationTool() override;
+  // Interface AbstractMemoryTrackerTool
+  std::string tool_name() const override;
+  void Run(Params* params) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InternalFragmentationTool);
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_INTERNAL_FRAGMENTATION_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.cc b/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.cc
index 4ee2c90..bdf235f 100644
--- a/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.cc
@@ -27,25 +27,6 @@
 namespace cobalt {
 namespace browser {
 namespace memory_tracker {
-namespace {
-
-// Takes in bytes -> outputs as megabytes.
-class MemoryBytesHistogramCSV : public HistogramTableCSVBase<int64_t> {
- public:
-  MemoryBytesHistogramCSV() : HistogramTableCSVBase<int64_t>(0) {}
-  std::string ValueToString(const int64_t& bytes) const override {
-    return ToMegabyteString(bytes);
-  }
-
-  static std::string ToMegabyteString(int64_t bytes) {
-    double mega_bytes = static_cast<double>(bytes) / (1024.0 * 1024.0);
-    char buff[128];
-    SbStringFormatF(buff, sizeof(buff), "%.1f", mega_bytes);
-    return std::string(buff);
-  }
-};
-
-}  // namespace.
 
 MallocStatsTool::MallocStatsTool() {
 }
diff --git a/src/cobalt/browser/memory_tracker/tool/util.cc b/src/cobalt/browser/memory_tracker/tool/util.cc
index 5e83b22..49bfe90 100644
--- a/src/cobalt/browser/memory_tracker/tool/util.cc
+++ b/src/cobalt/browser/memory_tracker/tool/util.cc
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "base/time.h"
+#include "nb/bit_cast.h"
 #include "starboard/string.h"
 
 namespace cobalt {
@@ -132,6 +133,75 @@
   time_before_expiration_ = base::TimeDelta::FromMicroseconds(new_dt);
 }
 
+Segment::Segment(const std::string* name,
+                 const char* start_address,
+                 const char* end_address)
+    : name_(name), begin_(start_address), end_(end_address) {
+}
+
+void Segment::SplitAcrossPageBoundaries(size_t page_size,
+                                        std::vector<Segment>* segments) const {
+  if (size() == 0) {
+    segments->push_back(*this);
+    return;
+  }
+
+  uintptr_t page_start =
+      nb::bit_cast<uintptr_t>(begin_) / page_size;
+  uintptr_t page_end =
+      nb::bit_cast<uintptr_t>(end_ - 1) / page_size;
+
+  if (page_start == page_end) {
+    segments->push_back(*this);
+    return;
+  }
+
+  for (uintptr_t p = page_start; p <= page_end; ++p) {
+    uintptr_t start_addr;
+    if (p == page_start) {
+      start_addr = nb::bit_cast<uintptr_t>(begin_);
+    } else {
+      start_addr = p * page_size;
+    }
+
+    uintptr_t end_addr;
+    if (p == page_end) {
+      end_addr = nb::bit_cast<uintptr_t>(end_);
+    } else {
+      end_addr = (p+1) * page_size;
+    }
+
+    const char* start = nb::bit_cast<const char*>(start_addr);
+    const char* end = nb::bit_cast<const char*>(end_addr);
+    segments->push_back(Segment(name_, start, end));
+  }
+}
+
+bool Segment::Intersects(const Segment& other) const {
+  size_t total_span = std::distance(
+      std::min(begin_, other.begin()),
+      std::max(end_, other.end()));
+
+  bool intersects = (size() + other.size()) > total_span;
+  return intersects;
+}
+
+bool Segment::operator<(const Segment& other) const {
+  return begin_ < other.begin();
+}
+
+bool Segment::operator==(const Segment& other) const {
+  if (begin_ == other.begin() && end_ == other.end()) {
+    DCHECK(name_ == other.name_);
+    return true;
+  }
+  return false;
+}
+
+size_t Segment::size() const {
+  return std::distance(begin_, end_);
+}
+
 const char* BaseNameFast(const char* file_name) {
   // Case: Linux.
   const char* end_pos = file_name + SbStringGetLength(file_name);
diff --git a/src/cobalt/browser/memory_tracker/tool/util.h b/src/cobalt/browser/memory_tracker/tool/util.h
index 00cb0ff..c9aeee5 100644
--- a/src/cobalt/browser/memory_tracker/tool/util.h
+++ b/src/cobalt/browser/memory_tracker/tool/util.h
@@ -162,6 +162,38 @@
   return true;
 }
 
+// Segment represents a named memory segment of allocated memory.
+// The name is optional and can be null.
+class Segment {
+ public:
+  // Equal name string values must have equal pointers.
+  Segment(const std::string* name,
+          const char* start_address, const char* end_address);
+
+  // Using the page_size, split this Segment into one Segment
+  // per page. Each of the sub_segments will copy the name
+  // pointer from this.
+  void SplitAcrossPageBoundaries(
+      size_t page_size,
+      std::vector<Segment>* sub_segments) const;
+
+  bool Intersects(const Segment& other) const;
+
+  bool operator<(const Segment& other) const;
+  bool operator==(const Segment& other) const;
+
+  size_t size() const;
+  const std::string* name() const { return name_; }
+
+  const char* begin() const { return begin_; }
+  const char* end() const { return end_; }
+
+ private:
+  const std::string* name_;
+  const char* begin_;
+  const char* end_;
+};
+
 // Returns a substring with the directory path removed from the filename.
 // Example:
 //   F::BaseNameFast("directory/filename.cc") => "filename.cc"
diff --git a/src/cobalt/browser/memory_tracker/tool/util_test.cc b/src/cobalt/browser/memory_tracker/tool/util_test.cc
index 988414d..b00bafd 100644
--- a/src/cobalt/browser/memory_tracker/tool/util_test.cc
+++ b/src/cobalt/browser/memory_tracker/tool/util_test.cc
@@ -19,6 +19,8 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "nb/bit_cast.h"
+#include "starboard/types.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -27,6 +29,13 @@
 namespace memory_tracker {
 namespace {
 
+const char* ToAddress(uintptr_t val) {
+  return nb::bit_cast<const char*>(val);
+}
+
+// 4k page size.
+static const uintptr_t kPageSize = 4096;
+
 class FakeTimeSource {
  public:
   explicit FakeTimeSource(base::TimeTicks value) : static_time_(value) {}
@@ -150,6 +159,76 @@
   EXPECT_FALSE(test_timer.UpdateAndIsExpired());
 }
 
+TEST(Segment, SplitAcrossPageBoundaries_Simple) {
+  std::string name("AllocName");
+  const char* start_addr = ToAddress(0);
+  const char* end_addr = ToAddress(1);
+
+  Segment memory_segment(&name, start_addr, end_addr);
+
+  std::vector<Segment> segments;
+  memory_segment.SplitAcrossPageBoundaries(kPageSize, &segments);
+  ASSERT_EQ(1, segments.size());
+
+  ASSERT_EQ(memory_segment, segments[0]);
+}
+
+TEST(Segment, SplitAcrossPageBoundaries_WholePage) {
+  std::string name("AllocName");
+  const char* start_addr = ToAddress(0);
+  const char* end_addr = ToAddress(kPageSize);
+
+  Segment memory_segment(&name, start_addr, end_addr);
+
+  std::vector<Segment> segments;
+  memory_segment.SplitAcrossPageBoundaries(kPageSize, &segments);
+  ASSERT_EQ(1, segments.size());
+
+  ASSERT_EQ(memory_segment, segments[0]);
+}
+
+TEST(Segment, SplitAcrossPageBoundaries_Across) {
+  std::string name("AllocName");
+  const char* start_addr = ToAddress(kPageSize / 2);
+  const char* mid_addr = ToAddress(kPageSize);
+  const char* end_addr = ToAddress(kPageSize + kPageSize / 2);
+
+  Segment memory_segment(&name, start_addr, end_addr);
+
+  std::vector<Segment> segments;
+  memory_segment.SplitAcrossPageBoundaries(kPageSize, &segments);
+
+  ASSERT_EQ(2, segments.size());
+
+  Segment s1(&name, start_addr, mid_addr);
+  Segment s2(&name, mid_addr, end_addr);
+
+  ASSERT_EQ(s1, segments[0]);
+  ASSERT_EQ(s2, segments[1]);
+}
+
+TEST(Segment, SplitAcrossPageBoundaries_Many) {
+  std::string name("AllocName");
+  const char* start_addr = ToAddress(kPageSize / 2);
+  const char* mid_0_addr = ToAddress(kPageSize);
+  const char* mid_1_addr = ToAddress(kPageSize * 2);
+  const char* end_addr = ToAddress(2 * kPageSize + kPageSize / 2);
+
+  Segment memory_segment(&name, start_addr, end_addr);
+
+  std::vector<Segment> segments;
+  memory_segment.SplitAcrossPageBoundaries(4096, &segments);
+  ASSERT_EQ(3, segments.size());
+
+  Segment s1(&name, start_addr, mid_0_addr);
+  Segment s2(&name, mid_0_addr, mid_1_addr);
+  Segment s3(&name, mid_1_addr, end_addr);
+
+  ASSERT_EQ(s1, segments[0]);
+  ASSERT_EQ(s2, segments[1]);
+  ASSERT_EQ(s3, segments[2]);
+}
+
 }  // namespace memory_tracker
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/render_tree_combiner.cc b/src/cobalt/browser/render_tree_combiner.cc
index 764ea25..e38c16f 100644
--- a/src/cobalt/browser/render_tree_combiner.cc
+++ b/src/cobalt/browser/render_tree_combiner.cc
@@ -44,6 +44,24 @@
   receipt_time_ = base::TimeTicks::HighResNow();
 }
 
+base::optional<renderer::Submission>
+RenderTreeCombiner::Layer::GetCurrentSubmission() {
+  if (!render_tree_) {
+    return base::nullopt;
+  }
+
+  base::optional<base::TimeDelta> current_time_offset = CurrentTimeOffset();
+  DCHECK(current_time_offset);
+  renderer::Submission submission(render_tree_->render_tree,
+                                  *current_time_offset);
+  submission.timeline_info = render_tree_->timeline_info;
+  submission.on_rasterized_callbacks.assign(
+      render_tree_->on_rasterized_callbacks.begin(),
+      render_tree_->on_rasterized_callbacks.end());
+
+  return submission;
+}
+
 base::optional<base::TimeDelta> RenderTreeCombiner::Layer::CurrentTimeOffset() {
   if (!receipt_time_) {
     return base::nullopt;
diff --git a/src/cobalt/browser/render_tree_combiner.h b/src/cobalt/browser/render_tree_combiner.h
index a61b540..00c370b 100644
--- a/src/cobalt/browser/render_tree_combiner.h
+++ b/src/cobalt/browser/render_tree_combiner.h
@@ -53,6 +53,11 @@
 
     bool HasRenderTree() { return !!render_tree_; }
 
+    // Returns a current submission object that can be passed into a renderer
+    // for rasterization.  If the render tree does not exist, this will
+    // return a base::nullopt.
+    base::optional<renderer::Submission> GetCurrentSubmission();
+
    private:
     friend class RenderTreeCombiner;
 
diff --git a/src/cobalt/browser/screen_shot_writer.cc b/src/cobalt/browser/screen_shot_writer.cc
index e534c76..fa01378 100644
--- a/src/cobalt/browser/screen_shot_writer.cc
+++ b/src/cobalt/browser/screen_shot_writer.cc
@@ -14,103 +14,104 @@
 
 #include "cobalt/browser/screen_shot_writer.h"
 
+#include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/file_util.h"
-#include "cobalt/renderer/test/png_utils/png_encode.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkPixelRef.h"
-
-using cobalt::renderer::test::png_utils::EncodeRGBAToBuffer;
+#include "cobalt/loader/image/image_encoder.h"
+#include "cobalt/render_tree/resource_provider_stub.h"
 
 namespace cobalt {
 namespace browser {
-namespace {
-scoped_array<uint8> WriteRGBAPixelsToPNG(scoped_array<uint8> pixel_data,
-                                         const math::Size& dimensions,
-                                         size_t* out_num_bytes) {
-  // Given a chunk of memory formatted as RGBA8 with pitch = width * 4, this
-  // function will wrap that memory in a SkBitmap that does *not* own the
-  // pixels and return that.
-  const int kRGBABytesPerPixel = 4;
-  SkBitmap bitmap;
-  bitmap.installPixels(
-      SkImageInfo::Make(dimensions.width(), dimensions.height(),
-                        kRGBA_8888_SkColorType, kUnpremul_SkAlphaType),
-      const_cast<uint8_t*>(pixel_data.get()),
-      dimensions.width() * kRGBABytesPerPixel);
-
-  // No conversion needed here, simply write out the pixels as is.
-  return EncodeRGBAToBuffer(static_cast<uint8_t*>(bitmap.pixelRef()->pixels()),
-                            bitmap.width(), bitmap.height(),
-                            static_cast<int>(bitmap.rowBytes()), out_num_bytes);
-}
-}  // namespace
 
 ScreenShotWriter::ScreenShotWriter(renderer::Pipeline* pipeline)
     : pipeline_(pipeline),
       screenshot_thread_("Screenshot IO thread") {
+  DCHECK(pipeline);
   base::Thread::Options options;
   options.message_loop_type = MessageLoop::TYPE_IO;
   screenshot_thread_.StartWithOptions(options);
 }
 
-void ScreenShotWriter::RequestScreenshot(const FilePath& output_path,
-                                         const base::Closure& complete) {
-  RequestScreenshotToMemory(base::Bind(&ScreenShotWriter::EncodingComplete,
-                                       base::Unretained(this), output_path,
-                                       complete));
+void ScreenShotWriter::RequestScreenshotToFile(
+    loader::image::EncodedStaticImage::ImageFormat desired_format,
+    const FilePath& output_path,
+    const scoped_refptr<render_tree::Node>& render_tree_root,
+    const base::Closure& complete) {
+  base::Callback<void(const scoped_refptr<loader::image::EncodedStaticImage>&)>
+      done_encoding_callback =
+          base::Bind(&ScreenShotWriter::WriteEncodedImageToFile,
+                     base::Unretained(this), output_path, complete);
+
+  renderer::Pipeline::RasterizationCompleteCallback callback =
+      base::Bind(&ScreenShotWriter::EncodeData, base::Unretained(this),
+                 desired_format, done_encoding_callback);
+  RequestScreenshotToMemoryUnencoded(render_tree_root, callback);
+}
+
+void ScreenShotWriter::RequestScreenshotToMemoryUnencoded(
+    const scoped_refptr<render_tree::Node>& render_tree_root,
+    const renderer::Pipeline::RasterizationCompleteCallback& callback) {
+  DCHECK(!callback.is_null());
+  pipeline_->RasterizeToRGBAPixels(
+      render_tree_root, base::Bind(&ScreenShotWriter::RunOnScreenshotThread,
+                                   base::Unretained(this), callback));
 }
 
 void ScreenShotWriter::RequestScreenshotToMemory(
-    const PNGEncodeCompleteCallback& callback) {
-  DCHECK(!callback.is_null());
-  DCHECK(last_submission_);
-  renderer::Submission submission(last_submission_.value());
-  submission.time_offset +=
-      base::TimeTicks::HighResNow() - last_submission_time_;
-  pipeline_->RasterizeToRGBAPixels(
-      submission, base::Bind(&ScreenShotWriter::RasterizationComplete,
-                             base::Unretained(this), callback));
+    loader::image::EncodedStaticImage::ImageFormat desired_format,
+    const scoped_refptr<render_tree::Node>& render_tree_root,
+    const ScreenShotWriter::ImageEncodeCompleteCallback& screenshot_ready) {
+  renderer::Pipeline::RasterizationCompleteCallback callback =
+      base::Bind(&ScreenShotWriter::EncodeData, base::Unretained(this),
+                 desired_format, screenshot_ready);
+  RequestScreenshotToMemoryUnencoded(render_tree_root, callback);
 }
 
-void ScreenShotWriter::SetLastPipelineSubmission(
-    const renderer::Submission& submission) {
-  DCHECK(submission.render_tree.get());
-  last_submission_ = submission;
-  last_submission_time_ = base::TimeTicks::HighResNow();
+void ScreenShotWriter::EncodeData(
+    loader::image::EncodedStaticImage::ImageFormat desired_format,
+    const base::Callback<
+        void(const scoped_refptr<loader::image::EncodedStaticImage>&)>&
+        done_encoding_callback,
+    scoped_array<uint8> pixel_data, const math::Size& image_dimensions) {
+  scoped_refptr<loader::image::EncodedStaticImage> image_data =
+      loader::image::CompressRGBAImage(desired_format, pixel_data.get(),
+                                       image_dimensions);
+  done_encoding_callback.Run(image_data);
 }
 
-void ScreenShotWriter::ClearLastPipelineSubmission() {
-  last_submission_ = base::nullopt;
-}
+void ScreenShotWriter::RunOnScreenshotThread(
+    const renderer::Pipeline::RasterizationCompleteCallback& callback,
+    scoped_array<uint8> image_data, const math::Size& image_dimensions) {
+  DCHECK(image_data);
 
-void ScreenShotWriter::RasterizationComplete(
-    const PNGEncodeCompleteCallback& encode_complete_callback,
-    scoped_array<uint8> pixel_data, const math::Size& dimensions) {
   if (MessageLoop::current() != screenshot_thread_.message_loop()) {
     screenshot_thread_.message_loop()->PostTask(
-        FROM_HERE, base::Bind(&ScreenShotWriter::RasterizationComplete,
-                              base::Unretained(this), encode_complete_callback,
-                              base::Passed(&pixel_data), dimensions));
+        FROM_HERE, base::Bind(&ScreenShotWriter::RunOnScreenshotThread,
+                              base::Unretained(this), callback,
+                              base::Passed(&image_data), image_dimensions));
     return;
   }
-  size_t num_bytes;
-  scoped_array<uint8> png_data =
-      WriteRGBAPixelsToPNG(pixel_data.Pass(), dimensions, &num_bytes);
 
-  encode_complete_callback.Run(png_data.Pass(), num_bytes);
+  callback.Run(image_data.Pass(), image_dimensions);
 }
 
-void ScreenShotWriter::EncodingComplete(const FilePath& output_path,
-                                        const base::Closure& complete_callback,
-                                        scoped_array<uint8> png_data,
-                                        size_t num_bytes) {
+void ScreenShotWriter::WriteEncodedImageToFile(
+    const FilePath& output_path, const base::Closure& complete_callback,
+    const scoped_refptr<loader::image::EncodedStaticImage>& image_data) {
   DCHECK_EQ(MessageLoop::current(), screenshot_thread_.message_loop());
+
   // Blocking write to output_path.
-  int bytes_written =
-      file_util::WriteFile(output_path, reinterpret_cast<char*>(png_data.get()),
-                           static_cast<int>(num_bytes));
-  DLOG_IF(ERROR, bytes_written != num_bytes) << "Error writing PNG to file.";
+  if (!image_data) {
+    DLOG(ERROR)
+        << "Unable to take screenshot because image data is unavailable.";
+  } else {
+    int num_bytes = static_cast<int>(image_data->GetEstimatedSizeInBytes());
+    int bytes_written = file_util::WriteFile(
+        output_path, reinterpret_cast<char*>(image_data->GetMemory()),
+        num_bytes);
+    LOG_IF(ERROR, bytes_written != num_bytes)
+        << "Error writing screenshot to file.";
+  }
 
   // Notify the caller that the screenshot is complete.
   if (!complete_callback.is_null()) {
diff --git a/src/cobalt/browser/screen_shot_writer.h b/src/cobalt/browser/screen_shot_writer.h
index 9fc2702..83f2f4a 100644
--- a/src/cobalt/browser/screen_shot_writer.h
+++ b/src/cobalt/browser/screen_shot_writer.h
@@ -18,7 +18,11 @@
 #include "base/callback.h"
 #include "base/file_path.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/string_piece.h"
 #include "base/threading/thread.h"
+#include "cobalt/dom/screenshot_manager.h"
+#include "cobalt/loader/image/image.h"
+#include "cobalt/render_tree/image.h"
 #include "cobalt/renderer/pipeline.h"
 #include "cobalt/renderer/submission.h"
 
@@ -31,55 +35,60 @@
 // File I/O is performed on a dedicated thread.
 class ScreenShotWriter {
  public:
-  typedef base::Callback<void(scoped_array<uint8>, size_t)>
-      PNGEncodeCompleteCallback;
+  using ImageEncodeCompleteCallback = base::Callback<void(
+      const scoped_refptr<loader::image::EncodedStaticImage>& image_data)>;
 
   // Constructs a new ScreenShotWriter that will create offscreen render targets
   // through |graphics_context|, and submit the most recent Pipeline::Submission
   // to |pipeline| to be rasterized.
   explicit ScreenShotWriter(renderer::Pipeline* pipeline);
 
-  // Creates a PNG at |output_path| from the most recently submitted
-  // Pipeline::Submission. When the PNG has been written to disk, |complete|
+  // Creates a screenshot at |output_path| from the most recently submitted
+  // Pipeline::Submission. When the file has been written to disk, |complete|
   // will be called.
-  void RequestScreenshot(const FilePath& output_path,
-                         const base::Closure& complete);
+  void RequestScreenshotToFile(
+      loader::image::EncodedStaticImage::ImageFormat desired_format,
+      const FilePath& output_path,
+      const scoped_refptr<render_tree::Node>& render_tree_root,
+      const base::Closure& complete);
 
-  // Creates a screenshot from the most recently submitted Pipeline::Submission
-  // and converts it to a PNG. |callback| will be called with the PNG data and
-  // the number of bytes in the array.
-  void RequestScreenshotToMemory(const PNGEncodeCompleteCallback& callback);
+  // Renders the |render_tree_root| and converts it to the image format that is
+  // requested. |callback| will be called with the image data.
+  void RequestScreenshotToMemory(
+      loader::image::EncodedStaticImage::ImageFormat desired_format,
+      const scoped_refptr<render_tree::Node>& render_tree_root,
+      const ImageEncodeCompleteCallback& callback);
 
-  // This should be called whenever a new render tree is produced. The render
-  // tree that is submitted here is the one that will be rasterized when a
-  // screenshot is requested.
-  void SetLastPipelineSubmission(const renderer::Submission& submission);
-
-  // Clears the last submission from the pipeline, putting the ScreenShotWriter
-  // into a state similar to when it was first created, where there was no
-  // "last pipeline submission".
-  void ClearLastPipelineSubmission();
+  // Runs callback on screenshot thread.
+  void RequestScreenshotToMemoryUnencoded(
+      const scoped_refptr<render_tree::Node>& render_tree_root,
+      const renderer::Pipeline::RasterizationCompleteCallback& callback);
 
  private:
   // Callback function that will be fired from the rasterizer thread when
   // rasterization of |last_submission_| is complete.
   // After converting the |pixel_data| to an in-memory PNG,
   // |encode_complete_callback| will be called.
-  void RasterizationComplete(
-      const PNGEncodeCompleteCallback& encode_complete_callback,
-      scoped_array<uint8> pixel_data, const math::Size& dimensions);
+  void RunOnScreenshotThread(
+      const renderer::Pipeline::RasterizationCompleteCallback& cb,
+      scoped_array<uint8> image_data, const math::Size& image_dimensions);
+
+  void EncodeData(loader::image::EncodedStaticImage::ImageFormat desired_format,
+                  const base::Callback<void(
+                      const scoped_refptr<loader::image::EncodedStaticImage>&)>&
+                      done_encoding_callback,
+                  scoped_array<uint8> pixel_data,
+                  const math::Size& image_dimensions);
 
   // Callback function that will be fired from the RasterizationComplete
-  // callback when PNG encoding is complete.
-  // |write_complete_cb| will be called when |png_data| has been completely
-  // written to |output_path|.
-  void EncodingComplete(const FilePath& output_path,
-                        const base::Closure& write_complete_cb,
-                        scoped_array<uint8> png_data, size_t num_bytes);
+  // callback when image encoding is complete.
+  // |write_complete_cb| will be called when data from |image_data| has been
+  // completely written to |output_path|.
+  void WriteEncodedImageToFile(
+      const FilePath& output_path, const base::Closure& complete_callback,
+      const scoped_refptr<loader::image::EncodedStaticImage>& image_data);
 
   renderer::Pipeline* pipeline_;
-  base::optional<renderer::Submission> last_submission_;
-  base::TimeTicks last_submission_time_;
 
   base::Thread screenshot_thread_;
 };
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 92ec183..f26b317 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -237,6 +237,11 @@
     "limit allows. It is recommended that enough memory be reserved for two "
     "RGBA atlases about a quarter of the frame size.";
 
+const char kQrCodeOverlay[] = "qr_code_overlay";
+const char kQrCodeOverlayHelp[] =
+    "Display QrCode based overlay information. These information can be used"
+    " for performance tuning or playback quality check.";
+
 const char kReduceCpuMemoryBy[] = "reduce_cpu_memory_by";
 const char kReduceCpuMemoryByHelp[] =
     "Reduces the cpu-memory of the system by this amount. This causes AutoMem "
@@ -359,6 +364,7 @@
         {kMaxCobaltGpuUsage, kMaxCobaltGpuUsageHelp},
         {kOffscreenTargetCacheSizeInBytes,
          kOffscreenTargetCacheSizeInBytesHelp},
+        {kQrCodeOverlay, kQrCodeOverlayHelp},
         {kReduceCpuMemoryBy, kReduceCpuMemoryByHelp},
         {kReduceGpuMemoryBy, kReduceGpuMemoryByHelp},
         {kRemoteTypefaceCacheSizeInBytes, kRemoteTypefaceCacheSizeInBytesHelp},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index b347192..56a7a00 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -110,6 +110,8 @@
 extern const char kMaxCobaltGpuUsageHelp[];
 extern const char kOffscreenTargetCacheSizeInBytes[];
 extern const char kOffscreenTargetCacheSizeInBytesHelp[];
+extern const char kQrCodeOverlay[];
+extern const char kQrCodeOverlayHelp[];
 extern const char kReduceCpuMemoryBy[];
 extern const char kReduceCpuMemoryByHelp[];
 extern const char kReduceGpuMemoryBy[];
diff --git a/src/cobalt/browser/user_agent_string.cc b/src/cobalt/browser/user_agent_string.cc
new file mode 100644
index 0000000..eaf091d
--- /dev/null
+++ b/src/cobalt/browser/user_agent_string.cc
@@ -0,0 +1,373 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/user_agent_string.h"
+
+#include <vector>
+
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#if defined(COBALT_ENABLE_LIB)
+#include "cobalt/browser/lib/exported/user_agent.h"
+#endif
+#include "cobalt/script/javascript_engine.h"
+#include "cobalt/version.h"
+#include "cobalt_build_id.h"  // NOLINT(build/include)
+#include "starboard/log.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+// Setup CobaltLib overrides for some user agent components.
+#if defined(COBALT_ENABLE_LIB)
+
+namespace {
+
+// Max length including null terminator.
+const size_t kUserAgentPlatformSuffixMaxLength = 128;
+char g_user_agent_platform_suffix[kUserAgentPlatformSuffixMaxLength] = {0};
+
+bool g_device_type_override_set = false;
+SbSystemDeviceType g_device_type_override = kSbSystemDeviceTypeUnknown;
+
+// Max length including null terminator.
+const size_t kPropertyMaxLength = 512;
+bool g_brand_name_override_set = false;
+char g_brand_name_override[kPropertyMaxLength] = {0};
+bool g_model_name_override_set = false;
+char g_model_name_override[kPropertyMaxLength] = {0};
+
+bool CopyStringAndTestIfSuccess(char* out_value, size_t value_length,
+                                const char* from_value) {
+  if (strlen(from_value) + 1 > value_length) return false;
+  base::strlcpy(out_value, from_value, value_length);
+  return true;
+}
+
+}  // namespace
+
+// Allow host app to append a suffix to the reported platform name.
+bool CbLibUserAgentSetPlatformNameSuffix(const char* suffix) {
+  if (!CopyStringAndTestIfSuccess(g_user_agent_platform_suffix,
+                                  kPropertyMaxLength, suffix)) {
+    // If the suffix is too large then nothing is appended to the platform.
+    g_user_agent_platform_suffix[0] = '\0';
+    return false;
+  }
+
+  return true;
+}
+
+bool CbLibUserAgentSetBrandNameOverride(const char* value) {
+  if (!value) {
+    g_brand_name_override_set = false;
+    return true;
+  }
+
+  if (CopyStringAndTestIfSuccess(g_brand_name_override, kPropertyMaxLength,
+                                 value)) {
+    g_brand_name_override_set = true;
+    return true;
+  }
+
+  // Failed to reset/set value.
+  return false;
+}
+
+bool CbLibUserAgentSetModelNameOverride(const char* value) {
+  if (!value) {
+    g_model_name_override_set = false;
+    return true;
+  }
+
+  if (CopyStringAndTestIfSuccess(g_model_name_override, kPropertyMaxLength,
+                                 value)) {
+    g_model_name_override_set = true;
+    return true;
+  }
+
+  // Failed to reset/set value.
+  return false;
+}
+
+bool CbLibUserAgentSetDeviceTypeOverride(SbSystemDeviceType device_type) {
+  switch (device_type) {
+    case kSbSystemDeviceTypeBlueRayDiskPlayer:
+    case kSbSystemDeviceTypeGameConsole:
+    case kSbSystemDeviceTypeOverTheTopBox:
+    case kSbSystemDeviceTypeSetTopBox:
+    case kSbSystemDeviceTypeTV:
+    case kSbSystemDeviceTypeDesktopPC:
+    case kSbSystemDeviceTypeAndroidTV:
+    case kSbSystemDeviceTypeUnknown:
+      g_device_type_override_set = true;
+      g_device_type_override = device_type;
+      return true;
+    default:
+      g_device_type_override_set = false;
+      return false;
+  }
+}
+
+#endif  // defined(COBALT_ENABLE_LIB)
+
+namespace cobalt {
+namespace browser {
+
+namespace {
+
+#if SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION
+const char kStarboardStabilitySuffix[] = "-Experimental";
+#elif defined(SB_RELEASE_CANDIDATE_API_VERSION) &&        \
+    SB_API_VERSION >= SB_RELEASE_CANDIDATE_API_VERSION && \
+    SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+const char kStarboardStabilitySuffix[] = "-ReleaseCandidate";
+#else
+const char kStarboardStabilitySuffix[] = "";
+#endif
+
+struct SanitizeReplacements {
+  const char* replace_chars;
+  const char* replace_with;
+} kSanitizeReplacements[] = {
+    {",", u8"\uFF0C"},  // fullwidth comma
+    {"_", u8"\u2E0F"},  // paragraphos
+    {"/", u8"\u2215"},  // division slash
+    {"(", u8"\uFF08"},  // fullwidth left paren
+    {")", u8"\uFF09"},  // fullwidth right paren
+};
+
+// Replace reserved characters with Unicode homoglyphs
+std::string Sanitize(const std::string& str) {
+  std::string clean(str);
+  for (size_t i = 0; i < arraysize(kSanitizeReplacements); i++) {
+    const SanitizeReplacements* replacement = kSanitizeReplacements + i;
+    ReplaceChars(clean, replacement->replace_chars, replacement->replace_with,
+                 &clean);
+  }
+  return clean;
+}
+
+std::string CreateDeviceTypeString(SbSystemDeviceType device_type) {
+  switch (device_type) {
+    case kSbSystemDeviceTypeBlueRayDiskPlayer:
+      return "BDP";
+    case kSbSystemDeviceTypeGameConsole:
+      return "GAME";
+    case kSbSystemDeviceTypeOverTheTopBox:
+      return "OTT";
+    case kSbSystemDeviceTypeSetTopBox:
+      return "STB";
+    case kSbSystemDeviceTypeTV:
+      return "TV";
+    case kSbSystemDeviceTypeAndroidTV:
+      return "ATV";
+    case kSbSystemDeviceTypeDesktopPC:
+      return "DESKTOP";
+    case kSbSystemDeviceTypeUnknown:
+      return "UNKNOWN";
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
+std::string CreateConnectionTypeString(
+    const base::optional<SbSystemConnectionType>& connection_type) {
+  if (connection_type) {
+    switch (*connection_type) {
+      case kSbSystemConnectionTypeWired:
+        return "Wired";
+      case kSbSystemConnectionTypeWireless:
+        return "Wireless";
+      default:
+        NOTREACHED();
+    }
+  }
+
+  return "";
+}
+
+}  // namespace
+
+UserAgentPlatformInfo GetUserAgentPlatformInfoFromSystem() {
+  UserAgentPlatformInfo platform_info;
+
+  platform_info.starboard_version = base::StringPrintf(
+      "Starboard/%d%s", SB_API_VERSION, kStarboardStabilitySuffix);
+
+  const size_t kSystemPropertyMaxLength = 1024;
+  char value[kSystemPropertyMaxLength];
+  bool result;
+
+  result = SbSystemGetProperty(kSbSystemPropertyPlatformName, value,
+                               kSystemPropertyMaxLength);
+  SB_DCHECK(result);
+  platform_info.os_name_and_version = value;
+
+  // Fill platform info if it is a hardware TV device.
+  SbSystemDeviceType device_type = SbSystemGetDeviceType();
+
+  // Network operator
+  result = SbSystemGetProperty(kSbSystemPropertyNetworkOperatorName, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.network_operator = value;
+  }
+
+  platform_info.javascript_engine_version =
+      script::GetJavaScriptEngineNameAndVersion();
+
+  platform_info.cobalt_version = COBALT_VERSION;
+  platform_info.cobalt_build_version_number = COBALT_BUILD_VERSION_NUMBER;
+
+#if defined(COBALT_BUILD_TYPE_DEBUG)
+  platform_info.build_configuration = "debug";
+#elif defined(COBALT_BUILD_TYPE_DEVEL)
+  platform_info.build_configuration = "devel";
+#elif defined(COBALT_BUILD_TYPE_QA)
+  platform_info.build_configuration = "qa";
+#elif defined(COBALT_BUILD_TYPE_GOLD)
+  platform_info.build_configuration = "gold";
+#else
+#error Unknown build configuration.
+#endif
+
+#if SB_API_VERSION >= 5
+  result = SbSystemGetProperty(kSbSystemPropertyUserAgentAuxField, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.aux_field = value;
+  }
+#endif  // SB_API_VERSION >= 5
+
+  // Device Type
+  platform_info.device_type = device_type;
+
+  // Chipset model number
+  result = SbSystemGetProperty(kSbSystemPropertyChipsetModelNumber, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.chipset_model_number = value;
+  }
+
+  // Model year
+  result = SbSystemGetProperty(kSbSystemPropertyModelYear, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.model_year = value;
+  }
+
+  // Firmware version
+  result = SbSystemGetProperty(kSbSystemPropertyFirmwareVersion, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.firmware_version = value;
+  }
+
+  // Brand
+  result = SbSystemGetProperty(kSbSystemPropertyManufacturerName, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.brand = value;
+  }
+
+  // Model name
+  result = SbSystemGetProperty(kSbSystemPropertyModelName, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.model = value;
+  }
+
+  // Connection type
+  SbSystemConnectionType connection_type = SbSystemGetConnectionType();
+  if (connection_type != kSbSystemConnectionTypeUnknown) {
+    platform_info.connection_type = connection_type;
+  }
+
+#if defined(COBALT_ENABLE_LIB)
+  if (g_user_agent_platform_suffix[0] != '\0') {
+    platform_info.os_name_and_version += "; ";
+    platform_info.os_name_and_version += g_user_agent_platform_suffix;
+  }
+
+  // Check for overrided values, to see if a client of CobaltLib has explicitly
+  // requested that some values be overridden.
+  if (g_device_type_override_set) {
+    platform_info.device_type = g_device_type_override;
+  }
+  if (g_brand_name_override_set) {
+    platform_info.brand = g_brand_name_override;
+  }
+  if (g_model_name_override_set) {
+    platform_info.model = g_model_name_override;
+  }
+#endif  // defined(COBALT_ENABLE_LIB)
+
+  return platform_info;
+}
+
+// This function is expected to be deterministic and non-dependent on global
+// variables and state.  If global state should be referenced, it should be done
+// so during the creation of |platform_info| instead.
+std::string CreateUserAgentString(const UserAgentPlatformInfo& platform_info) {
+  // Cobalt's user agent contains the following sections:
+  //   Mozilla/5.0 (ChromiumStylePlatform)
+  //   Cobalt/Version.BuildNumber-BuildConfiguration (unlike Gecko)
+  //   JavaScript Engine Name/Version
+  //   Starboard/APIVersion,
+  //   Device/FirmwareVersion (Brand, Model, ConnectionType)
+
+  //   Mozilla/5.0 (ChromiumStylePlatform)
+  std::string user_agent = base::StringPrintf(
+      "Mozilla/5.0 (%s)", platform_info.os_name_and_version.c_str());
+
+  //   Cobalt/Version.BuildNumber-BuildConfiguration (unlike Gecko)
+  base::StringAppendF(&user_agent, " Cobalt/%s.%s-%s (unlike Gecko)",
+                      platform_info.cobalt_version.c_str(),
+                      platform_info.cobalt_build_version_number.c_str(),
+                      platform_info.build_configuration.c_str());
+
+  //   JavaScript Engine Name/Version
+  if (!platform_info.javascript_engine_version.empty()) {
+    base::StringAppendF(&user_agent, " %s",
+                        platform_info.javascript_engine_version.c_str());
+  }
+
+  //   Starboard/APIVersion,
+  if (!platform_info.starboard_version.empty()) {
+    base::StringAppendF(&user_agent, " %s",
+                        platform_info.starboard_version.c_str());
+  }
+
+  //   Device/FirmwareVersion (Brand, Model, ConnectionType)
+  base::StringAppendF(
+      &user_agent, ", %s_%s_%s_%s/%s (%s, %s, %s)",
+      Sanitize(platform_info.network_operator.value_or("")).c_str(),
+      CreateDeviceTypeString(platform_info.device_type).c_str(),
+      Sanitize(platform_info.chipset_model_number.value_or("")).c_str(),
+      Sanitize(platform_info.model_year.value_or("")).c_str(),
+      Sanitize(platform_info.firmware_version.value_or("")).c_str(),
+      Sanitize(platform_info.brand.value_or("")).c_str(),
+      Sanitize(platform_info.model.value_or("")).c_str(),
+      CreateConnectionTypeString(platform_info.connection_type).c_str());
+
+  if (!platform_info.aux_field.empty()) {
+    user_agent.append(" ");
+    user_agent.append(platform_info.aux_field);
+  }
+  return user_agent;
+}
+
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/user_agent_string.h b/src/cobalt/browser/user_agent_string.h
new file mode 100644
index 0000000..900c8a9
--- /dev/null
+++ b/src/cobalt/browser/user_agent_string.h
@@ -0,0 +1,59 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_USER_AGENT_STRING_H_
+#define COBALT_BROWSER_USER_AGENT_STRING_H_
+
+#include <string>
+
+#include "base/optional.h"
+
+namespace cobalt {
+namespace browser {
+
+struct UserAgentPlatformInfo {
+  UserAgentPlatformInfo() : device_type(kSbSystemDeviceTypeUnknown) {}
+
+  std::string starboard_version;
+  std::string os_name_and_version;
+  base::optional<std::string> network_operator;
+  SbSystemDeviceType device_type;
+  base::optional<std::string> chipset_model_number;
+  base::optional<std::string> model_year;
+  base::optional<std::string> firmware_version;
+  base::optional<std::string> brand;
+  base::optional<std::string> model;
+  std::string aux_field;
+  base::optional<SbSystemConnectionType> connection_type;
+  std::string javascript_engine_version;
+
+  std::string cobalt_version;
+  std::string cobalt_build_version_number;
+  std::string build_configuration;
+};
+
+// Function that will query Starboard and populate a UserAgentPlatformInfo
+// structure based on those results.  This is de-coupled from
+// CreateUserAgentString() so that the common logic in CreateUserAgentString()
+// can be easily unit tested.
+UserAgentPlatformInfo GetUserAgentPlatformInfoFromSystem();
+
+// This function is deterministic and non-dependent on global variables and
+// state.  It is dependent only on |platform_info|.
+std::string CreateUserAgentString(const UserAgentPlatformInfo& platform_info);
+
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_USER_AGENT_STRING_H_
diff --git a/src/cobalt/browser/user_agent_string_test.cc b/src/cobalt/browser/user_agent_string_test.cc
new file mode 100644
index 0000000..409c4c6
--- /dev/null
+++ b/src/cobalt/browser/user_agent_string_test.cc
@@ -0,0 +1,148 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/user_agent_string.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+
+namespace {
+
+UserAgentPlatformInfo CreateOnlyOSNameAndVersionPlatformInfo() {
+  UserAgentPlatformInfo platform_info;
+  platform_info.os_name_and_version = "GLaDOS 3.11";
+  return platform_info;
+}
+
+TEST(UserAgentStringFactoryTest, StartsWithMozilla) {
+  std::string user_agent_string =
+      CreateUserAgentString(CreateOnlyOSNameAndVersionPlatformInfo());
+  EXPECT_EQ(0, user_agent_string.find("Mozilla/5.0"));
+}
+
+TEST(UserAgentStringFactoryTest, ContainsCobalt) {
+  std::string user_agent_string =
+      CreateUserAgentString(CreateOnlyOSNameAndVersionPlatformInfo());
+  EXPECT_NE(std::string::npos, user_agent_string.find("Cobalt"));
+}
+
+TEST(UserAgentStringFactoryTest, WithOSNameAndVersion) {
+  std::string user_agent_string =
+      CreateUserAgentString(CreateOnlyOSNameAndVersionPlatformInfo());
+  EXPECT_NE(std::string::npos, user_agent_string.find("(GLaDOS 3.11)"));
+}
+
+TEST(UserAgentStringFactoryTest, WithCobaltVersionAndConfiguration) {
+  UserAgentPlatformInfo platform_info;
+  platform_info.os_name_and_version = "GLaDOS 3.11";
+  platform_info.cobalt_version = "16";
+  platform_info.cobalt_build_version_number = "123456";
+  platform_info.build_configuration = "gold";
+  std::string user_agent_string = CreateUserAgentString(platform_info);
+
+  const char* tv_info_str = "Cobalt/16.123456-gold (unlike Gecko)";
+  EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
+}
+
+// Look-alike replacements expected from sanitizing fields
+#define COMMA u8"\uFF0C"   // fullwidth comma
+#define UNDER u8"\u2E0F"   // paragraphos
+#define SLASH u8"\u2215"   // division slash
+#define LPAREN u8"\uFF08"  // fullwidth left paren
+#define RPAREN u8"\uFF09"  // fullwidth right paren
+
+TEST(UserAgentStringFactoryTest, WithPlatformInfo) {
+  // There are deliberately a variety of underscores, commas, slashes, and
+  // parentheses in the strings below to ensure they get sanitized.
+  UserAgentPlatformInfo platform_info;
+  platform_info.os_name_and_version = "GLaDOS 3.11";
+  platform_info.network_operator = "Aperture_Science_Innovators";
+  platform_info.device_type = kSbSystemDeviceTypeOverTheTopBox;
+  platform_info.chipset_model_number = "P-body/Orange_Atlas/Blue";
+  platform_info.model_year = "2013";
+  platform_info.firmware_version = "0,01";
+  platform_info.brand = "Aperture Science (Labs)";
+  platform_info.model = "GLaDOS";
+  std::string user_agent_string = CreateUserAgentString(platform_info);
+
+  const char* tv_info_str =
+      "Aperture" UNDER "Science" UNDER
+      "Innovators"
+      "_OTT_"
+      "P-body" SLASH "Orange" UNDER "Atlas" SLASH
+      "Blue"
+      "_2013"
+      "/0" COMMA "01 (Aperture Science " LPAREN "Labs" RPAREN ", GLaDOS, )";
+  EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
+}
+
+TEST(UserAgentStringFactoryTest, WithWiredConnection) {
+  UserAgentPlatformInfo platform_info;
+  platform_info.os_name_and_version = "GLaDOS 3.11";
+  platform_info.connection_type = kSbSystemConnectionTypeWired;
+  platform_info.device_type = kSbSystemDeviceTypeOverTheTopBox;
+  std::string user_agent_string = CreateUserAgentString(platform_info);
+
+  EXPECT_NE(std::string::npos, user_agent_string.find("Wired"));
+}
+
+TEST(UserAgentStringFactoryTest, WithWirelessConnection) {
+  UserAgentPlatformInfo platform_info;
+  platform_info.os_name_and_version = "GLaDOS 3.11";
+  platform_info.connection_type = kSbSystemConnectionTypeWireless;
+  platform_info.device_type = kSbSystemDeviceTypeOverTheTopBox;
+  std::string user_agent_string = CreateUserAgentString(platform_info);
+
+  EXPECT_NE(std::string::npos, user_agent_string.find("Wireless"));
+}
+
+TEST(UserAgentStringFactoryTest, WithOnlyBrandModelAndDeviceType) {
+  UserAgentPlatformInfo platform_info;
+  platform_info.os_name_and_version = "GLaDOS 3.11";
+  platform_info.device_type = kSbSystemDeviceTypeOverTheTopBox;
+  platform_info.brand = "Aperture Science";
+  platform_info.model = "GLaDOS";
+  std::string user_agent_string = CreateUserAgentString(platform_info);
+
+  const char* tv_info_str = ", _OTT__/ (Aperture Science, GLaDOS, )";
+  EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
+}
+
+TEST(UserAgentStringFactoryTest, WithStarboardVersion) {
+  UserAgentPlatformInfo platform_info;
+  platform_info.starboard_version = "Starboard/6";
+  platform_info.device_type = kSbSystemDeviceTypeOverTheTopBox;
+  std::string user_agent_string = CreateUserAgentString(platform_info);
+
+  const char* tv_info_str = "Starboard/6, _OTT__/ (, , )";
+  EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
+}
+
+TEST(UserAgentStringFactoryTest, WithJavaScriptVersion) {
+  UserAgentPlatformInfo platform_info;
+  platform_info.starboard_version = "Starboard/6";
+  platform_info.javascript_engine_version = "V8/6.5.254.28";
+  platform_info.device_type = kSbSystemDeviceTypeOverTheTopBox;
+  std::string user_agent_string = CreateUserAgentString(platform_info);
+
+  const char* tv_info_str = "V8/6.5.254.28 Starboard/6, _OTT__/ (, , )";
+  EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
+}
+
+}  // namespace
+
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 180ba6a..d004068 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -219,7 +219,6 @@
       media::WebMediaPlayerFactory* web_media_player_factory);
   void SetImageCacheCapacity(int64_t bytes);
   void SetRemoteTypefaceCacheCapacity(int64_t bytes);
-  void SetJavascriptGcThreshold(int64_t bytes);
 
   // Sets the application state, asserts preconditions to transition to that
   // state, and dispatches any precipitate web events.
@@ -579,6 +578,8 @@
   global_environment_ = javascript_engine_->CreateGlobalEnvironment();
   DCHECK(global_environment_);
 
+  mutation_observer_task_manager_.RegisterAsTracingRoot(global_environment_);
+
   execution_state_ =
       script::ExecutionState::CreateExecutionState(global_environment_);
   DCHECK(execution_state_);
@@ -626,6 +627,7 @@
       base::Bind(&WebModule::Impl::OnStartDispatchEvent,
                  base::Unretained(this)),
       base::Bind(&WebModule::Impl::OnStopDispatchEvent, base::Unretained(this)),
+      data.options.provide_screenshot_function,
       data.options.csp_insecure_allowed_token, data.dom_max_element_depth,
       data.options.video_playback_rate_multiplier,
 #if defined(ENABLE_TEST_RUNNER)
@@ -649,6 +651,8 @@
       data.options.dom_settings_options));
   DCHECK(environment_settings_);
 
+  window_->SetEnvironmentSettings(environment_settings_.get());
+
   global_environment_->CreateGlobalObject(window_, environment_settings_.get());
 
   render_tree_produced_callback_ = data.render_tree_produced_callback;
@@ -725,6 +729,7 @@
   layout_manager_.reset();
   environment_settings_.reset();
   window_weak_.reset();
+  window_->ClearPointerStateForShutdown();
   window_ = NULL;
   media_source_registry_.reset();
   blob_registry_.reset();
@@ -1003,10 +1008,6 @@
   remote_typeface_cache_->SetCapacity(static_cast<uint32>(bytes));
 }
 
-void WebModule::Impl::SetJavascriptGcThreshold(int64_t bytes) {
-  javascript_engine_->SetGcThreshold(bytes);
-}
-
 void WebModule::Impl::SetSize(math::Size window_dimensions,
                               float video_pixel_ratio) {
   window_->SetSize(window_dimensions.width(), window_dimensions.height(),
@@ -1454,9 +1455,9 @@
                "WebModule::InjectCaptionSettingsChangedEvent()");
   DCHECK(message_loop());
   DCHECK(impl_);
-  message_loop()->PostTask(FROM_HERE,
-      base::Bind(&WebModule::Impl::InjectCaptionSettingsChangedEvent,
-                 base::Unretained(impl_.get())));
+  message_loop()->PostTask(
+      FROM_HERE, base::Bind(&WebModule::Impl::InjectCaptionSettingsChangedEvent,
+                            base::Unretained(impl_.get())));
 }
 
 std::string WebModule::ExecuteJavascript(
@@ -1566,12 +1567,6 @@
                             base::Unretained(impl_.get()), bytes));
 }
 
-void WebModule::SetJavascriptGcThreshold(int64_t bytes) {
-  message_loop()->PostTask(
-      FROM_HERE, base::Bind(&WebModule::Impl::SetJavascriptGcThreshold,
-                            base::Unretained(impl_.get()), bytes));
-}
-
 void WebModule::Prestart() {
   // Must only be called by a thread external from the WebModule thread.
   DCHECK_NE(MessageLoop::current(), message_loop());
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index cc70eab..82230f8 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -30,6 +30,7 @@
 #include "cobalt/base/console_commands.h"
 #include "cobalt/base/source_location.h"
 #include "cobalt/browser/lifecycle_observer.h"
+#include "cobalt/browser/screen_shot_writer.h"
 #include "cobalt/browser/splash_screen_cache.h"
 #include "cobalt/css_parser/parser.h"
 #if defined(ENABLE_DEBUG_CONSOLE)
@@ -45,6 +46,7 @@
 #include "cobalt/dom/media_source.h"
 #include "cobalt/dom/on_screen_keyboard_bridge.h"
 #include "cobalt/dom/pointer_event_init.h"
+#include "cobalt/dom/screenshot_manager.h"
 #include "cobalt/dom/wheel_event_init.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/dom_parser/parser.h"
@@ -54,6 +56,7 @@
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/media/web_media_player_factory.h"
 #include "cobalt/network/network_module.h"
+#include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
@@ -211,6 +214,18 @@
 
     // The dom::OnScreenKeyboard forwards calls to this interface.
     dom::OnScreenKeyboardBridge* on_screen_keyboard_bridge = NULL;
+
+    // This function takes in a render tree as input, and then calls the 2nd
+    // argument (which is another callback) when the screenshot is available.
+    // The callback's first parameter points to an unencoded image, where the
+    // format is R8G8B8A8 pixels (with no padding at the end of each row),
+    // and the second parameter is the dimensions of the image.
+    // Note that the callback could be called on a different thread, and is not
+    // guaranteed to be called on the caller thread.
+    // By using Callbacks here, it is easier to write tests, and use this
+    // functionality in Cobalt.
+    dom::ScreenshotManager::ProvideScreenshotFunctionCallback
+        provide_screenshot_function;
   };
 
   typedef layout::LayoutManager::LayoutResults LayoutResults;
@@ -301,7 +316,6 @@
       media::WebMediaPlayerFactory* web_media_player_factory);
   void SetImageCacheCapacity(int64_t bytes);
   void SetRemoteTypefaceCacheCapacity(int64_t bytes);
-  void SetJavascriptGcThreshold(int64_t bytes);
 
   // LifecycleObserver implementation
   void Prestart() override;
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 283ad91..3a22d93 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -57,6 +57,7 @@
         '<(DEPTH)/cobalt/media_session/media_session.gyp:*',
         '<(DEPTH)/cobalt/media_session/media_session_test.gyp:*',
         '<(DEPTH)/cobalt/network/network.gyp:*',
+        '<(DEPTH)/cobalt/overlay_info/overlay_info.gyp:*',
         '<(DEPTH)/cobalt/page_visibility/page_visibility.gyp:*',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:*',
         '<(DEPTH)/cobalt/renderer/renderer.gyp:*',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index f31b753..5ea67dc 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-154703
\ No newline at end of file
+162639
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index 4a08129..f75cede 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -353,19 +353,11 @@
     # a warning if the engine consumes more memory than this value specifies.
     'reduce_gpu_memory_by%': -1,
 
-    # The only currently-supported Javascript engine is 'mozjs-45'.
-    'default_javascript_engine': 'mozjs-45',
-    'javascript_engine%': '<(default_javascript_engine)',
-
-    # Disable JIT and run in interpreter-only mode by default. It can be set
-    # to 1 to run in JIT mode.  For SpiderMonkey in particular, we have found
-    # that disabling JIT often results in faster JavaScript execution and
-    # lower memory usage.
-    # Setting this to 1 on a platform or engine for which there is no JIT
-    # implementation is a no-op.
-    # Setting this to 0 on an engine for which there is a JIT implementation
-    # is a platform configuration error.
-    'cobalt_enable_jit%': 0,
+    # Note: Ideally, |javascript_engine| and |cobalt_enable_jit| would live
+    # here, however due to weird gyp variable usage in bindings gyp files
+    # (that if we removed, would result in large code duplication), we have to
+    # define them in the python gyp platform files instead.  See
+    # "starboard/build/platform_configuration.py" for their documentation.
 
     # Can be set to enable zealous garbage collection, if |javascript_engine|
     # supports it.  Zealous garbage collection will cause garbage collection
@@ -488,7 +480,6 @@
       'ENABLE_PARTIAL_LAYOUT_CONTROL',
       'ENABLE_TEST_DATA',
       'ENABLE_TEST_RUNNER',
-      '__LB_SHELL__ENABLE_SCREENSHOT__',
 
       # TODO: Rename to COBALT_LOGGING_ENABLED.
       '__LB_SHELL__FORCE_LOGGING__',
@@ -508,7 +499,6 @@
       'ENABLE_PARTIAL_LAYOUT_CONTROL',
       'ENABLE_TEST_DATA',
       'ENABLE_TEST_RUNNER',
-      '__LB_SHELL__ENABLE_SCREENSHOT__',
       '__LB_SHELL__FORCE_LOGGING__',
       'SK_DEVELOPER',
     ],
@@ -524,7 +514,6 @@
       'ENABLE_PARTIAL_LAYOUT_CONTROL',
       'ENABLE_TEST_DATA',
       'ENABLE_TEST_RUNNER',
-      '__LB_SHELL__ENABLE_SCREENSHOT__',
     ],
     'defines_gold': [
       'ALLOCATOR_STATS_TRACKING',
@@ -601,7 +590,6 @@
         'enable_fake_microphone': 1,
         'enable_network_logging': 1,
         'enable_remote_debugging%': 1,
-        'enable_screenshot': 1,
         'enable_webdriver%': 1,
       },
     },
@@ -612,7 +600,6 @@
         'enable_fake_microphone': 0,
         'enable_network_logging': 0,
         'enable_remote_debugging%': 0,
-        'enable_screenshot': 0,
         'enable_webdriver': 0,
       },
     }],
diff --git a/src/cobalt/build/config/BUILD.gn b/src/cobalt/build/config/BUILD.gn
index b54d544..bd345c8 100644
--- a/src/cobalt/build/config/BUILD.gn
+++ b/src/cobalt/build/config/BUILD.gn
@@ -108,7 +108,6 @@
     "ENABLE_PARTIAL_LAYOUT_CONTROL",
     "ENABLE_TEST_DATA",
     "ENABLE_TEST_RUNNER",
-    "__LB_SHELL__ENABLE_SCREENSHOT__",
     "__LB_SHELL__FORCE_LOGGING__",  # TODO: Rename to COBALT_LOGGING_ENABLED.
     "SK_DEVELOPER",
   ]
@@ -128,7 +127,6 @@
     "ENABLE_PARTIAL_LAYOUT_CONTROL",
     "ENABLE_TEST_DATA",
     "ENABLE_TEST_RUNNER",
-    "__LB_SHELL__ENABLE_SCREENSHOT__",
     "__LB_SHELL__FORCE_LOGGING__",
     "SK_DEVELOPER",
   ]
@@ -147,7 +145,6 @@
     "ENABLE_PARTIAL_LAYOUT_CONTROL",
     "ENABLE_TEST_DATA",
     "ENABLE_TEST_RUNNER",
-    "__LB_SHELL__ENABLE_SCREENSHOT__",
     "NDEBUG",
   ]
 }
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni
index 93b2d4c..cfcd816 100644
--- a/src/cobalt/build/config/base.gni
+++ b/src/cobalt/build/config/base.gni
@@ -573,9 +573,6 @@
 if (!defined(enable_remote_debugging)) {
   enable_remote_debugging = _copy_test_data
 }
-if (!defined(enable_screenshot)) {
-  enable_screenshot = _copy_test_data
-}
 if (!defined(enable_webdriver)) {
   enable_webdriver = _copy_test_data
 }
diff --git a/src/cobalt/csp/csp.gyp b/src/cobalt/csp/csp.gyp
index b10984d..4b08ced 100644
--- a/src/cobalt/csp/csp.gyp
+++ b/src/cobalt/csp/csp.gyp
@@ -74,7 +74,7 @@
       'variables': {
         'executable_name': 'csp_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
diff --git a/src/cobalt/css_parser/css_parser.gyp b/src/cobalt/css_parser/css_parser.gyp
index 24bf201..b90e3a7 100644
--- a/src/cobalt/css_parser/css_parser.gyp
+++ b/src/cobalt/css_parser/css_parser.gyp
@@ -145,7 +145,7 @@
       'variables': {
         'executable_name': 'css_parser_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
   ],
diff --git a/src/cobalt/cssom/cssom_test.gyp b/src/cobalt/cssom/cssom_test.gyp
index 318282a..6cac46a 100644
--- a/src/cobalt/cssom/cssom_test.gyp
+++ b/src/cobalt/cssom/cssom_test.gyp
@@ -71,7 +71,7 @@
       'variables': {
         'executable_name': 'cssom_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/cssom/radial_gradient_value.cc b/src/cobalt/cssom/radial_gradient_value.cc
index d1840ab..1be0bec 100644
--- a/src/cobalt/cssom/radial_gradient_value.cc
+++ b/src/cobalt/cssom/radial_gradient_value.cc
@@ -34,8 +34,6 @@
     case kEllipse:
       result.append(kEllipseKeywordName);
       break;
-    default:
-      NOTREACHED();
   }
 
   if (size_keyword_) {
@@ -53,8 +51,6 @@
       case kFarthestCorner:
         result.append(kFarthestCornerKeywordName);
         break;
-      default:
-        NOTREACHED();
     }
   } else if (size_value_) {
     result.push_back(' ');
diff --git a/src/cobalt/debug/debug_script_runner.cc b/src/cobalt/debug/debug_script_runner.cc
index cb01a64..2d6ba50 100644
--- a/src/cobalt/debug/debug_script_runner.cc
+++ b/src/cobalt/debug/debug_script_runner.cc
@@ -82,8 +82,7 @@
       script::SourceCode::CreateSourceCode(js_code, GetInlineSourceLocation());
 
   ForceEnableEval();
-  bool succeeded = global_environment_->EvaluateScript(
-      source_code, false /*mute_errors*/, result);
+  bool succeeded = global_environment_->EvaluateScript(source_code, result);
   SetEvalAllowedFromCsp();
   return succeeded;
 }
diff --git a/src/cobalt/doc/gyp_to_gn.md b/src/cobalt/doc/gyp_to_gn.md
index c7fbc74..b1a8dbd 100644
--- a/src/cobalt/doc/gyp_to_gn.md
+++ b/src/cobalt/doc/gyp_to_gn.md
@@ -303,7 +303,7 @@
       'variables': {
         'executable_name': 'target',
       },
-      'includes': [ '../build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
 with
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
index 2d5d184..cbdc79d 100644
--- a/src/cobalt/dom/custom_event_test.cc
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -27,7 +27,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media_session/media_session.h"
-#include "cobalt/network/network_module.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/source_code.h"
@@ -56,7 +55,7 @@
         message_loop_(MessageLoop::TYPE_DEFAULT),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_error_callback_)),
-        fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+        fetcher_factory_(new loader::FetcherFactory(NULL)),
         local_storage_database_(NULL),
         url_("about:blank"),
         window_(new Window(
@@ -73,7 +72,8 @@
             dom::Window::CloseCallback() /* window_close */,
             base::Closure() /* window_minimize */, NULL, NULL, NULL,
             dom::Window::OnStartDispatchEventCallback(),
-            dom::Window::OnStopDispatchEventCallback())) {
+            dom::Window::OnStopDispatchEventCallback(),
+            dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {
     engine_ = script::JavaScriptEngine::CreateEngine();
     global_environment_ = engine_->CreateGlobalEnvironment();
     global_environment_->CreateGlobalObject(window_,
@@ -91,7 +91,6 @@
   MockErrorCallback mock_error_callback_;
   scoped_ptr<css_parser::Parser> css_parser_;
   scoped_ptr<dom_parser::Parser> dom_parser_;
-  network::NetworkModule network_module_;
   scoped_ptr<loader::FetcherFactory> fetcher_factory_;
   dom::LocalStorageDatabase local_storage_database_;
   GURL url_;
@@ -108,8 +107,7 @@
 
   global_environment_->EnableEval();
   global_environment_->SetReportEvalCallback(base::Closure());
-  bool succeeded = global_environment_->EvaluateScript(
-      source_code, false /*mute_errors*/, result);
+  bool succeeded = global_environment_->EvaluateScript(source_code, result);
   return succeeded;
 }
 }  // namespace
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index 545505f..08c0146 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -562,6 +562,20 @@
   }
 }
 
+scoped_refptr<render_tree::Node>
+Document::DoSynchronousLayoutAndGetRenderTree() {
+  TRACE_EVENT0("cobalt::dom",
+               "Document::DoSynchronousLayoutAndGetRenderTree()");
+
+  if (synchronous_layout_and_produce_render_tree_callback_.is_null()) {
+    DLOG(WARNING)
+        << "|synchronous_layout_and_produce_render_tree_callback_| is null";
+    return nullptr;
+  }
+
+  return synchronous_layout_and_produce_render_tree_callback_.Run();
+}
+
 void Document::NotifyUrlChanged(const GURL& url) {
   location_->set_url(url);
   csp_delegate_->NotifyUrlChanged(url);
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index f79fc3f..ffca957 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -343,11 +343,19 @@
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
 
   // Triggers a synchronous layout.
+  scoped_refptr<render_tree::Node> DoSynchronousLayoutAndGetRenderTree();
   void DoSynchronousLayout();
+
   void set_synchronous_layout_callback(
       const base::Closure& synchronous_layout_callback) {
     synchronous_layout_callback_ = synchronous_layout_callback;
   }
+  void set_synchronous_layout_and_produce_render_tree_callback(
+      const base::Callback<scoped_refptr<render_tree::Node>()>&
+          synchronous_layout_and_produce_render_tree_callback) {
+    synchronous_layout_and_produce_render_tree_callback_ =
+        synchronous_layout_and_produce_render_tree_callback;
+  }
 
   math::Size viewport_size() { return viewport_size_.value_or(math::Size()); }
   void SetViewport(const math::Size& viewport_size);
@@ -494,6 +502,9 @@
   const scoped_refptr<base::Clock> navigation_start_clock_;
   scoped_refptr<DocumentTimeline> default_timeline_;
 
+  base::Callback<scoped_refptr<render_tree::Node>()>
+      synchronous_layout_and_produce_render_tree_callback_;
+
   base::Closure synchronous_layout_callback_;
 
   scoped_refptr<cssom::CSSStyleSheet> user_agent_style_sheet_;
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 5a89d8c..793fa82 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -239,6 +239,10 @@
         'rule_matching.cc',
         'rule_matching.h',
         'screen.h',
+        'screenshot.cc',
+        'screenshot.h',
+        'screenshot_manager.cc',
+        'screenshot_manager.h',
         'security_policy_violation_event.cc',
         'security_policy_violation_event.h',
         'serializer.cc',
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 14a615a..3c0b534 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -77,6 +77,7 @@
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
         '<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
+        '<(DEPTH)/cobalt/loader/loader.gyp:loader',
         '<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
@@ -94,7 +95,7 @@
       'variables': {
         'executable_name': 'dom_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/dom/eme/media_key_status_map.cc b/src/cobalt/dom/eme/media_key_status_map.cc
index 50063c1..a950197 100644
--- a/src/cobalt/dom/eme/media_key_status_map.cc
+++ b/src/cobalt/dom/eme/media_key_status_map.cc
@@ -50,8 +50,6 @@
       return "status-pending";
     case kMediaKeyStatusInternalError:
       return "internal-error";
-    default:
-      break;
   }
 
   NOTREACHED();
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
index 6b30fb1..82ad15f 100644
--- a/src/cobalt/dom/error_event_test.cc
+++ b/src/cobalt/dom/error_event_test.cc
@@ -27,7 +27,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media_session/media_session.h"
-#include "cobalt/network/network_module.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/source_code.h"
@@ -56,7 +55,7 @@
         message_loop_(MessageLoop::TYPE_DEFAULT),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_error_callback_)),
-        fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+        fetcher_factory_(new loader::FetcherFactory(NULL)),
         local_storage_database_(NULL),
         url_("about:blank"),
         window_(new Window(
@@ -73,7 +72,8 @@
             dom::Window::CloseCallback() /* window_close */,
             base::Closure() /* window_minimize */, NULL, NULL, NULL,
             dom::Window::OnStartDispatchEventCallback(),
-            dom::Window::OnStopDispatchEventCallback())) {
+            dom::Window::OnStopDispatchEventCallback(),
+            dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {
     engine_ = script::JavaScriptEngine::CreateEngine();
     global_environment_ = engine_->CreateGlobalEnvironment();
     global_environment_->CreateGlobalObject(window_,
@@ -91,7 +91,6 @@
   MockErrorCallback mock_error_callback_;
   scoped_ptr<css_parser::Parser> css_parser_;
   scoped_ptr<dom_parser::Parser> dom_parser_;
-  network::NetworkModule network_module_;
   scoped_ptr<loader::FetcherFactory> fetcher_factory_;
   dom::LocalStorageDatabase local_storage_database_;
   GURL url_;
@@ -108,8 +107,7 @@
 
   global_environment_->EnableEval();
   global_environment_->SetReportEvalCallback(base::Closure());
-  bool succeeded = global_environment_->EvaluateScript(
-      source_code, false /*mute_errors*/, result);
+  bool succeeded = global_environment_->EvaluateScript(source_code, result);
   return succeeded;
 }
 }  // namespace
diff --git a/src/cobalt/dom/event_target.cc b/src/cobalt/dom/event_target.cc
index caa6667..b76eeef 100644
--- a/src/cobalt/dom/event_target.cc
+++ b/src/cobalt/dom/event_target.cc
@@ -162,10 +162,6 @@
   return false;
 }
 
-bool EventTarget::ShouldKeepWrapperAlive() {
-  return !event_listener_infos_.empty();
-}
-
 void EventTarget::FireEventOnListeners(const scoped_refptr<Event>& event) {
   DCHECK(event->IsBeingDispatched());
   DCHECK(event->target());
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h
index 9677b92..a89dbe5 100644
--- a/src/cobalt/dom/event_target.h
+++ b/src/cobalt/dom/event_target.h
@@ -396,10 +396,6 @@
   // Return true if one or more event listeners are registered
   bool HasOneOrMoreAttributeEventListener() const;
 
-  // script::Wrappable
-  //
-  bool ShouldKeepWrapperAlive() override;
-
   // Returns a string that represents the target for debug purpose.
   virtual std::string GetDebugName() { return ""; }
 
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index e82e801..6ddf6cf 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -664,7 +664,7 @@
   }
 
   // Do not update computed style for descendants of "display: none" elements,
-  // since they do not participate in layout. Note the "display: node" elements
+  // since they do not participate in layout. Note the "display: none" elements
   // themselves still need to have their computed style updated, in case the
   // value of display is changed.
   if (computed_style()->display() == cssom::KeywordValue::GetNone()) {
@@ -706,6 +706,14 @@
     descendant_computed_styles_valid_ = false;
   }
 
+  // Also clear out all animations/transitions on pseudo elements.
+  for (auto& pseudo_element : pseudo_elements_) {
+    if (pseudo_element) {
+      pseudo_element->css_transitions()->Clear();
+      pseudo_element->css_animations()->Clear();
+    }
+  }
+
   MarkDisplayNoneOnDescendants();
 }
 
@@ -735,30 +743,57 @@
 }
 
 void HTMLElement::InvalidateLayoutBoxesOfNodeAndAncestors() {
-  layout_boxes_.reset();
+  InvalidateLayoutBoxes();
   InvalidateLayoutBoxesOfAncestors();
 }
 
 void HTMLElement::InvalidateLayoutBoxesOfNodeAndDescendants() {
-  layout_boxes_.reset();
+  InvalidateLayoutBoxes();
   InvalidateLayoutBoxesOfDescendants();
 }
 
 void HTMLElement::InvalidateLayoutBoxSizes() {
   if (layout_boxes_) {
     layout_boxes_->InvalidateSizes();
+
+    for (auto& pseudo_element : pseudo_elements_) {
+      if (pseudo_element && pseudo_element->layout_boxes()) {
+        pseudo_element->layout_boxes()->InvalidateSizes();
+      }
+    }
   }
 }
 
 void HTMLElement::InvalidateLayoutBoxCrossReferences() {
   if (layout_boxes_) {
     layout_boxes_->InvalidateCrossReferences();
+
+    for (auto& pseudo_element : pseudo_elements_) {
+      if (pseudo_element && pseudo_element->layout_boxes()) {
+        pseudo_element->layout_boxes()->InvalidateCrossReferences();
+      }
+    }
   }
 }
 
 void HTMLElement::InvalidateLayoutBoxRenderTreeNodes() {
   if (layout_boxes_) {
     layout_boxes_->InvalidateRenderTreeNodes();
+
+    for (auto& pseudo_element : pseudo_elements_) {
+      if (pseudo_element && pseudo_element->layout_boxes()) {
+        pseudo_element->layout_boxes()->InvalidateRenderTreeNodes();
+      }
+    }
+  }
+}
+
+void HTMLElement::InvalidateLayoutBoxes() {
+  layout_boxes_.reset();
+  for (auto& pseudo_element : pseudo_elements_) {
+    if (pseudo_element) {
+      pseudo_element->reset_layout_boxes();
+    }
   }
 }
 
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 2568d30..23e23df 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -340,6 +340,11 @@
   // Purge the cached background images on only this node.
   void PurgeCachedBackgroundImages();
 
+  // Helper function that hosts the shared code between
+  // InvalidateLayoutBoxesOfNodeAndAncestors() and
+  // InvalidateLayoutBoxesOfNodeAndDescendants().
+  void InvalidateLayoutBoxes();
+
   bool locked_for_focus_;
 
   // The directionality of the html element is determined by the 'dir'
diff --git a/src/cobalt/dom/mutation_observer_task_manager.cc b/src/cobalt/dom/mutation_observer_task_manager.cc
index 01de24d..235db66 100644
--- a/src/cobalt/dom/mutation_observer_task_manager.cc
+++ b/src/cobalt/dom/mutation_observer_task_manager.cc
@@ -27,6 +27,7 @@
   DCHECK(observers_.find(observer) == observers_.end());
   observers_.insert(observer);
 }
+
 void MutationObserverTaskManager::OnMutationObserverDestroyed(
     MutationObserver* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -52,6 +53,10 @@
                  base::Unretained(this)));
 }
 
+void MutationObserverTaskManager::TraceMembers(script::Tracer* tracer) {
+  tracer->TraceItems(observers_);
+}
+
 void MutationObserverTaskManager::NotifyMutationObservers() {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(task_posted_);
diff --git a/src/cobalt/dom/mutation_observer_task_manager.h b/src/cobalt/dom/mutation_observer_task_manager.h
index 88341f1..dbfeacc 100644
--- a/src/cobalt/dom/mutation_observer_task_manager.h
+++ b/src/cobalt/dom/mutation_observer_task_manager.h
@@ -19,6 +19,8 @@
 
 #include "base/hash_tables.h"
 #include "base/threading/thread_checker.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/tracer.h"
 
 namespace cobalt {
 namespace dom {
@@ -32,10 +34,16 @@
 // The spec expects an EventLoop implementation, which Cobalt does not currently
 // have.
 // https://www.w3.org/TR/dom/#mutation-observers
-class MutationObserverTaskManager {
+class MutationObserverTaskManager : public script::Traceable {
  public:
   MutationObserverTaskManager() : task_posted_(false) {}
 
+  void RegisterAsTracingRoot(script::GlobalEnvironment* global_environment) {
+    // Note that we only add ourselves, and never remove ourselves, as we will
+    // actually outlive the web module.
+    global_environment->AddRoot(this);
+  }
+
   // These should be called in the constructor/destructor of the
   // MutationObserver.
   void OnMutationObserverCreated(MutationObserver* observer);
@@ -44,12 +52,14 @@
   // Post a task to notify mutation observers, if one is not already posted.
   void QueueMutationObserverMicrotask();
 
+  void TraceMembers(script::Tracer* tracer) override;
+
  private:
+  typedef base::hash_set<MutationObserver*> MutationObserverSet;
+
   // Notify all mutation observers.
   void NotifyMutationObservers();
 
-  typedef base::hash_set<MutationObserver*> MutationObserverSet;
-
   base::ThreadChecker thread_checker_;
   MutationObserverSet observers_;
   bool task_posted_;
diff --git a/src/cobalt/dom/on_screen_keyboard_test.cc b/src/cobalt/dom/on_screen_keyboard_test.cc
index 5f0d4a4..cbece44 100644
--- a/src/cobalt/dom/on_screen_keyboard_test.cc
+++ b/src/cobalt/dom/on_screen_keyboard_test.cc
@@ -25,7 +25,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media_session/media_session.h"
-#include "cobalt/network/network_module.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/source_code.h"
@@ -177,7 +176,7 @@
         message_loop_(MessageLoop::TYPE_DEFAULT),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_error_callback_)),
-        fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+        fetcher_factory_(new loader::FetcherFactory(NULL)),
         local_storage_database_(NULL),
         url_("about:blank"),
         engine_(script::JavaScriptEngine::CreateEngine()),
@@ -200,7 +199,8 @@
             base::Closure() /* window_minimize */,
             on_screen_keyboard_bridge_.get(), NULL, NULL,
             dom::Window::OnStartDispatchEventCallback(),
-            dom::Window::OnStopDispatchEventCallback())) {
+            dom::Window::OnStopDispatchEventCallback(),
+            dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
     on_screen_keyboard_bridge_->window_ = window_;
@@ -240,7 +240,6 @@
   MockErrorCallback mock_error_callback_;
   scoped_ptr<css_parser::Parser> css_parser_;
   scoped_ptr<dom_parser::Parser> dom_parser_;
-  network::NetworkModule network_module_;
   scoped_ptr<loader::FetcherFactory> fetcher_factory_;
   dom::LocalStorageDatabase local_storage_database_;
   GURL url_;
@@ -261,8 +260,7 @@
 
   global_environment_->EnableEval();
   global_environment_->SetReportEvalCallback(base::Closure());
-  bool succeeded = global_environment_->EvaluateScript(
-      source_code, false /*mute_errors*/, result);
+  bool succeeded = global_environment_->EvaluateScript(source_code, result);
   return succeeded;
 }
 
diff --git a/src/cobalt/dom/pointer_state.cc b/src/cobalt/dom/pointer_state.cc
index 6d517e8..6f41a71 100644
--- a/src/cobalt/dom/pointer_state.cc
+++ b/src/cobalt/dom/pointer_state.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/dom/pointer_state.h"
 
+#include <algorithm>
+
 #include "cobalt/dom/mouse_event.h"
 #include "cobalt/dom/pointer_event.h"
 #include "cobalt/dom/wheel_event.h"
@@ -246,5 +248,16 @@
   active_pointers_.erase(pointer_id);
 }
 
+void PointerState::ClearForShutdown() {
+  {
+    decltype(pointer_events_) empty_queue;
+    std::swap(pointer_events_, empty_queue);
+  }
+  target_override_.clear();
+  pending_target_override_.clear();
+  active_pointers_.clear();
+  pointers_with_active_buttons_.clear();
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/pointer_state.h b/src/cobalt/dom/pointer_state.h
index f934580..0f73a29 100644
--- a/src/cobalt/dom/pointer_state.h
+++ b/src/cobalt/dom/pointer_state.h
@@ -72,6 +72,11 @@
   void SetActive(int32_t pointer_id);
   void ClearActive(int32_t pointer_id);
 
+  // We will in general hold references back to window, which can result in
+  // leaking nearly the entire DOM if we don't forcibly clear them during
+  // shutdown.
+  void ClearForShutdown();
+
  private:
   // Stores pointer events until they are handled after a layout.
   std::queue<scoped_refptr<Event> > pointer_events_;
diff --git a/src/cobalt/dom/pseudo_element.h b/src/cobalt/dom/pseudo_element.h
index 4441fb2..9123f75 100644
--- a/src/cobalt/dom/pseudo_element.h
+++ b/src/cobalt/dom/pseudo_element.h
@@ -71,6 +71,12 @@
   HTMLElement* parent_element() { return parent_element_; }
   void ClearMatchingRules() { matching_rules_.clear(); }
 
+  void set_layout_boxes(scoped_ptr<LayoutBoxes> layout_boxes) {
+    layout_boxes_ = layout_boxes.Pass();
+  }
+  LayoutBoxes* layout_boxes() const { return layout_boxes_.get(); }
+  void reset_layout_boxes() { layout_boxes_.reset(); }
+
  private:
   HTMLElement* parent_element_;
 
@@ -86,6 +92,9 @@
 
   cssom::RulesWithCascadePrecedence matching_rules_;
 
+  // This contains information about the boxes generated from the element.
+  scoped_ptr<LayoutBoxes> layout_boxes_;
+
   // PseudoElement is a friend of Animatable so that animatable can insert and
   // remove animations into PseudoElement's set of animations.
   friend class DOMAnimatable;
diff --git a/src/cobalt/dom/screenshot.cc b/src/cobalt/dom/screenshot.cc
new file mode 100644
index 0000000..8449198
--- /dev/null
+++ b/src/cobalt/dom/screenshot.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/screenshot.h"
+
+#include "base/string_piece.h"
+#include "third_party/modp_b64/modp_b64.h"
+
+namespace cobalt {
+namespace dom {
+
+Screenshot::Screenshot(scoped_refptr<ArrayBuffer> pixel_data)
+    : pixel_data_(pixel_data) {
+  DCHECK(pixel_data);
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/screenshot.h b/src/cobalt/dom/screenshot.h
new file mode 100644
index 0000000..6989521
--- /dev/null
+++ b/src/cobalt/dom/screenshot.h
@@ -0,0 +1,45 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_SCREENSHOT_H_
+#define COBALT_DOM_SCREENSHOT_H_
+
+#include <string>
+
+#include "cobalt/dom/array_buffer.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+class Screenshot : public script::Wrappable {
+ public:
+  explicit Screenshot(scoped_refptr<ArrayBuffer> pixel_data);
+
+  // Readonly Attributes.
+  const scoped_refptr<ArrayBuffer>& buffer() const { return pixel_data_; }
+
+  DEFINE_WRAPPABLE_TYPE(Screenshot);
+
+ private:
+  scoped_refptr<ArrayBuffer> pixel_data_;
+
+  Screenshot(const Screenshot&) = delete;
+  Screenshot& operator=(const Screenshot&) = delete;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_SCREENSHOT_H_
diff --git a/src/cobalt/dom/screenshot.idl b/src/cobalt/dom/screenshot.idl
new file mode 100644
index 0000000..b9bc22d
--- /dev/null
+++ b/src/cobalt/dom/screenshot.idl
@@ -0,0 +1,17 @@
+// Copyright 2018 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.
+
+interface Screenshot {
+  readonly attribute ArrayBuffer buffer;
+};
diff --git a/src/cobalt/dom/screenshot_manager.cc b/src/cobalt/dom/screenshot_manager.cc
new file mode 100644
index 0000000..a95f63c
--- /dev/null
+++ b/src/cobalt/dom/screenshot_manager.cc
@@ -0,0 +1,113 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/screenshot_manager.h"
+
+#include "base/time.h"
+#include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/screenshot.h"
+#include "cobalt/render_tree/node.h"
+
+#include "cobalt/render_tree/resource_provider_stub.h"
+
+namespace cobalt {
+namespace dom {
+
+ScreenshotManager::ScreenshotManager(
+    const ScreenshotManager::ProvideScreenshotFunctionCallback&
+        screenshot_function_callback)
+    : screenshot_function_callback_(screenshot_function_callback) {}
+
+void ScreenshotManager::Screenshot(
+    loader::image::EncodedStaticImage::ImageFormat desired_format,
+    const scoped_refptr<render_tree::Node>& render_tree_root,
+    std::unique_ptr<ScreenshotManager::InterfacePromiseValue::Reference>
+        promise_reference) {
+  DLOG(INFO) << "Will take a screenshot asynchronously";
+  DCHECK(!screenshot_function_callback_.is_null());
+
+  // We want to ScreenshotManager::FillScreenshot, on this thread.
+  base::Callback<void(scoped_array<uint8>, const math::Size&)> fill_screenshot =
+      base::Bind(&ScreenshotManager::FillScreenshot, base::Unretained(this),
+                 next_ticket_id_, base::MessageLoopProxy::current(),
+                 desired_format);
+  bool was_emplaced =
+      ticket_to_screenshot_promise_map_
+          .emplace(next_ticket_id_, std::move(promise_reference))
+          .second;
+  DCHECK(was_emplaced);
+  ++next_ticket_id_;
+
+  screenshot_function_callback_.Run(render_tree_root, fill_screenshot);
+}
+
+void ScreenshotManager::SetEnvironmentSettings(
+    script::EnvironmentSettings* settings) {
+  environment_settings_ = settings;
+}
+
+void ScreenshotManager::FillScreenshot(
+    int64_t token, scoped_refptr<base::MessageLoopProxy> expected_message_loop,
+    loader::image::EncodedStaticImage::ImageFormat desired_format,
+    scoped_array<uint8> image_data, const math::Size& image_dimensions) {
+  if (base::MessageLoopProxy::current() != expected_message_loop) {
+    expected_message_loop->PostTask(
+        FROM_HERE,
+        base::Bind(&ScreenshotManager::FillScreenshot, base::Unretained(this),
+                   token, expected_message_loop, desired_format,
+                   base::Passed(&image_data), image_dimensions));
+    return;
+  }
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(environment_settings_);
+
+  auto iterator = ticket_to_screenshot_promise_map_.find(token);
+  DCHECK(iterator != ticket_to_screenshot_promise_map_.end());
+  const script::Promise<scoped_refptr<script::Wrappable>>& promise =
+      iterator->second->value();
+  do {
+    if (!image_data) {
+      // There was no data for the screenshot.
+      LOG(WARNING) << "There was no data for the screenshot.";
+      promise.Reject();
+      break;
+    }
+
+    scoped_refptr<loader::image::EncodedStaticImage> encoded_image_data =
+        CompressRGBAImage(desired_format, image_data.get(), image_dimensions);
+
+    int encoded_size =
+        static_cast<int>(encoded_image_data->GetEstimatedSizeInBytes());
+
+    if (encoded_image_data->GetEstimatedSizeInBytes() > kint32max) {
+      NOTREACHED();
+      promise.Reject();
+      break;
+    }
+    DLOG(INFO) << "Filling data in for the screenshot.";
+    scoped_refptr<ArrayBuffer> pixel_data =
+        new ArrayBuffer(environment_settings_, encoded_image_data->GetMemory(),
+                        static_cast<int>(encoded_size));
+    scoped_refptr<script::Wrappable> promise_result =
+        new dom::Screenshot(pixel_data);
+    promise.Resolve(promise_result);
+  } while (0);
+
+  DCHECK(promise.State() != script::PromiseState::kPending);
+  // Drop the reference to the promise since it will not be used.
+  ticket_to_screenshot_promise_map_.erase(iterator);
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/screenshot_manager.h b/src/cobalt/dom/screenshot_manager.h
new file mode 100644
index 0000000..4eea129
--- /dev/null
+++ b/src/cobalt/dom/screenshot_manager.h
@@ -0,0 +1,79 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_SCREENSHOT_MANAGER_H_
+#define COBALT_DOM_SCREENSHOT_MANAGER_H_
+
+#include <unordered_map>
+
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "cobalt/loader/image/image.h"
+#include "cobalt/loader/image/image_encoder.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/promise.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/script_value_factory.h"
+
+namespace cobalt {
+namespace dom {
+
+class ScreenshotManager {
+ public:
+  using InterfacePromise = script::Promise<scoped_refptr<script::Wrappable>>;
+  using InterfacePromiseValue = script::ScriptValue<InterfacePromise>;
+
+  using OnEncodedStaticImageCallback = base::Callback<void(
+      const scoped_refptr<loader::image::EncodedStaticImage>& image_data)>;
+  using OnUnencodedImageCallback =
+      base::Callback<void(scoped_array<uint8>, const math::Size&)>;
+
+  using ProvideScreenshotFunctionCallback =
+      base::Callback<void(const scoped_refptr<render_tree::Node>&,
+                          const OnUnencodedImageCallback&)>;
+
+  explicit ScreenshotManager(
+      const ProvideScreenshotFunctionCallback& screenshot_function_callback_);
+
+  void Screenshot(
+      loader::image::EncodedStaticImage::ImageFormat desired_format,
+      const scoped_refptr<render_tree::Node>& render_tree_root,
+      std::unique_ptr<ScreenshotManager::InterfacePromiseValue::Reference>
+          promise_reference);
+  void SetEnvironmentSettings(script::EnvironmentSettings* settings);
+
+ private:
+  void FillScreenshot(
+      int64_t token,
+      scoped_refptr<base::MessageLoopProxy> expected_message_loop,
+      loader::image::EncodedStaticImage::ImageFormat desired_format,
+      scoped_array<uint8> image_data, const math::Size& dimensions);
+
+  int64_t next_ticket_id_ = 0;
+
+  using TicketToPromiseMap =
+      std::unordered_map<int64_t,
+                         std::unique_ptr<InterfacePromiseValue::Reference>>;
+
+  base::ThreadChecker thread_checker_;
+  script::EnvironmentSettings* environment_settings_ = nullptr;
+  TicketToPromiseMap ticket_to_screenshot_promise_map_;
+  ProvideScreenshotFunctionCallback screenshot_function_callback_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_SCREENSHOT_MANAGER_H_
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
index f11dd17..298e799 100644
--- a/src/cobalt/dom/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -26,7 +26,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media_session/media_session.h"
-#include "cobalt/network/network_module.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "googleurl/src/gurl.h"
@@ -43,7 +42,7 @@
       : message_loop_(MessageLoop::TYPE_DEFAULT),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(base::Bind(&StubErrorCallback))),
-        fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+        fetcher_factory_(new loader::FetcherFactory(NULL)),
         local_storage_database_(NULL),
         url_("about:blank"),
         dom_stat_tracker_(new dom::DomStatTracker("StubWindow")) {
@@ -61,7 +60,8 @@
         dom::Window::CloseCallback() /* window_close */,
         base::Closure() /* window_minimize */, NULL, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
-        dom::Window::OnStopDispatchEventCallback());
+        dom::Window::OnStopDispatchEventCallback(),
+        dom::ScreenshotManager::ProvideScreenshotFunctionCallback());
     global_environment_->CreateGlobalObject(window_, &environment_settings_);
   }
 
@@ -77,7 +77,6 @@
   MessageLoop message_loop_;
   scoped_ptr<css_parser::Parser> css_parser_;
   scoped_ptr<dom_parser::Parser> dom_parser_;
-  network::NetworkModule network_module_;
   scoped_ptr<loader::FetcherFactory> fetcher_factory_;
   dom::LocalStorageDatabase local_storage_database_;
   GURL url_;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index becff85..0023eb0 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -11,7 +11,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 #include "cobalt/dom/window.h"
 
 #include <algorithm>
@@ -45,12 +44,16 @@
 #include "cobalt/dom/performance.h"
 #include "cobalt/dom/pointer_event.h"
 #include "cobalt/dom/screen.h"
+#include "cobalt/dom/screenshot.h"
+#include "cobalt/dom/screenshot_manager.h"
 #include "cobalt/dom/storage.h"
 #include "cobalt/dom/wheel_event.h"
 #include "cobalt/dom/window_timers.h"
 #include "cobalt/media_session/media_session_client.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/speech/speech_synthesis.h"
+#include "starboard/file.h"
 
 using cobalt::media_session::MediaSession;
 
@@ -119,6 +122,8 @@
     const scoped_refptr<MediaSession>& media_session,
     const OnStartDispatchEventCallback& on_start_dispatch_event_callback,
     const OnStopDispatchEventCallback& on_stop_dispatch_event_callback,
+    const ScreenshotManager::ProvideScreenshotFunctionCallback&
+        screenshot_function_callback,
     int csp_insecure_allowed_token, int dom_max_element_depth,
     float video_playback_rate_multiplier, ClockType clock_type,
     const CacheCallback& splash_screen_cache_callback,
@@ -189,7 +194,8 @@
                               : NULL),
       splash_screen_cache_callback_(splash_screen_cache_callback),
       on_start_dispatch_event_callback_(on_start_dispatch_event_callback),
-      on_stop_dispatch_event_callback_(on_stop_dispatch_event_callback) {
+      on_stop_dispatch_event_callback_(on_stop_dispatch_event_callback),
+      screenshot_manager_(screenshot_function_callback) {
 #if !defined(ENABLE_TEST_RUNNER)
   UNREFERENCED_PARAMETER(clock_type);
 #endif
@@ -242,6 +248,26 @@
 
 const scoped_refptr<Navigator>& Window::navigator() const { return navigator_; }
 
+script::Handle<ScreenshotManager::InterfacePromise> Window::Screenshot() {
+  scoped_refptr<render_tree::Node> render_tree_root =
+      document_->DoSynchronousLayoutAndGetRenderTree();
+
+  script::Handle<ScreenshotManager::InterfacePromise> promise =
+      html_element_context()
+          ->script_value_factory()
+          ->CreateInterfacePromise<scoped_refptr<dom::ArrayBuffer>>();
+
+  std::unique_ptr<ScreenshotManager::InterfacePromiseValue::Reference>
+      promise_reference(new ScreenshotManager::InterfacePromiseValue::Reference(
+          this, promise));
+
+  screenshot_manager_.Screenshot(
+      loader::image::EncodedStaticImage::ImageFormat::kPNG, render_tree_root,
+      std::move(promise_reference));
+
+  return promise;
+}
+
 scoped_refptr<cssom::CSSStyleDeclaration> Window::GetComputedStyle(
     const scoped_refptr<Element>& elt) {
   scoped_refptr<HTMLElement> html_element = elt->AsHTMLElement();
@@ -546,9 +572,13 @@
   return error_event->default_prevented();
 }
 
-void Window::SetSynchronousLayoutCallback(
-    const base::Closure& synchronous_layout_callback) {
-  document_->set_synchronous_layout_callback(synchronous_layout_callback);
+void Window::SetSynchronousLayoutCallback(const base::Closure& callback) {
+  document_->set_synchronous_layout_callback(callback);
+}
+
+void Window::SetSynchronousLayoutAndProduceRenderTreeCallback(
+    const SynchronousLayoutAndProduceRenderTreeCallback& callback) {
+  document_->set_synchronous_layout_and_produce_render_tree_callback(callback);
 }
 
 void Window::SetSize(int width, int height, float device_pixel_ratio) {
@@ -616,6 +646,10 @@
   }
 }
 
+void Window::ClearPointerStateForShutdown() {
+  document_->pointer_state()->ClearForShutdown();
+}
+
 void Window::TraceMembers(script::Tracer* tracer) {
   EventTarget::TraceMembers(tracer);
 
@@ -636,6 +670,10 @@
   tracer->Trace(on_screen_keyboard_);
 }
 
+void Window::SetEnvironmentSettings(script::EnvironmentSettings* settings) {
+  screenshot_manager_.SetEnvironmentSettings(settings);
+}
+
 void Window::CacheSplashScreen(const std::string& content) {
   if (splash_screen_cache_callback_.is_null()) {
     return;
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index e8d76b6..80ef19b 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -22,6 +22,7 @@
 #include "base/hash_tables.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/timer.h"
 #include "cobalt/base/application_state.h"
 #include "cobalt/cssom/css_parser.h"
@@ -38,6 +39,7 @@
 #include "cobalt/dom/on_screen_keyboard.h"
 #include "cobalt/dom/on_screen_keyboard_bridge.h"
 #include "cobalt/dom/parser.h"
+#include "cobalt/dom/screenshot_manager.h"
 #if defined(ENABLE_TEST_RUNNER)
 #include "cobalt/dom/test_runner.h"
 #endif  // ENABLE_TEST_RUNNER
@@ -49,6 +51,7 @@
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/font/remote_typeface_cache.h"
 #include "cobalt/loader/image/animated_image_tracker.h"
+#include "cobalt/loader/image/image.h"
 #include "cobalt/loader/image/image_cache.h"
 #include "cobalt/loader/loader.h"
 #include "cobalt/loader/mesh/mesh_cache.h"
@@ -116,6 +119,7 @@
   typedef UrlRegistry<MediaSource> MediaSourceRegistry;
   typedef base::Callback<bool(const GURL&, const std::string&)> CacheCallback;
   typedef base::Callback<SbWindow()> GetSbWindowCallback;
+
   enum ClockType { kClockTypeTestRunner, kClockTypeSystemTime };
 
   Window(
@@ -156,6 +160,8 @@
       const OnStartDispatchEventCallback&
           start_tracking_dispatch_event_callback,
       const OnStopDispatchEventCallback& stop_tracking_dispatch_event_callback,
+      const ScreenshotManager::ProvideScreenshotFunctionCallback&
+          screenshot_function_callback,
       int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0,
       float video_playback_rate_multiplier = 1.f,
       ClockType clock_type = kClockTypeSystemTime,
@@ -183,6 +189,8 @@
 
   const scoped_refptr<Navigator>& navigator() const;
 
+  script::Handle<ScreenshotManager::InterfacePromise> Screenshot();
+
   // Web API: CSSOM (partial interface)
   // Returns the computed style of the given element, as described in
   // https://www.w3.org/TR/2013/WD-cssom-20131205/#dom-window-getcomputedstyle.
@@ -309,8 +317,13 @@
   scoped_refptr<Window> opener() const { return NULL; }
 
   // Sets the function to call to trigger a synchronous layout.
+  using SynchronousLayoutAndProduceRenderTreeCallback =
+      base::Callback<scoped_refptr<render_tree::Node>()>;
   void SetSynchronousLayoutCallback(
       const base::Closure& synchronous_layout_callback);
+  void SetSynchronousLayoutAndProduceRenderTreeCallback(
+      const SynchronousLayoutAndProduceRenderTreeCallback&
+          synchronous_layout_callback);
 
   void SetSize(int width, int height, float device_pixel_ratio);
 
@@ -355,9 +368,18 @@
   void OnStartDispatchEvent(const scoped_refptr<dom::Event>& event);
   void OnStopDispatchEvent(const scoped_refptr<dom::Event>& event);
 
-  DEFINE_WRAPPABLE_TYPE(Window);
+  // |PointerState| will in general create reference cycles back to us, which is
+  // ok, as they are cleared over time.  During shutdown however, since no
+  // more queue progress can possibly be made, we must forcibly clear the
+  // queue.
+  void ClearPointerStateForShutdown();
+
   void TraceMembers(script::Tracer* tracer) override;
 
+  void SetEnvironmentSettings(script::EnvironmentSettings* settings);
+
+  DEFINE_WRAPPABLE_TYPE(Window);
+
  private:
   void StartDocumentLoad(
       loader::FetcherFactory* fetcher_factory, const GURL& url,
@@ -426,6 +448,10 @@
   OnStartDispatchEventCallback on_start_dispatch_event_callback_;
   OnStopDispatchEventCallback on_stop_dispatch_event_callback_;
 
+  ScreenshotManager screenshot_manager_;
+
+  script::EnvironmentSettings* environment_settings_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(Window);
 };
 
diff --git a/src/cobalt/dom/window.idl b/src/cobalt/dom/window.idl
index 77a3df6..04c4539 100644
--- a/src/cobalt/dom/window.idl
+++ b/src/cobalt/dom/window.idl
@@ -50,6 +50,8 @@
   // Mozilla's non-standard minimize() function.
   // https://developer.mozilla.org/en-US/docs/Web/API/Window/minimize
   void minimize();
+
+  Promise<Screenshot> screenshot();
 };
 Window implements GlobalEventHandlers;
 Window implements WindowEventHandlers;
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index 4e70b90..ea3f2fd 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -25,7 +25,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media_session/media_session.h"
-#include "cobalt/network/network_module.h"
 #include "cobalt/network_bridge/net_poster.h"
 #include "googleurl/src/gurl.h"
 #include "starboard/window.h"
@@ -45,7 +44,7 @@
       : message_loop_(MessageLoop::TYPE_DEFAULT),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_error_callback_)),
-        fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+        fetcher_factory_(new loader::FetcherFactory(NULL)),
         local_storage_database_(NULL),
         url_("about:blank"),
         window_(new Window(
@@ -62,7 +61,8 @@
             dom::Window::CloseCallback() /* window_close */,
             base::Closure() /* window_minimize */, NULL, NULL, NULL,
             dom::Window::OnStartDispatchEventCallback(),
-            dom::Window::OnStopDispatchEventCallback())) {}
+            dom::Window::OnStopDispatchEventCallback(),
+            dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {}
 
   ~WindowTest() override {}
 
@@ -70,7 +70,6 @@
   MockErrorCallback mock_error_callback_;
   scoped_ptr<css_parser::Parser> css_parser_;
   scoped_ptr<dom_parser::Parser> dom_parser_;
-  network::NetworkModule network_module_;
   scoped_ptr<loader::FetcherFactory> fetcher_factory_;
   dom::LocalStorageDatabase local_storage_database_;
   GURL url_;
diff --git a/src/cobalt/dom_parser/dom_parser.gyp b/src/cobalt/dom_parser/dom_parser.gyp
index 99775c7..eaf84bf 100644
--- a/src/cobalt/dom_parser/dom_parser.gyp
+++ b/src/cobalt/dom_parser/dom_parser.gyp
@@ -70,7 +70,7 @@
       'variables': {
         'executable_name': 'dom_parser_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/input/input_device_manager_desktop.cc b/src/cobalt/input/input_device_manager_desktop.cc
index 2904d04..ddba793 100644
--- a/src/cobalt/input/input_device_manager_desktop.cc
+++ b/src/cobalt/input/input_device_manager_desktop.cc
@@ -174,7 +174,13 @@
       // reported button press.
       buttons &= ~(1 << event->button());
       break;
-    default:
+    case system_window::InputEvent::kKeyDown:
+    case system_window::InputEvent::kKeyUp:
+    case system_window::InputEvent::kKeyMove:
+    case system_window::InputEvent::kInput:
+    case system_window::InputEvent::kPointerMove:
+    case system_window::InputEvent::kTouchpadMove:
+    case system_window::InputEvent::kWheel:
       break;
   }
 
@@ -226,7 +232,14 @@
     case system_window::InputEvent::kTouchpadMove:
       pointer_event.set_pointer_type("touchpad");
       break;
-    default:
+    case system_window::InputEvent::kKeyDown:
+    case system_window::InputEvent::kKeyUp:
+    case system_window::InputEvent::kKeyMove:
+    case system_window::InputEvent::kInput:
+    case system_window::InputEvent::kPointerDown:
+    case system_window::InputEvent::kPointerMove:
+    case system_window::InputEvent::kPointerUp:
+    case system_window::InputEvent::kWheel:
       pointer_event.set_pointer_type("mouse");
       break;
   }
@@ -328,12 +341,12 @@
     case system_window::InputEvent::kWheel:
       HandleWheelEvent(input_event);
       break;
-#if SB_HAS(ON_SCREEN_KEYBOARD)
     case system_window::InputEvent::kInput:
+#if SB_HAS(ON_SCREEN_KEYBOARD)
       HandleInputEvent(input_event);
       break;
 #endif  // SB_HAS(ON_SCREEN_KEYBOARD)
-    default:
+    case system_window::InputEvent::kKeyMove:
       break;
   }
 
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 324e609..b21d447 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -716,7 +716,7 @@
 #endif  // COBALT_BOX_DUMP_ENABLED
 
 namespace {
-void PopulateBaseStyleForBackgroundNode(
+void PopulateBaseStyleForBackgroundColorNode(
     const scoped_refptr<const cssom::CSSComputedStyleData>& source_style,
     const scoped_refptr<cssom::CSSComputedStyleData>& destination_style) {
   // NOTE: Properties set by PopulateBaseStyleForBackgroundNode() should match
@@ -724,7 +724,7 @@
   destination_style->set_background_color(source_style->background_color());
 }
 
-void SetupBackgroundNodeFromStyle(
+void SetupBackgroundColorNodeFromStyle(
     const base::optional<RoundedCorners>& rounded_corners,
     const scoped_refptr<const cssom::CSSComputedStyleData>& style,
     RectNode::Builder* rect_node_builder) {
@@ -1445,8 +1445,8 @@
                                  border_top_width().toFloat()),
                     GetPaddingBoxSize()),
         scoped_ptr<Brush>());
-    SetupBackgroundNodeFromStyle(rounded_corners, computed_style(),
-                                 &rect_node_builder);
+    SetupBackgroundColorNodeFromStyle(rounded_corners, computed_style(),
+                                      &rect_node_builder);
     if (!rect_node_builder.rect.IsEmpty()) {
       scoped_refptr<RectNode> rect_node(new RectNode(rect_node_builder.Pass()));
       border_node_builder->AddChild(rect_node);
@@ -1455,8 +1455,8 @@
       // instead here.
       if (background_color_animated) {
         AddAnimations<RectNode>(
-            base::Bind(&PopulateBaseStyleForBackgroundNode),
-            base::Bind(&SetupBackgroundNodeFromStyle, rounded_corners),
+            base::Bind(&PopulateBaseStyleForBackgroundColorNode),
+            base::Bind(&SetupBackgroundColorNodeFromStyle, rounded_corners),
             *css_computed_style_declaration(), rect_node, animate_node_builder);
       }
     }
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index 4217bb1..f6abf01 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -103,14 +103,14 @@
       context_(context) {}
 
 BoxGenerator::~BoxGenerator() {
-  if (generating_html_element_) {
-    scoped_ptr<LayoutBoxes> layout_boxes;
-    if (!boxes_.empty()) {
-      layout_boxes = make_scoped_ptr(new LayoutBoxes());
-      layout_boxes->SwapBoxes(boxes_);
-    }
+  // Later code assumes that if layout_boxes() is non-null, then it contains
+  // more than one box.  This allows us to avoid some allocations of LayoutBoxes
+  // objects.  We don't need to worry about setting layout_boxes() back to
+  // null because this should end up being done in html_element.cc when the
+  // boxes become invalidated.
+  if (generating_html_element_ && !boxes_.empty()) {
     generating_html_element_->set_layout_boxes(
-        layout_boxes.PassAs<dom::LayoutBoxes>());
+        scoped_ptr<dom::LayoutBoxes>(new LayoutBoxes(std::move(boxes_))));
   }
 }
 
@@ -133,9 +133,7 @@
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
 
   // If the html element already has layout boxes, we can reuse them.
-  if (partial_layout_is_enabled && html_element->layout_boxes() &&
-      html_element->layout_boxes()->type() ==
-          dom::LayoutBoxes::kLayoutLayoutBoxes) {
+  if (partial_layout_is_enabled && html_element->layout_boxes()) {
     LayoutBoxes* layout_boxes =
         base::polymorphic_downcast<LayoutBoxes*>(html_element->layout_boxes());
     DCHECK(boxes_.empty());
@@ -805,66 +803,81 @@
   //   https://www.w3.org/TR/CSS21/generate.html#before-after-content
   dom::PseudoElement* pseudo_element =
       html_element->pseudo_element(pseudo_element_type);
-  if (pseudo_element) {
-    ContainerBoxGenerator pseudo_element_box_generator(
-        dom::kNoExplicitDirectionality,
-        pseudo_element->css_computed_style_declaration(), paragraph_, context_);
-    pseudo_element->computed_style()->display()->Accept(
-        &pseudo_element_box_generator);
-    scoped_refptr<ContainerBox> pseudo_element_box =
-        pseudo_element_box_generator.container_box();
-    // A pseudo element with "display: none" generates no boxes and has no
-    // effect on layout.
-    if (pseudo_element_box != NULL) {
-      // Generate the box(es) to be added to the associated html element, using
-      // the computed style of the pseudo element.
+  if (!pseudo_element) {
+    return;
+  }
 
-      // The generated content is a text node with the string value of the
-      // 'content' property.
-      ContentProvider content_provider;
-      pseudo_element->computed_style()->content()->Accept(&content_provider);
-      if (content_provider.is_element_generated()) {
-        scoped_refptr<dom::Text> child_node(new dom::Text(
-            html_element->node_document(), content_provider.content_string()));
+  // We assume that if our parent element's boxes are being regenerated, then we
+  // should regenerate the pseudo element boxes.  There are some cases where
+  // the parent element may be regenerating its boxes even if it already had
+  // some, such as if its boxes were inline level.  In that case, pseudo
+  // elements may also have boxes, so we make it clear that we will not be
+  // reusing pseudo element boxes even if they exist by explicitly resetting
+  // them now.
+  pseudo_element->reset_layout_boxes();
 
-        // In the case where the pseudo element has no color property of its
-        // own, but is directly inheriting a color property from its parent html
-        // element, we use the parent's animations if the pseudo element has
-        // none and the parent has only color property animations. This allows
-        // the child text boxes to animate properly and fixes bugs, while
-        // keeping the impact of the fix as small as possible to minimize the
-        // risk of introducing new bugs.
-        // TODO: Remove this logic when support for inheriting
-        // animations on inherited properties is added.
-        bool use_html_element_animations =
-            !pseudo_element->computed_style()->IsDeclared(
-                cssom::kColorProperty) &&
-            html_element->computed_style()->IsDeclared(cssom::kColorProperty) &&
-            pseudo_element->css_computed_style_declaration()
-                ->animations()
-                ->IsEmpty() &&
-            HasOnlyColorPropertyAnimations(
-                html_element->css_computed_style_declaration()->animations());
+  ContainerBoxGenerator pseudo_element_box_generator(
+      dom::kNoExplicitDirectionality,
+      pseudo_element->css_computed_style_declaration(), paragraph_, context_);
+  pseudo_element->computed_style()->display()->Accept(
+      &pseudo_element_box_generator);
+  scoped_refptr<ContainerBox> pseudo_element_box =
+      pseudo_element_box_generator.container_box();
+  // A pseudo element with "display: none" generates no boxes and has no
+  // effect on layout.
+  if (pseudo_element_box == NULL) {
+    return;
+  }
 
-        BoxGenerator child_box_generator(
-            pseudo_element->css_computed_style_declaration(),
-            use_html_element_animations ? html_element->animations()
-                                        : pseudo_element->animations(),
-            paragraph_, dom_element_depth_ + 1, context_);
-        child_node->Accept(&child_box_generator);
-        const Boxes& child_boxes = child_box_generator.boxes();
-        for (Boxes::const_iterator child_box_iterator = child_boxes.begin();
-             child_box_iterator != child_boxes.end(); ++child_box_iterator) {
-          if (!pseudo_element_box->TryAddChild(*child_box_iterator)) {
-            return;
-          }
-        }
+  // Generate the box(es) to be added to the associated html element, using
+  // the computed style of the pseudo element.
 
-        // Add the box(es) from the pseudo element to the associated element.
-        AppendChildBoxToLine(pseudo_element_box);
-      }
+  // The generated content is a text node with the string value of the
+  // 'content' property.
+  ContentProvider content_provider;
+  pseudo_element->computed_style()->content()->Accept(&content_provider);
+  if (!content_provider.is_element_generated()) {
+    return;
+  }
+
+  scoped_refptr<dom::Text> child_node(new dom::Text(
+      html_element->node_document(), content_provider.content_string()));
+
+  // In the case where the pseudo element has no color property of its
+  // own, but is directly inheriting a color property from its parent html
+  // element, we use the parent's animations if the pseudo element has
+  // none and the parent has only color property animations. This allows
+  // the child text boxes to animate properly and fixes bugs, while
+  // keeping the impact of the fix as small as possible to minimize the
+  // risk of introducing new bugs.
+  // TODO: Remove this logic when support for inheriting
+  // animations on inherited properties is added.
+  bool use_html_element_animations =
+      !pseudo_element->computed_style()->IsDeclared(cssom::kColorProperty) &&
+      html_element->computed_style()->IsDeclared(cssom::kColorProperty) &&
+      pseudo_element->css_computed_style_declaration()
+          ->animations()
+          ->IsEmpty() &&
+      HasOnlyColorPropertyAnimations(
+          html_element->css_computed_style_declaration()->animations());
+
+  BoxGenerator child_box_generator(
+      pseudo_element->css_computed_style_declaration(),
+      use_html_element_animations ? html_element->animations()
+                                  : pseudo_element->animations(),
+      paragraph_, dom_element_depth_ + 1, context_);
+  child_node->Accept(&child_box_generator);
+  for (const auto& child_box : child_box_generator.boxes()) {
+    if (!pseudo_element_box->TryAddChild(child_box)) {
+      return;
     }
   }
+
+  pseudo_element->set_layout_boxes(
+      scoped_ptr<dom::LayoutBoxes>(new LayoutBoxes({pseudo_element_box})));
+
+  // Add the box(es) from the pseudo element to the associated element.
+  AppendChildBoxToLine(pseudo_element_box);
 }
 
 namespace {
diff --git a/src/cobalt/layout/layout.gyp b/src/cobalt/layout/layout.gyp
index 6b6031e..1873f57 100644
--- a/src/cobalt/layout/layout.gyp
+++ b/src/cobalt/layout/layout.gyp
@@ -145,7 +145,7 @@
       'variables': {
         'executable_name': 'layout_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/layout/layout_boxes.cc b/src/cobalt/layout/layout_boxes.cc
index 99787b8..07857a0 100644
--- a/src/cobalt/layout/layout_boxes.cc
+++ b/src/cobalt/layout/layout_boxes.cc
@@ -23,10 +23,6 @@
 namespace cobalt {
 namespace layout {
 
-LayoutBoxes::LayoutBoxes() {}
-
-LayoutBoxes::~LayoutBoxes() {}
-
 LayoutBoxes::Type LayoutBoxes::type() const { return kLayoutLayoutBoxes; }
 
 // Algorithm for GetClientRects:
diff --git a/src/cobalt/layout/layout_boxes.h b/src/cobalt/layout/layout_boxes.h
index ffae93d..797fcad 100644
--- a/src/cobalt/layout/layout_boxes.h
+++ b/src/cobalt/layout/layout_boxes.h
@@ -28,8 +28,9 @@
 
 class LayoutBoxes : public dom::LayoutBoxes {
  public:
-  LayoutBoxes();
-  ~LayoutBoxes() override;
+  LayoutBoxes() {}
+  explicit LayoutBoxes(Boxes&& boxes) : boxes_(std::move(boxes)) {}
+  ~LayoutBoxes() override{};
 
   // From: dom:LayoutBoxes
   //
@@ -60,7 +61,6 @@
 
   // Other
   //
-  void SwapBoxes(Boxes& boxes) { boxes_.swap(boxes); }
   const Boxes& boxes() { return boxes_; }
 
  private:
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index d16304a..89bc349 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -57,6 +57,7 @@
 
   // Called to perform a synchronous layout.
   void DoSynchronousLayout();
+  scoped_refptr<render_tree::Node> DoSynchronousLayoutAndGetRenderTree();
 
   void Suspend();
   void Resume();
@@ -183,6 +184,8 @@
   window_->document()->AddObserver(this);
   window_->SetSynchronousLayoutCallback(
       base::Bind(&Impl::DoSynchronousLayout, base::Unretained(this)));
+  window_->SetSynchronousLayoutAndProduceRenderTreeCallback(base::Bind(
+      &Impl::DoSynchronousLayoutAndGetRenderTree, base::Unretained(this)));
 
   UErrorCode status = U_ZERO_ERROR;
   line_break_iterator_ =
@@ -235,9 +238,35 @@
   }
 }
 
+scoped_refptr<render_tree::Node>
+LayoutManager::Impl::DoSynchronousLayoutAndGetRenderTree() {
+  TRACE_EVENT0("cobalt::layout",
+               "LayoutManager::Impl::DoSynchronousLayoutAndGetRenderTree()");
+  DoSynchronousLayout();
+
+  scoped_refptr<render_tree::Node> render_tree_root =
+      layout::GenerateRenderTreeFromBoxTree(used_style_provider_.get(),
+                                            layout_stat_tracker_,
+                                            &initial_containing_block_);
+
+  base::optional<double> current_time_milliseconds =
+      this->window_->document()->timeline()->current_time();
+  DCHECK(current_time_milliseconds.has_engaged());
+  base::TimeDelta current_time =
+      base::TimeDelta::FromMillisecondsD(*current_time_milliseconds);
+
+  using render_tree::animations::AnimateNode;
+  AnimateNode* animate_node =
+      base::polymorphic_downcast<AnimateNode*>(render_tree_root.get());
+  AnimateNode::AnimateResults results = animate_node->Apply(current_time);
+
+  return results.animated->source();
+}
+
 void LayoutManager::Impl::DoSynchronousLayout() {
   TRACE_EVENT0("cobalt::layout", "LayoutManager::Impl::DoSynchronousLayout()");
   if (suspended_) {
+    DLOG(WARNING) << "Skipping layout since Cobalt is in a suspended state.";
     return;
   }
 
@@ -364,14 +393,7 @@
       TRACE_EVENT_BEGIN0("cobalt::layout", kBenchmarkStatLayout);
     }
 
-    if (are_computed_styles_and_box_tree_dirty_) {
-      layout::UpdateComputedStylesAndLayoutBoxTree(
-          locale_, window_->document(), dom_max_element_depth_,
-          used_style_provider_.get(), layout_stat_tracker_,
-          line_break_iterator_.get(), character_break_iterator_.get(),
-          &initial_containing_block_);
-      are_computed_styles_and_box_tree_dirty_ = false;
-    }
+    DoSynchronousLayout();
 
     // If no render tree has been produced yet, check if html display
     // should prevent the first render tree.
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index b18953d..7c7e2a8 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -1155,6 +1155,7 @@
     case cssom::KeywordValue::kClip:
     case cssom::KeywordValue::kEllipsis:
     case cssom::KeywordValue::kEnd:
+    case cssom::KeywordValue::kEquirectangular:
     case cssom::KeywordValue::kFantasy:
     case cssom::KeywordValue::kFixed:
     case cssom::KeywordValue::kForwards:
@@ -1190,7 +1191,6 @@
     case cssom::KeywordValue::kTop:
     case cssom::KeywordValue::kUppercase:
     case cssom::KeywordValue::kVisible:
-    default:
       NOTREACHED();
   }
 }
@@ -1340,6 +1340,7 @@
       case cssom::KeywordValue::kCursive:
       case cssom::KeywordValue::kEllipsis:
       case cssom::KeywordValue::kEnd:
+      case cssom::KeywordValue::kEquirectangular:
       case cssom::KeywordValue::kFantasy:
       case cssom::KeywordValue::kForwards:
       case cssom::KeywordValue::kFixed:
@@ -1375,7 +1376,6 @@
       case cssom::KeywordValue::kTop:
       case cssom::KeywordValue::kUppercase:
       case cssom::KeywordValue::kVisible:
-      default:
         NOTREACHED();
     }
   }
@@ -1413,6 +1413,7 @@
       case cssom::KeywordValue::kCursive:
       case cssom::KeywordValue::kEllipsis:
       case cssom::KeywordValue::kEnd:
+      case cssom::KeywordValue::kEquirectangular:
       case cssom::KeywordValue::kFantasy:
       case cssom::KeywordValue::kForwards:
       case cssom::KeywordValue::kFixed:
@@ -1447,7 +1448,6 @@
       case cssom::KeywordValue::kTop:
       case cssom::KeywordValue::kUppercase:
       case cssom::KeywordValue::kVisible:
-      default:
         NOTREACHED();
     }
   }
diff --git a/src/cobalt/layout_tests/layout_benchmarks.cc b/src/cobalt/layout_tests/layout_benchmarks.cc
index 78939d4..1f25f1f 100644
--- a/src/cobalt/layout_tests/layout_benchmarks.cc
+++ b/src/cobalt/layout_tests/layout_benchmarks.cc
@@ -189,7 +189,8 @@
   // results.
   layout_results =
       SnapshotURL(test_info_.url, viewport_size,
-                  renderer_benchmark_runner_.GetResourceProvider());
+                  renderer_benchmark_runner_.GetResourceProvider(),
+                  dom::ScreenshotManager::ProvideScreenshotFunctionCallback());
 
   // Finally run the renderer benchmarks to acquire performance data on
   // rendering.
diff --git a/src/cobalt/layout_tests/layout_snapshot.cc b/src/cobalt/layout_tests/layout_snapshot.cc
index 4762640..3198f0f 100644
--- a/src/cobalt/layout_tests/layout_snapshot.cc
+++ b/src/cobalt/layout_tests/layout_snapshot.cc
@@ -19,7 +19,9 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
+#include "cobalt/browser/user_agent_string.h"
 #include "cobalt/browser/web_module.h"
+#include "cobalt/dom/screenshot_manager.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/render_tree/resource_provider.h"
@@ -55,7 +57,9 @@
 
 browser::WebModule::LayoutResults SnapshotURL(
     const GURL& url, const math::Size& viewport_size,
-    render_tree::ResourceProvider* resource_provider) {
+    render_tree::ResourceProvider* resource_provider,
+    const dom::ScreenshotManager::ProvideScreenshotFunctionCallback&
+        screenshot_provider) {
   base::RunLoop run_loop;
 
   // Setup WebModule's auxiliary components.
@@ -63,7 +67,10 @@
   // Some layout tests test Content Security Policy; allow HTTP so we
   // don't interfere.
   net_options.https_requirement = network::kHTTPSOptional;
-  network::NetworkModule network_module(net_options);
+  network::NetworkModule network_module(
+      browser::CreateUserAgentString(
+          browser::GetUserAgentPlatformInfoFromSystem()),
+      NULL, NULL, net_options);
 
   // Use 128M of image cache to minimize the effect of image loading.
   const size_t kImageCacheCapacity = 128 * 1024 * 1024;
@@ -74,6 +81,8 @@
   web_module_options.layout_trigger = layout::LayoutManager::kTestRunnerMode;
   web_module_options.image_cache_capacity = kImageCacheCapacity;
 
+  web_module_options.provide_screenshot_function = screenshot_provider;
+
   // Prepare a slot for our results to be placed when ready.
   base::optional<browser::WebModule::LayoutResults> results;
 
diff --git a/src/cobalt/layout_tests/layout_snapshot.h b/src/cobalt/layout_tests/layout_snapshot.h
index 2cd86b5..d69701a 100644
--- a/src/cobalt/layout_tests/layout_snapshot.h
+++ b/src/cobalt/layout_tests/layout_snapshot.h
@@ -16,6 +16,7 @@
 #define COBALT_LAYOUT_TESTS_LAYOUT_SNAPSHOT_H_
 
 #include "cobalt/browser/web_module.h"
+#include "cobalt/dom/screenshot_manager.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "googleurl/src/gurl.h"
 
@@ -29,7 +30,9 @@
 // tests.
 browser::WebModule::LayoutResults SnapshotURL(
     const GURL& url, const math::Size& viewport_size,
-    render_tree::ResourceProvider* resource_provider);
+    render_tree::ResourceProvider* resource_provider,
+    const dom::ScreenshotManager::ProvideScreenshotFunctionCallback&
+        screenshot_provider);
 
 }  // namespace layout_tests
 }  // namespace cobalt
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index b4a2f08..7dd02fc 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -50,6 +50,26 @@
 const char kOutputAllTestDetails[] = "output-all-test-details";
 }  // namespace switches
 
+namespace {
+
+void ScreenshotFunction(
+    scoped_refptr<base::MessageLoopProxy> expected_message_loop,
+    renderer::RenderTreePixelTester* pixel_tester,
+    const scoped_refptr<render_tree::Node>& node,
+    const dom::ScreenshotManager::OnUnencodedImageCallback& callback) {
+  if (base::MessageLoopProxy::current() != expected_message_loop) {
+    expected_message_loop->PostTask(
+        FROM_HERE, base::Bind(&ScreenshotFunction, expected_message_loop,
+                              pixel_tester, node, callback));
+    return;
+  }
+  scoped_array<uint8_t> image_data = pixel_tester->RasterizeRenderTree(node);
+  const math::Size& image_dimensions = pixel_tester->GetTargetSize();
+  callback.Run(image_data.Pass(), image_dimensions);
+}
+
+}  // namespace
+
 class LayoutTest : public ::testing::TestWithParam<TestInfo> {};
 TEST_P(LayoutTest, LayoutTest) {
   // Output the name of the current input file so that it is visible in test
@@ -86,7 +106,9 @@
       pixel_tester_options);
 
   browser::WebModule::LayoutResults layout_results = SnapshotURL(
-      GetParam().url, viewport_size, pixel_tester.GetResourceProvider());
+      GetParam().url, viewport_size, pixel_tester.GetResourceProvider(),
+      base::Bind(&ScreenshotFunction, base::MessageLoopProxy::current(),
+                 base::Unretained(&pixel_tester)));
 
   scoped_refptr<render_tree::animations::AnimateNode> animate_node =
       new render_tree::animations::AnimateNode(layout_results.render_tree);
diff --git a/src/cobalt/layout_tests/layout_tests.gyp b/src/cobalt/layout_tests/layout_tests.gyp
index 44d7738..fd69009 100644
--- a/src/cobalt/layout_tests/layout_tests.gyp
+++ b/src/cobalt/layout_tests/layout_tests.gyp
@@ -85,7 +85,7 @@
       'variables': {
         'executable_name': 'layout_tests',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
@@ -114,7 +114,7 @@
       'variables': {
         'executable_name': 'layout_benchmarks',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
     {
       'target_name': 'web_platform_tests',
@@ -142,7 +142,7 @@
       'variables': {
         'executable_name': 'web_platform_tests',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
   ],
diff --git a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
index c5f7dbf..5fa976c 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
@@ -14,9 +14,12 @@
 platform-object-user-properties-survive-gc
 positioned-boxes-with-same-z-index-are-processed-in-insertion-order
 relative-font-size
+screenshot
+screenshot-with-animation
 simple-transform-text
 transform-translate-with-em-units
 transform-with-background-color
 url-utils-interfaces
 user-agent-style-sheet-display
 user-agent-test
+window-onerror
diff --git a/src/cobalt/layout_tests/testdata/cobalt/screenshot-expected.png b/src/cobalt/layout_tests/testdata/cobalt/screenshot-expected.png
new file mode 100644
index 0000000..608f34a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/screenshot-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/screenshot-with-animation-expected.png b/src/cobalt/layout_tests/testdata/cobalt/screenshot-with-animation-expected.png
new file mode 100644
index 0000000..bca69bb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/screenshot-with-animation-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/screenshot-with-animation.html b/src/cobalt/layout_tests/testdata/cobalt/screenshot-with-animation.html
new file mode 100644
index 0000000..6298fe3
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/screenshot-with-animation.html
@@ -0,0 +1,84 @@
+ <HEAD>
+ <style>
+    html,body { height: 100% }
+
+    @keyframes testAnimation {
+      0% { transform: translate(0, 0); }
+      50% { transform: translate(100px, 100px); }
+      100% { transform: translate(200px, 0px); }
+    }
+
+    #blue {
+      height: 50%;
+      width: 128px;
+      background-size: cover;
+      background-color: blue;
+      display: inline-block;
+      margin-left: 25%;
+    }
+    #green {
+      height: 50%;
+      width: 128px;
+      background-size: cover;
+      background-color: green;
+      display: inline-block;
+      margin-right: 25%;
+      animation: testAnimation 2s forwards;
+    }
+    /* Note that the width and height values below were carefully chosen to have
+       a smaller dependency on platform specific texture minification filters.
+       An example of this is on directfb platform (used for testing), where
+       bilinear filtering is not available (when blending is turned on).
+     */
+    #shot {
+      height: 50%;
+      width: 50%;
+    }
+  </style>
+</HEAD>
+<BODY>
+  <div id="blue">
+  </div>
+  <div id="green">
+  </div>
+  <div id="shot">
+  </div>
+<script>
+  if (window.testRunner) {
+    window.testRunner.waitUntilDone();
+  }
+  window.addEventListener('load', (event) => {
+      if (window.testRunner) {
+        window.testRunner.AdvanceClockByMs(1000);
+        window.testRunner.DoNonMeasuredLayout();
+      }
+      window.screenshot().then( screen_shot_data => {
+        shot = document.getElementById("shot");
+        var image_blob = new Blob([screen_shot_data.buffer]);
+        var image_url = URL.createObjectURL(image_blob);
+        var image = new Image();
+        // By waiting for the onload event, we can safely ensure that the
+        // screenshot image is decoded, and render to be rendered.
+        image.onload = () => {
+          var shot = document.getElementById('shot');
+          // Put a solid red border around the image to make it easier to
+          // spot manually.
+          shot.style.borderStyle = 'solid';
+          shot.style.borderColor = 'red';
+          // Shrink-to-fit the image onto the div.
+          shot.style.backgroundSize = '100% 100%';
+          shot.style.backgroundImage = 'url(' + image_url + ')';
+          if (window.testRunner) {
+            window.testRunner.notifyDone();
+          }
+        }
+        image.src = image_url;
+      }).catch( () => {
+        console.log('window.screenshot() error occured.');
+        if (window.testRunner) {
+          window.testRunner.notifyDone();
+        }
+      });
+  });
+</script>
+</BODY>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/screenshot.html b/src/cobalt/layout_tests/testdata/cobalt/screenshot.html
new file mode 100644
index 0000000..cc86f83
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/screenshot.html
@@ -0,0 +1,72 @@
+ <HEAD>
+ <style>
+    html,body { height: 100% }
+    #blue {
+      height: 50%;
+      width: 128px;
+      background-size: cover;
+      background-color: blue;
+      display: inline-block;
+      margin-left: 25%;
+    }
+    #green {
+      height: 50%;
+      width: 128px;
+      background-size: cover;
+      background-color: green;
+      display: inline-block;
+      margin-right: 25%;
+    }
+    /* Note that the width and height values below were carefully chosen to have
+       a smaller dependency on platform specific texture minification filters.
+       An example of this is on directfb platform (used for testing), where
+       bilinear filtering is not available (when blending is turned on).
+     */
+    #shot {
+      height: 50%;
+      width: 50%;
+    }
+  </style>
+</HEAD>
+<BODY>
+  <div id="blue">
+  </div>
+  <div id="green">
+  </div>
+  <div id="shot">
+  </div>
+<script>
+  if (window.testRunner) {
+    window.testRunner.waitUntilDone();
+  }
+  window.addEventListener('load', (event) => {
+    window.screenshot().then( screen_shot_data => {
+      shot = document.getElementById("shot");
+      var image_blob = new Blob([screen_shot_data.buffer]);
+      var image_url = URL.createObjectURL(image_blob);
+      var image = new Image();
+      // By waiting for the onload event, we can safely ensure that the
+      // screenshot image is decoded, and render to be rendered.
+      image.onload = () => {
+        var shot = document.getElementById('shot');
+        // Put a solid red border around the image to make it easier to
+        // spot manually.
+        shot.style.borderStyle = 'solid';
+        shot.style.borderColor = 'red';
+        // Shrink-to-fit the image onto the div.
+        shot.style.backgroundSize = '100% 100%';
+        shot.style.backgroundImage = 'url(' + image_url + ')';
+        if (window.testRunner) {
+          window.testRunner.notifyDone();
+        }
+      }
+      image.src = image_url;
+    }).catch( () => {
+      console.log('window.screenshot() error occured.');
+      if (window.testRunner) {
+        window.testRunner.notifyDone();
+      }
+    });
+  });
+</script>
+</BODY>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/window-onerror-expected.png b/src/cobalt/layout_tests/testdata/cobalt/window-onerror-expected.png
new file mode 100644
index 0000000..dec2811
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/window-onerror-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/window-onerror.html b/src/cobalt/layout_tests/testdata/cobalt/window-onerror.html
new file mode 100644
index 0000000..6baa77d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/window-onerror.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>window-onerror</title>
+  <style>
+    #result {
+      width: 100px;
+      height:100px;
+      background-color:#0047AB;
+    }
+  </style>
+</head>
+<body>
+
+<div id="result"></div>
+
+<script>
+
+function expect(condition, message) {
+  if (!condition) {
+    document.querySelector('#result').style.display = 'none';
+    const fullMessage = `failed expectation at:
+${new Error().stack}
+${typeof message === 'undefined' ? '' : 'with message: ' + message}`;
+    console.error(fullMessage);
+  }
+}
+
+let windowOnErrorWasCalled = false;
+window.onerror = (message, filename, lineno, colno, error) => {
+  if (typeof message === 'object') {
+    // The code below is to work around Cobalt's window.onerror signature not being correct
+    // (although weird, the spec says you're supposed to expand the event into its attributes before
+    // dispatching it).  It is still important to test the rest of window.onerror support, despite
+    // this known issue.
+    const errorEvent = message;
+    message = errorEvent.message;
+    filename = errorEvent.filename;
+    lineno = errorEvent.lineno;
+    colno = errorEvent.colno;
+    error = errorEvent.error;
+  }
+
+  // We allow anything that ends with "myCoolMessage" to pass, Chrome/Firefox behavior differs here
+  // (Chrome: "Uncaught Error: myCoolMessage", Firefox: "Error: myCoolMessage").
+  expect(/.*myCoolMessage/.test(message), message);
+  // Allow any filename that ends with "window-onerror.html", in order to not couple too heavily to
+  // the implementation of layout_tests.
+  expect(/.*window-onerror.html/.test(filename), filename);
+  expect(lineno === 72, lineno);
+  // The value of the column number is not standard across major browsers.
+  //   Chrome: 1 without devtools open, 7 with devtools open.
+  //   Edge: Always 1.
+  //   Firefox: Always 7.
+  //   Safari: 18.
+  expect(colno === 1 || colno === 7 || colno === 18, colno);
+  expect(typeof error === 'object', typeof error);
+  expect(String(error) === 'Error: myCoolMessage', String(error));
+
+  windowOnErrorWasCalled = true;
+};
+
+if (window.testRunner) {
+  window.testRunner.waitUntilDone();
+}
+setTimeout(() => {
+  expect(windowOnErrorWasCalled);
+  if (window.testRunner) {
+    window.testRunner.notifyDone();
+  }
+}, 1);
+throw new Error('myCoolMessage');
+
+</script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/12-1-before-pseudoelement-responds-to-style-change-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/12-1-before-pseudoelement-responds-to-style-change-expected.png
new file mode 100644
index 0000000..c99808e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/12-1-before-pseudoelement-responds-to-style-change-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/12-1-before-pseudoelement-responds-to-style-change.html b/src/cobalt/layout_tests/testdata/css-2-1/12-1-before-pseudoelement-responds-to-style-change.html
new file mode 100644
index 0000000..0c7a6a5
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/12-1-before-pseudoelement-responds-to-style-change.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<!--
+ | Use the :before pseudo element and checks that it partially re-lays out correctly
+ | after that element's style is changed.
+-->
+<head>
+  <style>
+    body {
+      background-color: white;
+    }
+
+    .test-block::before {
+      content: "";
+      background-color: red;
+      width:100px;
+      height:100px;
+      position: absolute;
+    }
+
+    .focused::before {
+      background-color: green;
+    }
+  </style>
+</head>
+<body>
+  <div class="test-block"></div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.addEventListener('load', function() {
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      var testBlock = document.getElementsByClassName('test-block')[0];
+      testBlock.classList.add('focused');
+
+      if (window.testRunner) {
+        window.testRunner.notifyDone();
+      }
+    });
+  </script>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
index 37e4c1e..05cf6f7 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
@@ -3,10 +3,10 @@
 10-1-absolute-positioned-elements-compute-used-values-from-containing-block
 10-1-absolute-positioned-elements-container-block-is-absolute-positioned-ancestor
 10-1-absolute-positioned-elements-do-not-effect-containing-block-size
+10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements
 10-1-containing-block-should-be-ancestor-padding-edge-for-absolutely-positioned-elements
 10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements
 10-1-containing-block-should-be-ancestor-padding-edge-for-percentage-of-absolutely-positioned-elements
-10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements
 10-1-non-positioned-stacking-context-above-containing-block-should-apply-proper-offset-to-children
 10-1-positioned-stacking-context-above-fixed-containing-block-should-apply-proper-offset-to-children
 10-3-1-auto-margin-should-become-zero-in-inline-non-replaced-elements
@@ -20,8 +20,8 @@
 10-3-3-one-auto-should-follow-from-equality
 10-3-4-block-level-replaced-box-margins-should-be-calculated-as-for-non-replaced-box
 10-3-4-block-level-replaced-box-width-should-be-calculated-as-for-inline-replaced-box
-10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit
 10-3-7-absolute-element-children-should-shrink-to-fit
+10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit
 10-3-7-absolute-position-elements-solve-for-height-when-top-and-bottom-are-specified
 10-3-7-absolute-position-elements-solve-for-width-when-left-and-right-are-specified
 10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block
@@ -88,6 +88,7 @@
 12-1-after-pseudoelement-simple
 12-1-before-pseudoelement
 12-1-before-pseudoelement-does-not-inherit-inline-style
+12-1-before-pseudoelement-responds-to-style-change
 16-2-text-align-can-be-left-center-right
 16-2-text-align-should-not-apply-when-child-boxes-overflow
 18-4-outline
@@ -95,8 +96,8 @@
 18-4-outline-overflow-hidden
 8-1-margin-should-be-transparent
 8-3-margin-percentage-should-refer-containing-block-width
-8-3-negative_margins-should-be-allowed-to-produce_negative-box-widths
 8-3-negative-margins-should-be-allowed
+8-3-negative_margins-should-be-allowed-to-produce_negative-box-widths
 8-3-vertical-margins-should-not-apply-to-non-replaced-inline-elements
 8-4-padding-color-should-be-specified-by-background
 8-4-padding-image-should-be-specified-by-background
@@ -152,11 +153,11 @@
 9-9-1-fixed-position-element-should-not-appear-on-top-of-later-siblings
 9-9-1-nearest-ancestor-stacking-context-should-contain-element
 9-9-1-negative-z-indices
-9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling
 9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context
+9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling
 9-9-1-simple-positive-z-indices
 9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees
 9-9-1-stacking-contexts-and-containing-blocks-with-transforms
-9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks
 9-9-1-stacking-contexts-differ-from-containing-blocks
+9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks
 9-9-1-z-index-should-only-be-applied-to-positioned-elements
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-ends-on-display-none-for-pseudoelement-expected.png b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-ends-on-display-none-for-pseudoelement-expected.png
new file mode 100644
index 0000000..87b6ee6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-ends-on-display-none-for-pseudoelement-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-ends-on-display-none-for-pseudoelement.html b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-ends-on-display-none-for-pseudoelement.html
new file mode 100644
index 0000000..a52de39
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-ends-on-display-none-for-pseudoelement.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<!--
+ | Test case which ensures that when a pseudo-element whose parent element has
+ | its display set to none, all running transitions on the pseudo-element are
+ | immediately ended.
+ | While the specification doesn't seem to say anything about this, it is
+ | how Chrome and Firefox both work, and it is also intuitively in line with
+ | the following behavior specified for CSS Animations:
+ | From https://www.w3.org/TR/2013/WD-css3-animations-20130219/#animations,
+ | "Setting the display property to ‘none’ will terminate any running animation
+ |  applied to the element and its descendants. If an element has a display of
+ |  ‘none’, updating display to a value other than ‘none’ will start all
+ |  animations applied to the element by the ‘animation-name’ property, as well
+ |  as all animations applied to descendants with display other than ‘none’."
+ -->
+<html>
+<head>
+  <style>
+    #block {
+      width: 100px;
+      height: 100px;
+      background-color: yellow;
+      font-size: 30px;
+    }
+    #block::after {
+      content: "";
+      display:block;
+      transform: translateX(100px);
+      width: 100px;
+      height: 100px;
+      background-color: #0f0;
+      transition: background-color 1s linear;
+    }
+  </style>
+</head>
+<body>
+  <div id="block"></div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    var blockDiv = document.getElementById('block');
+
+    window.addEventListener('load', function() {
+      if (window.testRunner) {
+        // Do a layout upon the load event so that we setup our source styles
+        // that we will be transitioning from.
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      // Modify the rule that targets the pseudo-element, to trigger the
+      // transition.
+      var sheet = document.styleSheets[0];
+      var rules = sheet.cssRules;
+      rules[1].style.backgroundColor = '#00f';
+
+      if (window.testRunner) {
+        // Do a layout to start the transition.
+        window.testRunner.DoNonMeasuredLayout();
+        window.testRunner.AdvanceClockByMs(500);
+      }
+
+      blockDiv.style.display = "none";
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      blockDiv.style.display = "block";
+
+      if (window.testRunner) {
+        // Measure that the transition should now appear to be fully completed.
+        window.testRunner.notifyDone();
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck-expected.png b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck-expected.png
new file mode 100644
index 0000000..87b6ee6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck.html b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck.html
new file mode 100644
index 0000000..cd2f0d7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<!--
+ | Test case to handle a particular bug where a DCHECK could result when a
+ | pseudoelement's animations/transitions are not cleared properly while
+ | moving from a parent's "display: none" to the main div's "display: none".
+ | In the end, we expect the final result to be that no DCHECKs get set off,
+ | and that the transition appears to have been fully completed.
+ -->
+<html>
+<head>
+  <style>
+    #block {
+      width: 100px;
+      height: 100px;
+      background-color: yellow;
+      font-size: 30px;
+    }
+    #block::after {
+      content: "";
+      display:block;
+      transform: translateX(100px);
+      width: 100px;
+      height: 100px;
+      background-color: #0f0;
+      transition: background-color 1s linear;
+    }
+  </style>
+</head>
+<body>
+  <div id="parent">
+    <div id="block"></div>
+  </div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    var parentDiv = document.getElementById('parent');
+    var blockDiv = document.getElementById('block');
+
+    window.addEventListener('load', function() {
+      if (window.testRunner) {
+        // Do a layout upon the load event so that we setup our source styles
+        // that we will be transitioning from.
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      // Modify the rule that targets the pseudo-element, to trigger the
+      // transition.
+      var sheet = document.styleSheets[0];
+      var rules = sheet.cssRules;
+      rules[1].style.backgroundColor = '#00f';
+
+      if (window.testRunner) {
+        // Do a layout to start the transition.
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      // Set the parent div's display to none.  This should have the effect
+      // of hiding the parent div and cancelling all descendant animations
+      // and transitions, like the one we started above.
+      parentDiv.style.display = "none";
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      // Reveal the parent div but hide the main div at the same time.  This
+      // should result in the main div having its computed style calculated
+      // again, even though we do nothing with it because it still has its
+      // display set to none.  This is mostly just a sanity check to make
+      // sure no DCHECKs go off, as a bug had been found in this case before.
+      parentDiv.style.display = "block";
+      blockDiv.style.display = "none";
+
+      if (window.testRunner) {
+        // Run the transition a bit so that it is started but not completed.
+        window.testRunner.AdvanceClockByMs(500);
+        window.testRunner.DoNonMeasuredLayout();
+
+        blockDiv.style.display = "block";
+
+        // Measure that the transition should now appear to be fully completed.
+        window.testRunner.notifyDone();
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/5-simple-transition-for-pseudoelement-expected.png b/src/cobalt/layout_tests/testdata/css3-transitions/5-simple-transition-for-pseudoelement-expected.png
new file mode 100644
index 0000000..39852d5
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/5-simple-transition-for-pseudoelement-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/5-simple-transition-for-pseudoelement.html b/src/cobalt/layout_tests/testdata/css3-transitions/5-simple-transition-for-pseudoelement.html
new file mode 100644
index 0000000..db1aa07
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/5-simple-transition-for-pseudoelement.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!--
+ | Test case which ensures that when a pseudo-element can handle CSS
+ | transitions being applied to it properly.
+ -->
+<html>
+<head>
+  <style>
+    #block {
+      width: 100px;
+      height: 100px;
+      background-color: yellow;
+      font-size: 30px;
+    }
+    #block::after {
+      content: "";
+      display:block;
+      transform: translateX(100px);
+      width: 100px;
+      height: 100px;
+      background-color: #0f0;
+      transition: background-color 1s linear;
+    }
+
+  </style>
+</head>
+<body>
+  <div id="block"></div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    var blockDiv = document.getElementById('block');
+
+    window.addEventListener('load', function() {
+      if (window.testRunner) {
+        // Do a layout upon the load event so that we setup our source styles
+        // that we will be transitioning from.
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      // Modify the rule that targets the pseudo-element, to trigger the
+      // transition.
+      var sheet = document.styleSheets[0];
+      var rules = sheet.cssRules;
+      rules[1].style.backgroundColor = '#00f';
+
+      if (window.testRunner) {
+        // Run the transition a bit so that it is started but not completed.
+        window.testRunner.DoNonMeasuredLayout();
+        window.testRunner.AdvanceClockByMs(500);
+
+        // Measure that the transition should now appear half-way finished.
+        window.testRunner.notifyDone();
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt
index f5ae277..868bea5 100644
--- a/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-transitions/layout_tests.txt
@@ -1,6 +1,9 @@
 2-transition-ends-on-display-none
+2-transition-ends-on-display-none-for-pseudoelement
 2-transition-ends-on-display-none-from-ancestor
 2-transition-start-during-display-none-does-not-transition
 2-transition-start-during-display-none-does-not-transition-from-ancestor
+2-transition-started-on-display-none-pseudoelement-does-not-dcheck
 5-multiple-transitions-should-fire-in-correct-order
 5-simple-transition
+5-simple-transition-for-pseudoelement
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/cobalt_special/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/cobalt_special/web_platform_tests.txt
index e16a414..25ffd6c 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/cobalt_special/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/cobalt_special/web_platform_tests.txt
@@ -1,3 +1,4 @@
 # Cobalt's special tests that borrows WPT infrastructures.
 
 origin-clean.htm,PASS
+preflight-cache-2.htm,PASS
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/web_platform_test_parser.cc b/src/cobalt/layout_tests/web_platform_test_parser.cc
index 68e7185..4cbabfe 100644
--- a/src/cobalt/layout_tests/web_platform_test_parser.cc
+++ b/src/cobalt/layout_tests/web_platform_test_parser.cc
@@ -110,7 +110,7 @@
     bool success = global_environment->EvaluateScript(
         script::SourceCode::CreateSourceCode(
             precondition, base::SourceLocation(__FILE__, __LINE__, 1)),
-        false /*mute_errors*/, &result);
+        &result);
 
     if (!success) {
       DLOG(ERROR) << "Failed to evaluate precondition: "
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index 50bbaaa..281803e 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -199,6 +199,8 @@
   scoped_ptr<base::Value> root;
   base::JSONReader reader;
   root.reset(reader.ReadToValue(json_results));
+  // Expect that parsing test result succeeded.
+  EXPECT_EQ(base::JSONReader::JSON_NO_ERROR, reader.error_code());
   if (!root) {
     // Unparseable JSON, or empty string.
     return test_results;
diff --git a/src/cobalt/loader/cors_preflight_cache.cc b/src/cobalt/loader/cors_preflight_cache.cc
index 32d53b3..e61b134 100644
--- a/src/cobalt/loader/cors_preflight_cache.cc
+++ b/src/cobalt/loader/cors_preflight_cache.cc
@@ -149,17 +149,20 @@
 void CORSPreflightCache::ClearObsoleteEntries() {
   while (expiration_time_heap_.size() > 0 &&
          expiration_time_heap_.top().expiration_time < base::Time::Now()) {
-    DCHECK(content_.find(expiration_time_heap_.top().url_str) !=
-           content_.end());
-    auto url_iter = content_.find(expiration_time_heap_.top().url_str);
-    DCHECK(url_iter->second.find(expiration_time_heap_.top().origin) !=
-           url_iter->second.end());
-    auto entry_iter = url_iter->second.find(expiration_time_heap_.top().origin);
+    auto heap_top = expiration_time_heap_.top();
+    expiration_time_heap_.pop();
+    auto url_iter = content_.find(heap_top.url_str);
+    if (url_iter == content_.end()) {
+      continue;
+    }
+    auto entry_iter = url_iter->second.find(heap_top.origin);
+    if (entry_iter == url_iter->second.end()) {
+      continue;
+    }
     // The entry could have been updated and should only delete obselete ones.
     if (entry_iter->second->expiration_time < base::Time::Now()) {
       url_iter->second.erase(entry_iter);
     }
-    expiration_time_heap_.pop();
   }
 }
 
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index 0d12b57..5994acc 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -275,8 +275,8 @@
       return "image/webp";
     case ImageDecoder::kImageTypeInvalid:
       return NULL;
-    default: { NOTREACHED(); }
   }
+  NOTREACHED();
   return NULL;
 }
 
diff --git a/src/cobalt/loader/image/image_encoder.cc b/src/cobalt/loader/image/image_encoder.cc
new file mode 100644
index 0000000..da3f832
--- /dev/null
+++ b/src/cobalt/loader/image/image_encoder.cc
@@ -0,0 +1,69 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/loader/image/image_encoder.h"
+
+#include "cobalt/renderer/test/png_utils/png_encode.h"
+#include "third_party/libwebp/webp/encode.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+using cobalt::renderer::test::png_utils::EncodeRGBAToBuffer;
+
+scoped_array<uint8> WriteRGBAPixelsToPNG(const uint8* const pixel_data,
+                                         const math::Size& dimensions,
+                                         size_t* out_num_bytes) {
+  const int kRGBABytesPerPixel = 4;
+  const int kPitchSizeInBytes = dimensions.width() * kRGBABytesPerPixel;
+  return EncodeRGBAToBuffer(pixel_data, dimensions.width(), dimensions.height(),
+                            kPitchSizeInBytes, out_num_bytes);
+}
+
+scoped_refptr<loader::image::EncodedStaticImage> CompressRGBAImage(
+    loader::image::EncodedStaticImage::ImageFormat desired_format,
+    const uint8* const image_data, const math::Size& dimensions) {
+  using ImageFormat = loader::image::EncodedStaticImage::ImageFormat;
+  switch (desired_format) {
+    case ImageFormat::kPNG: {
+      size_t num_bytes;
+      scoped_array<uint8> png_data =
+          WriteRGBAPixelsToPNG(image_data, dimensions, &num_bytes);
+      DCHECK_LT(static_cast<int>(num_bytes), kint32max);
+
+      return scoped_refptr<loader::image::EncodedStaticImage>(
+          new loader::image::EncodedStaticImage(desired_format, png_data.Pass(),
+                                                static_cast<int>(num_bytes),
+                                                dimensions));
+    }
+    case ImageFormat::kWEBP:
+      NOTIMPLEMENTED();
+  }
+
+  return nullptr;
+}
+
+EncodedStaticImage::EncodedStaticImage(
+    image::EncodedStaticImage::ImageFormat image_format,
+    scoped_array<uint8> memory, uint32 size_in_bytes,
+    const math::Size& image_dimensions)
+    : image_format_(image_format),
+      size_in_bytes_(size_in_bytes),
+      image_dimensions_(image_dimensions),
+      memory_(memory.Pass()) {}
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
diff --git a/src/cobalt/loader/image/image_encoder.h b/src/cobalt/loader/image/image_encoder.h
new file mode 100644
index 0000000..e0e9bd3
--- /dev/null
+++ b/src/cobalt/loader/image/image_encoder.h
@@ -0,0 +1,65 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_LOADER_IMAGE_IMAGE_ENCODER_H_
+#define COBALT_LOADER_IMAGE_IMAGE_ENCODER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/loader/image/image.h"
+#include "cobalt/math/size.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+class EncodedStaticImage : public Image {
+ public:
+  enum class ImageFormat {
+    kPNG,
+    kWEBP,
+  };
+
+  EncodedStaticImage(ImageFormat image_format, scoped_array<uint8> memory,
+                     uint32 size_in_bytes, const math::Size& image_dimensions);
+
+  ImageFormat GetImageFormat() const { return image_format_; }
+  uint8_t* GetMemory() { return memory_.get(); }
+
+  uint32 GetEstimatedSizeInBytes() const override { return size_in_bytes_; }
+  const math::Size& GetSize() const override { return image_dimensions_; }
+  bool IsOpaque() const override { return false; }
+  bool IsAnimated() const override { return false; }
+
+ private:
+  ImageFormat image_format_;
+  uint32 size_in_bytes_;
+  math::Size image_dimensions_;
+  scoped_array<uint8> memory_;
+};
+
+scoped_array<uint8> WriteRGBAPixelsToPNG(const uint8* const pixel_data,
+                                         const math::Size& dimensions,
+                                         size_t* out_num_bytes);
+
+scoped_refptr<EncodedStaticImage> CompressRGBAImage(
+    EncodedStaticImage::ImageFormat desired_format,
+    const uint8* const image_data, const math::Size& dimensions);
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // COBALT_LOADER_IMAGE_IMAGE_ENCODER_H_
diff --git a/src/cobalt/loader/image/sandbox/sandbox.gyp b/src/cobalt/loader/image/sandbox/sandbox.gyp
index 88e4597..45057d7 100644
--- a/src/cobalt/loader/image/sandbox/sandbox.gyp
+++ b/src/cobalt/loader/image/sandbox/sandbox.gyp
@@ -45,7 +45,7 @@
       'variables': {
         'executable_name': 'image_decoder_sandbox',
       },
-      'includes': [ '../../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index a7e4105..6f3d18f 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -56,6 +56,8 @@
         'image/image_decoder_starboard.h',
         'image/image_decoder.cc',
         'image/image_decoder.h',
+        'image/image_encoder.cc',
+        'image/image_encoder.h',
         'image/image.h',
         'image/jpeg_image_decoder.cc',
         'image/jpeg_image_decoder.h',
@@ -94,6 +96,7 @@
         '<(DEPTH)/cobalt/csp/csp.gyp:csp',
         '<(DEPTH)/cobalt/network/network.gyp:network',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
+	'<(DEPTH)/cobalt/renderer/test/png_utils/png_utils.gyp:png_utils',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
         '<(DEPTH)/third_party/libjpeg/libjpeg.gyp:libjpeg',
         '<(DEPTH)/third_party/libpng/libpng.gyp:libpng',
@@ -160,7 +163,7 @@
       'variables': {
         'executable_name': 'loader_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
diff --git a/src/cobalt/math/math.gyp b/src/cobalt/math/math.gyp
index a281754..e0c2d13 100644
--- a/src/cobalt/math/math.gyp
+++ b/src/cobalt/math/math.gyp
@@ -108,7 +108,7 @@
       'variables': {
         'executable_name': 'math_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/media/base/mime_util_internal.cc b/src/cobalt/media/base/mime_util_internal.cc
index fd6d7b1..6c03097 100644
--- a/src/cobalt/media/base/mime_util_internal.cc
+++ b/src/cobalt/media/base/mime_util_internal.cc
@@ -251,7 +251,15 @@
       return kCodecVP9;
     case MimeUtil::THEORA:
       return kCodecTheora;
-    default:
+    case MimeUtil::INVALID_CODEC:
+    case MimeUtil::PCM:
+    case MimeUtil::MP3:
+    case MimeUtil::AC3:
+    case MimeUtil::EAC3:
+    case MimeUtil::MPEG2_AAC:
+    case MimeUtil::MPEG4_AAC:
+    case MimeUtil::VORBIS:
+    case MimeUtil::OPUS:
       break;
   }
   return kUnknownVideoCodec;
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h
index 5530b30..da449b7 100644
--- a/src/cobalt/media/base/pipeline.h
+++ b/src/cobalt/media/base/pipeline.h
@@ -77,7 +77,8 @@
   static scoped_refptr<Pipeline> Create(
       PipelineWindow window,
       const scoped_refptr<base::MessageLoopProxy>& message_loop,
-      MediaLog* media_log, VideoFrameProvider* video_frame_provider);
+      bool allow_resume_after_suspend, MediaLog* media_log,
+      VideoFrameProvider* video_frame_provider);
 
   virtual ~Pipeline() {}
 
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 9e6741b..028145b 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -77,7 +77,7 @@
   // Constructs a media pipeline that will execute on |message_loop|.
   SbPlayerPipeline(PipelineWindow window,
                    const scoped_refptr<base::MessageLoopProxy>& message_loop,
-                   MediaLog* media_log,
+                   bool allow_resume_after_suspend, MediaLog* media_log,
                    VideoFrameProvider* video_frame_provider);
   ~SbPlayerPipeline() override;
 
@@ -165,6 +165,9 @@
   // Message loop used to execute pipeline tasks.  It is thread-safe.
   scoped_refptr<base::MessageLoopProxy> message_loop_;
 
+  // Whether we should save DecoderBuffers for resume after suspend.
+  const bool allow_resume_after_suspend_;
+
   // The window this player associates with.  It should only be assigned in the
   // dtor and accesed once by SbPlayerCreate().
   PipelineWindow window_;
@@ -254,9 +257,11 @@
 SbPlayerPipeline::SbPlayerPipeline(
     PipelineWindow window,
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
-    MediaLog* media_log, VideoFrameProvider* video_frame_provider)
+    bool allow_resume_after_suspend, MediaLog* media_log,
+    VideoFrameProvider* video_frame_provider)
     : window_(window),
       message_loop_(message_loop),
+      allow_resume_after_suspend_(allow_resume_after_suspend),
       natural_size_(0, 0),
       volume_(1.f),
       playback_rate_(0.f),
@@ -753,7 +758,7 @@
     DLOG(INFO) << "StarboardPlayer created with url: " << source_url;
     player_.reset(new StarboardPlayer(
         message_loop_, source_url, window_, this, set_bounds_helper_.get(),
-        *decode_to_texture_output_mode_,
+        allow_resume_after_suspend_, *decode_to_texture_output_mode_,
         on_encrypted_media_init_data_encountered_cb_, video_frame_provider_));
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
@@ -828,8 +833,8 @@
     base::AutoLock auto_lock(lock_);
     player_.reset(new StarboardPlayer(
         message_loop_, audio_config, video_config, window_, drm_system, this,
-        set_bounds_helper_.get(), *decode_to_texture_output_mode_,
-        video_frame_provider_));
+        set_bounds_helper_.get(), allow_resume_after_suspend_,
+        *decode_to_texture_output_mode_, video_frame_provider_));
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
   }
@@ -1196,9 +1201,10 @@
 scoped_refptr<Pipeline> Pipeline::Create(
     PipelineWindow window,
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
-    MediaLog* media_log, VideoFrameProvider* video_frame_provider) {
-  return new SbPlayerPipeline(window, message_loop, media_log,
-                              video_frame_provider);
+    bool allow_resume_after_suspend, MediaLog* media_log,
+    VideoFrameProvider* video_frame_provider) {
+  return new SbPlayerPipeline(window, message_loop, allow_resume_after_suspend,
+                              media_log, video_frame_provider);
 }
 
 }  // namespace media
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 3850c6e..35ea7fd 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -85,7 +85,8 @@
 StarboardPlayer::StarboardPlayer(
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
     const std::string& url, SbWindow window, Host* host,
-    SbPlayerSetBoundsHelper* set_bounds_helper, bool prefer_decode_to_texture,
+    SbPlayerSetBoundsHelper* set_bounds_helper, bool allow_resume_after_suspend,
+    bool prefer_decode_to_texture,
     const OnEncryptedMediaInitDataEncounteredCB&
         on_encrypted_media_init_data_encountered_cb,
     VideoFrameProvider* const video_frame_provider)
@@ -94,16 +95,9 @@
       callback_helper_(
           new CallbackHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this))),
       window_(window),
-      drm_system_(kSbDrmSystemInvalid),
       host_(host),
       set_bounds_helper_(set_bounds_helper),
-      frame_width_(1),
-      frame_height_(1),
-      ticket_(SB_PLAYER_INITIAL_TICKET),
-      volume_(1.0),
-      playback_rate_(0.0),
-      seek_pending_(false),
-      state_(kPlaying),
+      allow_resume_after_suspend_(allow_resume_after_suspend),
       on_encrypted_media_init_data_encountered_cb_(
           on_encrypted_media_init_data_encountered_cb),
       video_frame_provider_(video_frame_provider) {
@@ -127,7 +121,8 @@
     const AudioDecoderConfig& audio_config,
     const VideoDecoderConfig& video_config, SbWindow window,
     SbDrmSystem drm_system, Host* host,
-    SbPlayerSetBoundsHelper* set_bounds_helper, bool prefer_decode_to_texture,
+    SbPlayerSetBoundsHelper* set_bounds_helper, bool allow_resume_after_suspend,
+    bool prefer_decode_to_texture,
     VideoFrameProvider* const video_frame_provider)
     : message_loop_(message_loop),
       callback_helper_(
@@ -138,13 +133,7 @@
       drm_system_(drm_system),
       host_(host),
       set_bounds_helper_(set_bounds_helper),
-      frame_width_(1),
-      frame_height_(1),
-      ticket_(SB_PLAYER_INITIAL_TICKET),
-      volume_(1.0),
-      playback_rate_(0.0),
-      seek_pending_(false),
-      state_(kPlaying),
+      allow_resume_after_suspend_(allow_resume_after_suspend),
       video_frame_provider_(video_frame_provider) {
   DCHECK(video_config.IsValidConfig());
   DCHECK(host_);
@@ -192,8 +181,13 @@
   DCHECK(frame_height);
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerInfo out_player_info;
   SbPlayerGetInfo(player_, &out_player_info);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerInfo2 out_player_info;
+  SbPlayerGetInfo2(player_, &out_player_info);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   frame_width_ = out_player_info.frame_width;
   frame_height_ = out_player_info.frame_height;
 
@@ -208,11 +202,16 @@
   DCHECK(message_loop_->BelongsToCurrentThread());
   DCHECK(buffer);
 
-  decoder_buffer_cache_.AddBuffer(type, buffer);
+  if (allow_resume_after_suspend_) {
+    decoder_buffer_cache_.AddBuffer(type, buffer);
 
-  if (state_ != kSuspended) {
-    WriteNextBufferFromCache(type);
+    if (state_ != kSuspended) {
+      WriteNextBufferFromCache(type);
+    }
+
+    return;
   }
+  WriteBufferInternal(type, buffer);
 }
 
 #endif  // !SB_HAS(PLAYER_WITH_URL)
@@ -262,8 +261,13 @@
   DCHECK(SbPlayerIsValid(player_));
 
   ++ticket_;
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerSeek(player_, SB_TIME_TO_SB_MEDIA_TIME(time.InMicroseconds()),
                ticket_);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerSeek2(player_, time.InMicroseconds(), ticket_);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+
   seek_pending_ = false;
   SbPlayerSetPlaybackRate(player_, playback_rate_);
 }
@@ -322,14 +326,9 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerInfo info;
   SbPlayerGetInfo(player_, &info);
-  if (video_frames_decoded) {
-    *video_frames_decoded = info.total_video_frames;
-  }
-  if (video_frames_dropped) {
-    *video_frames_dropped = info.dropped_video_frames;
-  }
   if (media_time) {
     *media_time = base::TimeDelta::FromMicroseconds(
         SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts));
@@ -342,6 +341,29 @@
     *buffer_length_time = base::TimeDelta::FromMicroseconds(
         SB_MEDIA_TIME_TO_SB_TIME(info.buffer_duration_pts));
   }
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerInfo2 info;
+  SbPlayerGetInfo2(player_, &info);
+  if (media_time) {
+    *media_time =
+        base::TimeDelta::FromMicroseconds(info.current_media_timestamp);
+  }
+  if (buffer_start_time) {
+    *buffer_start_time =
+        base::TimeDelta::FromMicroseconds(info.buffer_start_timestamp);
+  }
+  if (buffer_length_time) {
+    *buffer_length_time =
+        base::TimeDelta::FromMicroseconds(info.buffer_duration);
+  }
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+
+  if (video_frames_decoded) {
+    *video_frames_decoded = info.total_video_frames;
+  }
+  if (video_frames_dropped) {
+    *video_frames_dropped = info.dropped_video_frames;
+  }
   if (frame_width) {
     *frame_width = info.frame_width;
   }
@@ -372,18 +394,27 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerInfo info;
   SbPlayerGetInfo(player_, &info);
+  if (media_time) {
+    *media_time = base::TimeDelta::FromMicroseconds(
+        SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts));
+  }
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerInfo2 info;
+  SbPlayerGetInfo2(player_, &info);
+  if (media_time) {
+    *media_time =
+        base::TimeDelta::FromMicroseconds(info.current_media_timestamp);
+  }
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   if (video_frames_decoded) {
     *video_frames_decoded = info.total_video_frames;
   }
   if (video_frames_dropped) {
     *video_frames_dropped = info.dropped_video_frames;
   }
-  if (media_time) {
-    *media_time = base::TimeDelta::FromMicroseconds(
-        SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts));
-  }
 }
 
 #if SB_HAS(PLAYER_WITH_URL)
@@ -395,11 +426,18 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerInfo info;
   SbPlayerGetInfo(player_, &info);
   DCHECK_NE(info.duration_pts, SB_PLAYER_NO_DURATION);
   return base::TimeDelta::FromMicroseconds(
       SB_MEDIA_TIME_TO_SB_TIME(info.duration_pts));
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerInfo2 info;
+  SbPlayerGetInfo2(player_, &info);
+  DCHECK_NE(info.duration, SB_PLAYER_NO_DURATION);
+  return base::TimeDelta::FromMicroseconds(info.duration);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 }
 
 base::TimeDelta StarboardPlayer::GetStartDate() {
@@ -410,8 +448,13 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerInfo info = {};
   SbPlayerGetInfo(player_, &info);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerInfo2 info = {};
+  SbPlayerGetInfo2(player_, &info);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   return base::TimeDelta::FromMicroseconds(info.start_date);
 }
 
@@ -437,12 +480,19 @@
 
   state_ = kSuspended;
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerInfo info;
   SbPlayerGetInfo(player_, &info);
-  cached_video_frames_decoded_ = info.total_video_frames;
-  cached_video_frames_dropped_ = info.dropped_video_frames;
   preroll_timestamp_ = base::TimeDelta::FromMicroseconds(
       SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts));
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerInfo2 info;
+  SbPlayerGetInfo2(player_, &info);
+  preroll_timestamp_ =
+      base::TimeDelta::FromMicroseconds(info.current_media_timestamp);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  cached_video_frames_decoded_ = info.total_video_frames;
+  cached_video_frames_dropped_ = info.dropped_video_frames;
 
   set_bounds_helper_->SetPlayer(NULL);
   video_frame_provider_->SetOutputMode(VideoFrameProvider::kOutputModeInvalid);
@@ -515,7 +565,10 @@
   DCHECK(!on_encrypted_media_init_data_encountered_cb_.is_null());
   DLOG(INFO) << "SbPlayerCreateWithUrl passed url " << url;
   player_ = SbPlayerCreateWithUrl(
-      url.c_str(), window_, SB_PLAYER_NO_DURATION,
+      url.c_str(), window_,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+      SB_PLAYER_NO_DURATION,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
       &StarboardPlayer::PlayerStatusCB,
       &StarboardPlayer::EncryptedMediaInitDataEncounteredCB,
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
@@ -558,16 +611,20 @@
 
   DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
 
-  player_ = SbPlayerCreate(
-      window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
-      has_audio ? &audio_header : NULL, &StarboardPlayer::DeallocateSampleCB,
-      &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB,
+  player_ = SbPlayerCreate(window_, video_codec, audio_codec,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+                           SB_PLAYER_NO_DURATION,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+                           drm_system_, has_audio ? &audio_header : NULL,
+                           &StarboardPlayer::DeallocateSampleCB,
+                           &StarboardPlayer::DecoderStatusCB,
+                           &StarboardPlayer::PlayerStatusCB,
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
-      &StarboardPlayer::PlayerErrorCB,
+                           &StarboardPlayer::PlayerErrorCB,
 #endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
-      this, output_mode_,
-      ShellMediaPlatform::Instance()
-          ->GetSbDecodeTargetGraphicsContextProvider());
+                           this, output_mode_,
+                           ShellMediaPlatform::Instance()
+                               ->GetSbDecodeTargetGraphicsContextProvider());
   DCHECK(SbPlayerIsValid(player_));
 
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
@@ -594,6 +651,11 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+  WriteBufferInternal(type, buffer);
+}
+
+void StarboardPlayer::WriteBufferInternal(
+    DemuxerStream::Type type, const scoped_refptr<DecoderBuffer>& buffer) {
   if (buffer->end_of_stream()) {
     SbPlayerWriteEndOfStream(player_, DemuxerStreamTypeToSbMediaType(type));
     return;
@@ -628,6 +690,7 @@
     FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
   }
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SbPlayerWriteSample(
       player_, DemuxerStreamTypeToSbMediaType(type),
 #if SB_API_VERSION >= 6
@@ -640,6 +703,14 @@
       SB_TIME_TO_SB_MEDIA_TIME(buffer->timestamp().InMicroseconds()),
       type == DemuxerStream::VIDEO ? &video_info : NULL,
       drm_info.subsample_count > 0 ? &drm_info : NULL);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SbPlayerWriteSample2(player_, DemuxerStreamTypeToSbMediaType(type),
+                       allocations.buffers(), allocations.buffer_sizes(),
+                       allocations.number_of_buffers(),
+                       buffer->timestamp().InMicroseconds(),
+                       type == DemuxerStream::VIDEO ? &video_info : NULL,
+                       drm_info.subsample_count > 0 ? &drm_info : NULL);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 }
 
 #endif  // SB_HAS(PLAYER_WITH_URL)
@@ -739,9 +810,13 @@
     if (ticket_ == SB_PLAYER_INITIAL_TICKET) {
       ++ticket_;
     }
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
     SbPlayerSeek(player_,
                  SB_TIME_TO_SB_MEDIA_TIME(preroll_timestamp_.InMicroseconds()),
                  ticket_);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+    SbPlayerSeek2(player_, preroll_timestamp_.InMicroseconds(), ticket_);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
     SetVolume(volume_);
     SbPlayerSetPlaybackRate(player_, playback_rate_);
     return;
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index a68f733..9079e14 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -61,6 +61,7 @@
   StarboardPlayer(const scoped_refptr<base::MessageLoopProxy>& message_loop,
                   const std::string& url, SbWindow window, Host* host,
                   SbPlayerSetBoundsHelper* set_bounds_helper,
+                  bool allow_resume_after_suspend,
                   bool prefer_decode_to_texture,
                   const OnEncryptedMediaInitDataEncounteredCB&
                       encrypted_media_init_data_encountered_cb,
@@ -71,6 +72,7 @@
                   const VideoDecoderConfig& video_config, SbWindow window,
                   SbDrmSystem drm_system, Host* host,
                   SbPlayerSetBoundsHelper* set_bounds_helper,
+                  bool allow_resume_after_suspend,
                   bool prefer_decode_to_texture,
                   VideoFrameProvider* const video_frame_provider);
 #endif  // SB_HAS(PLAYER_WITH_URL)
@@ -160,6 +162,8 @@
   void CreatePlayer();
 
   void WriteNextBufferFromCache(DemuxerStream::Type type);
+  void WriteBufferInternal(DemuxerStream::Type type,
+                           const scoped_refptr<DecoderBuffer>& buffer);
 #endif  // SB_HAS(PLAYER_WITH_URL)
 
   void UpdateBounds_Locked();
@@ -208,21 +212,21 @@
   AudioDecoderConfig audio_config_;
   VideoDecoderConfig video_config_;
   const SbWindow window_;
-  SbDrmSystem drm_system_;
+  SbDrmSystem drm_system_ = kSbDrmSystemInvalid;
   Host* const host_;
   // Consider merge |SbPlayerSetBoundsHelper| into CallbackHelper.
   SbPlayerSetBoundsHelper* const set_bounds_helper_;
+  const bool allow_resume_after_suspend_;
 
   // The following variables are only changed or accessed from the
   // |message_loop_|.
-  int frame_width_;
-  int frame_height_;
+  int frame_width_ = 1;
+  int frame_height_ = 1;
   DecodingBuffers decoding_buffers_;
-  int ticket_;
-  float volume_;
-  double playback_rate_;
-  bool paused_;
-  bool seek_pending_;
+  int ticket_ = SB_PLAYER_INITIAL_TICKET;
+  float volume_ = 1.0f;
+  double playback_rate_ = 0.0;
+  bool seek_pending_ = false;
   DecoderBufferCache decoder_buffer_cache_;
 
   // The following variables can be accessed from GetInfo(), which can be called
@@ -232,7 +236,7 @@
   // Stores the |z_index| and |rect| parameters of the latest SetBounds() call.
   base::optional<int> set_bounds_z_index_;
   base::optional<gfx::Rect> set_bounds_rect_;
-  State state_;
+  State state_ = kPlaying;
   SbPlayer player_;
   uint32 cached_video_frames_decoded_;
   uint32 cached_video_frames_dropped_;
diff --git a/src/cobalt/media/filters/stream_parser_factory.cc b/src/cobalt/media/filters/stream_parser_factory.cc
index 6c6119e..79defc7 100644
--- a/src/cobalt/media/filters/stream_parser_factory.cc
+++ b/src/cobalt/media/filters/stream_parser_factory.cc
@@ -272,12 +272,13 @@
 #endif
       if (video_codecs) video_codecs->push_back(codec_info->tag);
       return true;
-    default:
-      // Not audio or video, so skip it.
-      DVLOG(1) << "CodecInfo type of " << codec_info->type
-               << " should not be specified in a SupportedTypes list";
-      return false;
+    case CodecInfo::UNKNOWN:
+      break;
   }
+  // Not audio or video, so skip it.
+  DVLOG(1) << "CodecInfo type of " << codec_info->type
+           << " should not be specified in a SupportedTypes list";
+  return false;
 }
 
 // Checks to see if the specified |type| and |codecs| list are supported.
diff --git a/src/cobalt/media/media_module.h b/src/cobalt/media/media_module.h
index 098c58b..f073c24 100644
--- a/src/cobalt/media/media_module.h
+++ b/src/cobalt/media/media_module.h
@@ -56,15 +56,13 @@
                     public WebMediaPlayerDelegate {
  public:
   struct Options {
-    Options()
-        : use_audio_decoder_stub(false),
-          use_null_audio_streamer(false),
-          use_video_decoder_stub(false),
-          disable_webm_vp9(false) {}
-    bool use_audio_decoder_stub;
-    bool use_null_audio_streamer;
-    bool use_video_decoder_stub;
-    bool disable_webm_vp9;
+    Options() {}
+
+    bool use_audio_decoder_stub = false;
+    bool use_null_audio_streamer = false;
+    bool use_video_decoder_stub = false;
+    bool disable_webm_vp9 = false;
+    bool allow_resume_after_suspend = true;
     base::optional<math::Size> output_resolution_override;
   };
 
diff --git a/src/cobalt/media/media_module_starboard.cc b/src/cobalt/media/media_module_starboard.cc
index 9aa0ea4..239ea7e 100644
--- a/src/cobalt/media/media_module_starboard.cc
+++ b/src/cobalt/media/media_module_starboard.cc
@@ -66,7 +66,8 @@
       window = system_window_->GetSbWindow();
     }
     return make_scoped_ptr<WebMediaPlayer>(new media::WebMediaPlayerImpl(
-        window, client, this, &decoder_buffer_allocator_, new media::MediaLog));
+        window, client, this, &decoder_buffer_allocator_,
+        options_.allow_resume_after_suspend, new media::MediaLog));
   }
 
   system_window::SystemWindow* system_window() const override {
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 805b2c7..a5ac65f 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -107,7 +107,7 @@
 WebMediaPlayerImpl::WebMediaPlayerImpl(
     PipelineWindow window, WebMediaPlayerClient* client,
     WebMediaPlayerDelegate* delegate,
-    DecoderBuffer::Allocator* buffer_allocator,
+    DecoderBuffer::Allocator* buffer_allocator, bool allow_resume_after_suspend,
     const scoped_refptr<MediaLog>& media_log)
     : pipeline_thread_("media_pipeline"),
       network_state_(WebMediaPlayer::kNetworkStateEmpty),
@@ -116,6 +116,7 @@
       client_(client),
       delegate_(delegate),
       buffer_allocator_(buffer_allocator),
+      allow_resume_after_suspend_(allow_resume_after_suspend),
       proxy_(new WebMediaPlayerProxy(main_loop_->message_loop_proxy(), this)),
       media_log_(media_log),
       incremented_externally_allocated_memory_(false),
@@ -137,7 +138,8 @@
 
   pipeline_thread_.Start();
   pipeline_ = Pipeline::Create(window, pipeline_thread_.message_loop_proxy(),
-                               media_log_, video_frame_provider_);
+                               allow_resume_after_suspend_, media_log_,
+                               video_frame_provider_);
 
   // Also we want to be notified of |main_loop_| destruction.
   main_loop_->AddDestructionObserver(this);
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index 97bbd92..92fdb4a 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -105,11 +105,11 @@
   // When calling this, the |audio_source_provider| and
   // |audio_renderer_sink| arguments should be the same object.
 
-  WebMediaPlayerImpl(
-      PipelineWindow window, WebMediaPlayerClient* client,
-      WebMediaPlayerDelegate* delegate,
-      DecoderBuffer::Allocator* buffer_allocator,
-      const scoped_refptr<MediaLog>& media_log);
+  WebMediaPlayerImpl(PipelineWindow window, WebMediaPlayerClient* client,
+                     WebMediaPlayerDelegate* delegate,
+                     DecoderBuffer::Allocator* buffer_allocator,
+                     bool allow_resume_after_suspend,
+                     const scoped_refptr<MediaLog>& media_log);
   ~WebMediaPlayerImpl() override;
 
 #if SB_HAS(PLAYER_WITH_URL)
@@ -298,6 +298,7 @@
   WebMediaPlayerClient* client_;
   WebMediaPlayerDelegate* delegate_;
   DecoderBuffer::Allocator* buffer_allocator_;
+  bool allow_resume_after_suspend_;
   scoped_refptr<VideoFrameProvider> video_frame_provider_;
 
   scoped_refptr<WebMediaPlayerProxy> proxy_;
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp
index 383a9ad..8c5c47c 100644
--- a/src/cobalt/media/sandbox/sandbox.gyp
+++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -75,7 +75,7 @@
       'variables': {
         'executable_name': 'web_media_player_sandbox',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
   'conditions': [
@@ -102,7 +102,7 @@
           'variables': {
             'executable_name': 'media2_sandbox',
           },
-          'includes': [ '../../../starboard/build/deploy.gypi' ],
+          'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
         },
       ],
     }],
@@ -148,7 +148,7 @@
           'variables': {
             'executable_name': 'raw_video_decoder_fuzzer',
           },
-          'includes': [ '../../../starboard/build/deploy.gypi' ],
+          'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
         },
 
         # This target will build a sandbox application that allows for fuzzing
@@ -191,7 +191,7 @@
           'variables': {
             'executable_name': 'shell_demuxer_fuzzer',
           },
-          'includes': [ '../../../starboard/build/deploy.gypi' ],
+          'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
         },
       ],
     }],
diff --git a/src/cobalt/media_capture/media_devices.h b/src/cobalt/media_capture/media_devices.h
index b917475..83739d3 100644
--- a/src/cobalt/media_capture/media_devices.h
+++ b/src/cobalt/media_capture/media_devices.h
@@ -17,6 +17,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/event_target.h"
 #include "cobalt/media_capture/media_device_info.h"
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value.h"
@@ -30,7 +31,7 @@
 // The MediaDevices object is the entry point to the API used to examine and
 // get access to media devices available to the User Agent.
 //   https://www.w3.org/TR/mediacapture-streams/#mediadevices
-class MediaDevices : public script::Wrappable {
+class MediaDevices : public dom::EventTarget {
  public:
   using MediaInfoSequence = script::Sequence<scoped_refptr<script::Wrappable>>;
   using MediaInfoSequencePromise = script::Promise<MediaInfoSequence>;
diff --git a/src/cobalt/media_session/media_session_test.gyp b/src/cobalt/media_session/media_session_test.gyp
index e4787af..8e8ce42 100644
--- a/src/cobalt/media_session/media_session_test.gyp
+++ b/src/cobalt/media_session/media_session_test.gyp
@@ -38,7 +38,7 @@
       'variables': {
         'executable_name': 'media_session_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ]
 }
diff --git a/src/cobalt/network/lib/README.md b/src/cobalt/network/lib/README.md
deleted file mode 100644
index 55af499..0000000
--- a/src/cobalt/network/lib/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-This directory provides an API for clients that build Cobalt into a lib using
-the 'cobalt_enable_lib' flag.
-
-It is highly experimental at this point and is expected to change significantly
-across releases.
-
-As a general convention, functions whose symbols are exported are defined in an
-'exported' subdirectory and functions that are required to be implemented by
-clients are defined in a 'imported' subdirectory.
diff --git a/src/cobalt/network/network.gyp b/src/cobalt/network/network.gyp
index 011c295..e2a0eb6 100644
--- a/src/cobalt/network/network.gyp
+++ b/src/cobalt/network/network.gyp
@@ -42,15 +42,12 @@
         'proxy_config_service.h',
         'starboard/network_system.cc',
         'starboard/proxy_config_service.cc',
-        'starboard/user_agent_string_factory_starboard.cc',
         'switches.cc',
         'switches.h',
         'url_request_context.cc',
         'url_request_context.h',
         'url_request_context_getter.cc',
         'url_request_context_getter.h',
-        'user_agent_string_factory.cc',
-        'user_agent_string_factory.h',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
@@ -79,9 +76,6 @@
             'ENABLE_NETWORK_LOGGING',
           ],
         }],
-        ['cobalt_enable_lib == 1', {
-          'defines' : ['COBALT_ENABLE_LIB'],
-        }],
       ],
       'export_dependent_settings': [
         '<(DEPTH)/net/net.gyp:net',
@@ -107,7 +101,6 @@
       'sources': [
         'local_network_test.cc',
         'persistent_cookie_store_test.cc',
-        'user_agent_string_factory_test.cc',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
@@ -126,7 +119,7 @@
       'variables': {
         'executable_name': 'network_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/network/network_module.cc b/src/cobalt/network/network_module.cc
index b4ec250..cf6225c 100644
--- a/src/cobalt/network/network_module.cc
+++ b/src/cobalt/network/network_module.cc
@@ -23,26 +23,22 @@
 #include "base/threading/thread.h"
 #include "cobalt/network/network_system.h"
 #include "cobalt/network/switches.h"
-#include "cobalt/network/user_agent_string_factory.h"
 #include "net/url_request/static_http_user_agent_settings.h"
 
 namespace cobalt {
 namespace network {
 
-NetworkModule::NetworkModule() : storage_manager_(NULL) {
-  Initialize(NULL /* event_dispatcher */);
-}
-
 NetworkModule::NetworkModule(const Options& options)
     : storage_manager_(NULL), options_(options) {
-  Initialize(NULL /* event_dispatcher */);
+  Initialize("Null user agent string.", NULL);
 }
 
-NetworkModule::NetworkModule(storage::StorageManager* storage_manager,
+NetworkModule::NetworkModule(const std::string& user_agent_string,
+                             storage::StorageManager* storage_manager,
                              base::EventDispatcher* event_dispatcher,
                              const Options& options)
     : storage_manager_(storage_manager), options_(options) {
-  Initialize(event_dispatcher);
+  Initialize(user_agent_string, event_dispatcher);
 }
 
 NetworkModule::~NetworkModule() {
@@ -86,15 +82,15 @@
                             custom_proxy_rules));
 }
 
-void NetworkModule::Initialize(base::EventDispatcher* event_dispatcher) {
+void NetworkModule::Initialize(const std::string& user_agent_string,
+                               base::EventDispatcher* event_dispatcher) {
   thread_.reset(new base::Thread("NetworkModule"));
 #if !defined(OS_STARBOARD)
   object_watch_multiplexer_.reset(new base::ObjectWatchMultiplexer());
 #endif
   network_system_ = NetworkSystem::Create(event_dispatcher);
   http_user_agent_settings_.reset(new net::StaticHttpUserAgentSettings(
-      options_.preferred_language, "utf-8",
-      UserAgentStringFactory::ForCurrentPlatform()->CreateUserAgentString()));
+      options_.preferred_language, "utf-8", user_agent_string));
 
 #if defined(ENABLE_NETWORK_LOGGING)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
diff --git a/src/cobalt/network/network_module.h b/src/cobalt/network/network_module.h
index 2b51e12..0a7a4cf 100644
--- a/src/cobalt/network/network_module.h
+++ b/src/cobalt/network/network_module.h
@@ -69,13 +69,14 @@
     std::string custom_proxy;
   };
 
-  // Default constructor, for use by unit tests.
-  NetworkModule();
-  explicit NetworkModule(const Options& options);
+  // Simple constructor intended to be used only by tests.
+  explicit NetworkModule(const Options& options = Options());
+
   // Constructor for production use.
-  NetworkModule(storage::StorageManager* storage_manager,
+  NetworkModule(const std::string& user_agent_string,
+                storage::StorageManager* storage_manager,
                 base::EventDispatcher* event_dispatcher,
-                const Options& options);
+                const Options& options = Options());
   ~NetworkModule();
 
   URLRequestContext* url_request_context() const {
@@ -105,7 +106,8 @@
   void SetProxy(const std::string& custom_proxy_rules);
 
  private:
-  void Initialize(base::EventDispatcher* event_dispatcher);
+  void Initialize(const std::string& user_agent_string,
+                  base::EventDispatcher* event_dispatcher);
   void OnCreate(base::WaitableEvent* creation_event);
   scoped_ptr<network_bridge::NetPoster> CreateNetPoster();
 
diff --git a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
deleted file mode 100644
index 3c65fc7..0000000
--- a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
+++ /dev/null
@@ -1,145 +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 "cobalt/network/user_agent_string_factory.h"
-
-#include "starboard/log.h"
-#include "starboard/string.h"
-#include "starboard/system.h"
-
-#include "base/stringprintf.h"
-
-namespace cobalt {
-namespace network {
-
-namespace {
-
-#if SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION
-const char kStarboardStabilitySuffix[] = "-Experimental";
-#elif defined(SB_RELEASE_CANDIDATE_API_VERSION) && \
-    SB_API_VERSION >= SB_RELEASE_CANDIDATE_API_VERSION && \
-    SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
-const char kStarboardStabilitySuffix[] = "-ReleaseCandidate";
-#else
-const char kStarboardStabilitySuffix[] = "";
-#endif
-
-bool ShouldUsePlatformInfo(SbSystemDeviceType device_type) {
-  switch (device_type) {
-    case kSbSystemDeviceTypeBlueRayDiskPlayer:
-    case kSbSystemDeviceTypeGameConsole:
-    case kSbSystemDeviceTypeOverTheTopBox:
-    case kSbSystemDeviceTypeSetTopBox:
-    case kSbSystemDeviceTypeTV:
-    case kSbSystemDeviceTypeAndroidTV:
-    case kSbSystemDeviceTypeUnknown:
-      return true;
-    case kSbSystemDeviceTypeDesktopPC:
-    default:
-      return false;
-  }
-}
-
-class UserAgentStringFactoryStarboard : public UserAgentStringFactory {
- public:
-  UserAgentStringFactoryStarboard();
-};
-
-UserAgentStringFactoryStarboard::UserAgentStringFactoryStarboard() {
-  starboard_version_ = base::StringPrintf("Starboard/%d%s", SB_API_VERSION,
-                                          kStarboardStabilitySuffix);
-
-  const size_t kSystemPropertyMaxLength = 1024;
-  char value[kSystemPropertyMaxLength];
-  bool result;
-
-  result = SbSystemGetProperty(kSbSystemPropertyPlatformName, value,
-                               kSystemPropertyMaxLength);
-  SB_DCHECK(result);
-  os_name_and_version_ = value;
-
-  // Fill platform info if it is a hardware TV device.
-  SbSystemDeviceType device_type = SbSystemGetDeviceType();
-  if (ShouldUsePlatformInfo(device_type)) {
-    platform_info_ = PlatformInfo();
-
-    // Network operator
-    result = SbSystemGetProperty(kSbSystemPropertyNetworkOperatorName, value,
-                                 kSystemPropertyMaxLength);
-    if (result) {
-      platform_info_->network_operator = value;
-    }
-
-#if SB_API_VERSION >= 5
-    result = SbSystemGetProperty(kSbSystemPropertyUserAgentAuxField, value,
-                                 kSystemPropertyMaxLength);
-    if (result) {
-      aux_field_ = value;
-    }
-#endif  // SB_API_VERSION >= 5
-
-    // Device Type
-    platform_info_->device_type =
-        StarboardToPlatformInfoDeviceType(device_type);
-
-    // Chipset model number
-    result = SbSystemGetProperty(kSbSystemPropertyChipsetModelNumber, value,
-                                 kSystemPropertyMaxLength);
-    if (result) {
-      platform_info_->chipset_model_number = value;
-    }
-
-    // Firmware version
-    result = SbSystemGetProperty(kSbSystemPropertyFirmwareVersion, value,
-                                 kSystemPropertyMaxLength);
-    if (result) {
-      platform_info_->firmware_version = value;
-    }
-
-    // Brand
-    result = SbSystemGetProperty(kSbSystemPropertyManufacturerName, value,
-                                 kSystemPropertyMaxLength);
-    SB_DCHECK(result);
-    if (result) {
-      platform_info_->brand = value;
-    }
-
-    // Model
-    result = SbSystemGetProperty(kSbSystemPropertyModelName, value,
-                                 kSystemPropertyMaxLength);
-    SB_DCHECK(result);
-    if (result) {
-      platform_info_->model = value;
-    }
-
-    // Connection type
-    SbSystemConnectionType connection_type = SbSystemGetConnectionType();
-    if (connection_type == kSbSystemConnectionTypeWired) {
-      platform_info_->connection_type = PlatformInfo::kWiredConnection;
-    } else if (connection_type == kSbSystemConnectionTypeWireless) {
-      platform_info_->connection_type = PlatformInfo::kWirelessConnection;
-    }
-  }
-}
-
-}  // namespace
-
-scoped_ptr<UserAgentStringFactory>
-UserAgentStringFactory::ForCurrentPlatform() {
-  return scoped_ptr<UserAgentStringFactory>(
-      new UserAgentStringFactoryStarboard());
-}
-
-}  // namespace network
-}  // namespace cobalt
diff --git a/src/cobalt/network/user_agent_string_factory.cc b/src/cobalt/network/user_agent_string_factory.cc
deleted file mode 100644
index 6c82452..0000000
--- a/src/cobalt/network/user_agent_string_factory.cc
+++ /dev/null
@@ -1,319 +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 "cobalt/network/user_agent_string_factory.h"
-
-#include <vector>
-
-#include "base/string_util.h"
-#include "base/stringprintf.h"
-#if defined(COBALT_ENABLE_LIB)
-#include "cobalt/network/lib/exported/user_agent.h"
-#endif
-#include "cobalt/version.h"
-
-#include "cobalt_build_id.h"  // NOLINT(build/include)
-
-namespace {
-
-// Max length including null terminator.
-const size_t kUserAgentPlatformSuffixMaxLength = 128;
-char g_user_agent_platform_suffix[kUserAgentPlatformSuffixMaxLength] = {0};
-
-bool g_device_type_override_set = false;
-SbSystemDeviceType g_device_type_override = kSbSystemDeviceTypeUnknown;
-
-// Max length including null terminator.
-const size_t kPropertyMaxLength = 512;
-bool g_brand_name_override_set = false;
-char g_brand_name_override[kPropertyMaxLength] = {0};
-bool g_model_name_override_set = false;
-char g_model_name_override[kPropertyMaxLength] = {0};
-
-bool ShouldOverrideDevice() {
-  return g_device_type_override_set || g_brand_name_override_set ||
-         g_model_name_override_set;
-}
-
-#if defined(COBALT_ENABLE_LIB)
-bool CopyStringAndTestIfSuccess(char* out_value,
-                                size_t value_length,
-                                const char* from_value) {
-  if (strlen(from_value) + 1 > value_length)
-    return false;
-  base::strlcpy(out_value, from_value, value_length);
-  return true;
-}
-#endif
-
-}  // namespace
-
-namespace cobalt {
-namespace network {
-
-namespace {
-
-#if defined(COBALT_BUILD_TYPE_DEBUG)
-const char kBuildConfiguration[] = "debug";
-#elif defined(COBALT_BUILD_TYPE_DEVEL)
-const char kBuildConfiguration[] = "devel";
-#elif defined(COBALT_BUILD_TYPE_QA)
-const char kBuildConfiguration[] = "qa";
-#elif defined(COBALT_BUILD_TYPE_GOLD)
-const char kBuildConfiguration[] = "gold";
-#else
-#error Unknown build configuration.
-#endif
-
-struct SanitizeReplacements {
-  const char* replace_chars;
-  const char* replace_with;
-} kSanitizeReplacements[] = {
-  { ",", u8"\uFF0C" },  // fullwidth comma
-  { "_", u8"\u2E0F" },  // paragraphos
-  { "/", u8"\u2215" },  // division slash
-  { "(", u8"\uFF08" },  // fullwidth left paren
-  { ")", u8"\uFF09" },  // fullwidth right paren
-};
-
-// Replace reserved characters with Unicode homoglyphs
-std::string Sanitize(const std::string& str) {
-  std::string clean(str);
-  for (size_t i=0; i < arraysize(kSanitizeReplacements); i++) {
-    const SanitizeReplacements* replacement = kSanitizeReplacements + i;
-    ReplaceChars(
-        clean, replacement->replace_chars, replacement->replace_with, &clean);
-  }
-  return clean;
-}
-
-}  // namespace
-
-std::string UserAgentStringFactory::CreateUserAgentString() {
-  // Cobalt's user agent contains the following sections:
-  //   Mozilla/5.0 (ChromiumStylePlatform)
-  //   Cobalt/Version.BuildNumber-BuildConfiguration (unlike Gecko)
-  //   Starboard/APIVersion,
-  //   Device/FirmwareVersion (Brand, Model, ConnectionType)
-
-  //   Mozilla/5.0 (ChromiumStylePlatform)
-  std::string user_agent =
-      base::StringPrintf("Mozilla/5.0 (%s)", CreatePlatformString().c_str());
-
-  //   Cobalt/Version.BuildNumber-BuildConfiguration (unlike Gecko)
-  base::StringAppendF(&user_agent, " Cobalt/%s.%s-%s (unlike Gecko)",
-                      COBALT_VERSION, COBALT_BUILD_VERSION_NUMBER,
-                      kBuildConfiguration);
-
-  //   Starboard/APIVersion,
-  if (!starboard_version_.empty()) {
-    base::StringAppendF(&user_agent, " %s", starboard_version_.c_str());
-  }
-
-  //   Device/FirmwareVersion (Brand, Model, ConnectionType)
-  if (platform_info_) {
-    base::StringAppendF(
-        &user_agent, ", %s_%s_%s/%s (%s, %s, %s)",
-        Sanitize(platform_info_->network_operator.value_or("")).c_str(),
-        CreateDeviceTypeString().c_str(),
-        Sanitize(platform_info_->chipset_model_number.value_or("")).c_str(),
-        Sanitize(platform_info_->firmware_version.value_or("")).c_str(),
-        Sanitize(g_brand_name_override_set ?
-            std::string(g_brand_name_override) : platform_info_->brand).c_str(),
-        Sanitize(g_model_name_override_set ?
-            std::string(g_model_name_override) : platform_info_->model).c_str(),
-        CreateConnectionTypeString().c_str());
-  } else if (ShouldOverrideDevice()) {
-    // When Starboard does not report platform info but we are overriding the
-    // device.
-    base::StringAppendF(
-        &user_agent, ", _%s_/ (%s, %s,)", CreateDeviceTypeString().c_str(),
-        Sanitize(g_brand_name_override_set ? g_brand_name_override : "")
-            .c_str(),
-        Sanitize(g_model_name_override_set ? g_model_name_override : "")
-            .c_str());
-  }
-
-  if (!aux_field_.empty()) {
-    user_agent.append(" ");
-    user_agent.append(aux_field_);
-  }
-  return user_agent;
-}
-
-// Inserts semicolons between non-empty components of the platform.
-std::string UserAgentStringFactory::CreatePlatformString() {
-  std::string platform;
-  if (!historic_platform_name_.empty()) {
-    platform += historic_platform_name_;
-    platform += "; ";
-  }
-
-  DCHECK(!os_name_and_version_.empty());
-  platform += os_name_and_version_;
-
-  if (!architecture_tokens_.empty()) {
-    platform += "; ";
-    platform += architecture_tokens_;
-  }
-  if (g_user_agent_platform_suffix[0] != '\0') {
-    platform += "; ";
-    platform += g_user_agent_platform_suffix;
-  }
-
-  return platform;
-}
-
-std::string UserAgentStringFactory::CreateDeviceTypeString() {
-  auto reported_device_type =
-      g_device_type_override_set ?
-          StarboardToPlatformInfoDeviceType(g_device_type_override) :
-          platform_info_ ? platform_info_->device_type :
-                           PlatformInfo::kInvalidDeviceType;
-
-  switch (reported_device_type) {
-    case PlatformInfo::kBlueRayDiskPlayer:
-      return "BDP";
-    case PlatformInfo::kGameConsole:
-      return "GAME";
-    case PlatformInfo::kOverTheTopBox:
-      return "OTT";
-    case PlatformInfo::kSetTopBox:
-      return "STB";
-    case PlatformInfo::kTV:
-      return "TV";
-    case PlatformInfo::kAndroidTV:
-      return "ATV";
-    case PlatformInfo::kUnknown:
-      return "UNKNOWN";
-    case PlatformInfo::kInvalidDeviceType:
-    default:
-      NOTREACHED();
-      return "";
-  }
-}
-
-std::string UserAgentStringFactory::CreateConnectionTypeString() {
-  if (platform_info_->connection_type) {
-    switch (*platform_info_->connection_type) {
-      case PlatformInfo::kWiredConnection:
-        return "Wired";
-      case PlatformInfo::kWirelessConnection:
-        return "Wireless";
-      default:
-        NOTREACHED();
-    }
-  }
-
-  return "";
-}
-
-UserAgentStringFactory::PlatformInfo::DeviceType
-UserAgentStringFactory::StarboardToPlatformInfoDeviceType(
-    SbSystemDeviceType device_type) {
-  switch (device_type) {
-    case kSbSystemDeviceTypeBlueRayDiskPlayer:
-      return PlatformInfo::kBlueRayDiskPlayer;
-      break;
-    case kSbSystemDeviceTypeGameConsole:
-      return PlatformInfo::kGameConsole;
-      break;
-    case kSbSystemDeviceTypeOverTheTopBox:
-      return PlatformInfo::kOverTheTopBox;
-      break;
-    case kSbSystemDeviceTypeSetTopBox:
-      return PlatformInfo::kSetTopBox;
-      break;
-    case kSbSystemDeviceTypeTV:
-      return PlatformInfo::kTV;
-      break;
-    case kSbSystemDeviceTypeAndroidTV:
-      return PlatformInfo::kAndroidTV;
-      break;
-    case kSbSystemDeviceTypeUnknown:
-      return PlatformInfo::kUnknown;
-      break;
-    case kSbSystemDeviceTypeDesktopPC:
-    default:
-      return PlatformInfo::kInvalidDeviceType;
-  }
-}
-
-}  // namespace network
-}  // namespace cobalt
-
-#if defined(COBALT_ENABLE_LIB)
-// Allow host app to append a suffix to the reported platform name.
-bool CbLibUserAgentSetPlatformNameSuffix(const char* suffix) {
-  if (!CopyStringAndTestIfSuccess(g_user_agent_platform_suffix,
-                                 kPropertyMaxLength, suffix)) {
-    // If the suffix is too large then nothing is appended to the platform.
-    g_user_agent_platform_suffix[0] = '\0';
-    return false;
-  }
-
-  return true;
-}
-
-bool CbLibUserAgentSetBrandNameOverride(const char* value) {
-  if (!value) {
-    g_brand_name_override_set = false;
-    return true;
-  }
-
-  if (CopyStringAndTestIfSuccess(g_brand_name_override,
-                                 kPropertyMaxLength, value)) {
-    g_brand_name_override_set = true;
-    return true;
-  }
-
-  // Failed to reset/set value.
-  return false;
-}
-
-bool CbLibUserAgentSetModelNameOverride(const char* value) {
-  if (!value) {
-    g_model_name_override_set = false;
-    return true;
-  }
-
-  if (CopyStringAndTestIfSuccess(g_model_name_override,
-                                 kPropertyMaxLength, value)) {
-    g_model_name_override_set = true;
-    return true;
-  }
-
-  // Failed to reset/set value.
-  return false;
-}
-
-bool CbLibUserAgentSetDeviceTypeOverride(SbSystemDeviceType device_type) {
-  switch (device_type) {
-    case kSbSystemDeviceTypeBlueRayDiskPlayer:
-    case kSbSystemDeviceTypeGameConsole:
-    case kSbSystemDeviceTypeOverTheTopBox:
-    case kSbSystemDeviceTypeSetTopBox:
-    case kSbSystemDeviceTypeTV:
-    case kSbSystemDeviceTypeDesktopPC:
-    case kSbSystemDeviceTypeAndroidTV:
-    case kSbSystemDeviceTypeUnknown:
-      g_device_type_override_set = true;
-      g_device_type_override = device_type;
-      return true;
-    default:
-      g_device_type_override_set = false;
-      return false;
-  }
-}
-#endif  // defined(COBALT_ENABLE_LIB)
diff --git a/src/cobalt/network/user_agent_string_factory.h b/src/cobalt/network/user_agent_string_factory.h
deleted file mode 100644
index f51ea1b..0000000
--- a/src/cobalt/network/user_agent_string_factory.h
+++ /dev/null
@@ -1,92 +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.
-
-#ifndef COBALT_NETWORK_USER_AGENT_STRING_FACTORY_H_
-#define COBALT_NETWORK_USER_AGENT_STRING_FACTORY_H_
-
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/optional.h"
-#include "starboard/system.h"
-
-namespace cobalt {
-namespace network {
-
-class UserAgentStringFactory {
- public:
-  static scoped_ptr<UserAgentStringFactory> ForCurrentPlatform();
-  virtual ~UserAgentStringFactory() {}
-
-  // Creates user agent string for the current platform that follows the format
-  // of Chromium's user agent, without pretending to be Chromium.
-  // Optionally includes platform information specified in YouTube TV HTML5
-  // Technical Requirements (2016), see
-  // https://docs.google.com/document/d/1STWxTx4PMz0BQpB29EcJFPMMUwPrk8_BD6ky_N6moaI/edit#heading=h.f921oqp9td36.
-  std::string CreateUserAgentString();
-
- protected:
-  UserAgentStringFactory() {}
-
-  std::string historic_platform_name_;
-  std::string os_name_and_version_;
-  std::string architecture_tokens_;
-  std::string starboard_version_;
-  std::string aux_field_;
-
-  struct PlatformInfo {
-    enum DeviceType {
-      kInvalidDeviceType,
-      kAndroidTV,
-      kBlueRayDiskPlayer,
-      kGameConsole,
-      kOverTheTopBox,
-      kSetTopBox,
-      kTV,
-      kUnknown
-    };
-
-    enum ConnectionType {
-      kWiredConnection,
-      kWirelessConnection,
-    };
-
-    PlatformInfo() : device_type(kInvalidDeviceType) {}
-
-    base::optional<std::string> network_operator;
-    DeviceType device_type;
-    base::optional<std::string> chipset_model_number;
-    base::optional<std::string> firmware_version;
-    std::string brand;
-    std::string model;
-    base::optional<ConnectionType> connection_type;
-  };
-
-  static PlatformInfo::DeviceType StarboardToPlatformInfoDeviceType(
-      SbSystemDeviceType device_type);
-
-  base::optional<PlatformInfo> platform_info_;
-
- private:
-  std::string CreatePlatformString();
-  std::string CreateDeviceTypeString();
-  std::string CreateConnectionTypeString();
-
-  DISALLOW_COPY_AND_ASSIGN(UserAgentStringFactory);
-};
-
-}  // namespace network
-}  // namespace cobalt
-
-#endif  // COBALT_NETWORK_USER_AGENT_STRING_FACTORY_H_
diff --git a/src/cobalt/network/user_agent_string_factory_test.cc b/src/cobalt/network/user_agent_string_factory_test.cc
deleted file mode 100644
index ff7bf93..0000000
--- a/src/cobalt/network/user_agent_string_factory_test.cc
+++ /dev/null
@@ -1,153 +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 "cobalt/network/user_agent_string_factory.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cobalt {
-namespace network {
-
-namespace {
-
-class UserAgentStringFactoryWithOSNameAndVerison
-    : public UserAgentStringFactory {
- public:
-  UserAgentStringFactoryWithOSNameAndVerison() {
-    os_name_and_version_ = "GLaDOS 3.11";
-  }
-};
-
-TEST(UserAgentStringFactoryTest, StartsWithMozilla) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithOSNameAndVerison().CreateUserAgentString();
-  EXPECT_EQ(0, user_agent_string.find("Mozilla/5.0"));
-}
-
-TEST(UserAgentStringFactoryTest, ContainsCobalt) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithOSNameAndVerison().CreateUserAgentString();
-  EXPECT_NE(std::string::npos, user_agent_string.find("Cobalt"));
-}
-
-TEST(UserAgentStringFactoryTest, WithOSNameAndVersion) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithOSNameAndVerison().CreateUserAgentString();
-  EXPECT_NE(std::string::npos, user_agent_string.find("(GLaDOS 3.11)"));
-}
-
-class UserAgentStringFactoryWithHistoricPlatformName
-    : public UserAgentStringFactory {
- public:
-  UserAgentStringFactoryWithHistoricPlatformName() {
-    historic_platform_name_ = "Caroline";
-    os_name_and_version_ = "GLaDOS 3.11";
-  }
-};
-
-TEST(UserAgentStringFactoryTest, WithHistoricPlatformName) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithHistoricPlatformName().CreateUserAgentString();
-  EXPECT_NE(std::string::npos,
-            user_agent_string.find("(Caroline; GLaDOS 3.11)"));
-}
-
-class UserAgentStringFactoryWithArchitectureTokens
-    : public UserAgentStringFactory {
- public:
-  UserAgentStringFactoryWithArchitectureTokens() {
-    os_name_and_version_ = "GLaDOS 3.11";
-    architecture_tokens_ = "Wheatley";
-  }
-};
-
-TEST(UserAgentStringFactoryTest, WithArchitectureTokens) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithArchitectureTokens().CreateUserAgentString();
-  EXPECT_NE(std::string::npos,
-            user_agent_string.find("(GLaDOS 3.11; Wheatley)"));
-}
-
-class UserAgentStringFactoryWithPlatformInfo : public UserAgentStringFactory {
- public:
-  UserAgentStringFactoryWithPlatformInfo() {
-    // There are deliberately a variety of underscores, commas, slashes, and
-    // parentheses in the strings below to ensure they get sanitized.
-    os_name_and_version_ = "GLaDOS 3.11";
-    platform_info_ = PlatformInfo();
-    platform_info_->network_operator = "Aperture_Science_Innovators";
-    platform_info_->device_type = PlatformInfo::kOverTheTopBox;
-    platform_info_->chipset_model_number = "P-body/Orange_Atlas/Blue";
-    platform_info_->firmware_version = "0,01";
-    platform_info_->brand = "Aperture Science (Labs)";
-    platform_info_->model = "GLaDOS";
-  }
-};
-
-// Look-alike replacements expected from sanitizing fields
-#define COMMA  u8"\uFF0C"  // fullwidth comma
-#define UNDER  u8"\u2E0F"  // paragraphos
-#define SLASH  u8"\u2215"  // division slash
-#define LPAREN u8"\uFF08"  // fullwidth left paren
-#define RPAREN u8"\uFF09"  // fullwidth right paren
-
-TEST(UserAgentStringFactoryTest, WithPlatformInfo) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithPlatformInfo().CreateUserAgentString();
-  const char* tv_info_str =
-      "Aperture" UNDER "Science" UNDER "Innovators"
-      "_OTT_"
-      "P-body" SLASH "Orange" UNDER "Atlas" SLASH "Blue"
-      "/0" COMMA "01 (Aperture Science " LPAREN "Labs" RPAREN ", GLaDOS, )";
-  EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
-}
-
-class UserAgentStringFactoryWithWiredConnection
-    : public UserAgentStringFactory {
- public:
-  UserAgentStringFactoryWithWiredConnection() {
-    os_name_and_version_ = "GLaDOS 3.11";
-    platform_info_ = PlatformInfo();
-    platform_info_->connection_type = PlatformInfo::kWiredConnection;
-    platform_info_->device_type = PlatformInfo::kOverTheTopBox;
-  }
-};
-
-TEST(UserAgentStringFactoryTest, WithWiredConnection) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithWiredConnection().CreateUserAgentString();
-  EXPECT_NE(std::string::npos, user_agent_string.find("Wired"));
-}
-
-class UserAgentStringFactoryWithWirelessConnection
-    : public UserAgentStringFactory {
- public:
-  UserAgentStringFactoryWithWirelessConnection() {
-    os_name_and_version_ = "GLaDOS 3.11";
-    platform_info_ = PlatformInfo();
-    platform_info_->connection_type = PlatformInfo::kWirelessConnection;
-    platform_info_->device_type = PlatformInfo::kOverTheTopBox;
-  }
-};
-
-TEST(UserAgentStringFactoryTest, WithWirelessConnection) {
-  std::string user_agent_string =
-      UserAgentStringFactoryWithWirelessConnection().CreateUserAgentString();
-  EXPECT_NE(std::string::npos, user_agent_string.find("Wireless"));
-}
-
-}  // namespace
-
-}  // namespace network
-}  // namespace cobalt
diff --git a/src/cobalt/overlay_info/overlay_info.gyp b/src/cobalt/overlay_info/overlay_info.gyp
new file mode 100644
index 0000000..41a6bbb
--- /dev/null
+++ b/src/cobalt/overlay_info/overlay_info.gyp
@@ -0,0 +1,62 @@
+# Copyright 2018 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': {
+    'sb_pedantic_warnings': 1,
+  },
+  'targets': [
+    {
+      'target_name': 'overlay_info',
+      'type': 'static_library',
+      'sources': [
+        'overlay_info_registry.cc',
+        'overlay_info_registry.h',
+        'qr_code_overlay.cc',
+        'qr_code_overlay.h',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/math/math.gyp:math',
+        '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
+        '<(DEPTH)/third_party/QR-Code-generator/qr_code_generator.gyp:qr_code_generator',
+      ],
+    },
+
+    {
+      'target_name': 'overlay_info_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        'overlay_info_registry_test.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/overlay_info/overlay_info.gyp:overlay_info',
+        '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+
+    {
+      'target_name': 'overlay_info_test_deploy',
+      'type': 'none',
+      'dependencies': [
+        'overlay_info_test',
+      ],
+      'variables': {
+        'executable_name': 'overlay_info_test',
+      },
+      'includes': [ '../../starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/cobalt/overlay_info/overlay_info_registry.cc b/src/cobalt/overlay_info/overlay_info_registry.cc
new file mode 100644
index 0000000..67e6b76
--- /dev/null
+++ b/src/cobalt/overlay_info/overlay_info_registry.cc
@@ -0,0 +1,130 @@
+// Copyright 2018 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/overlay_info/overlay_info_registry.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "starboard/mutex.h"
+#include "starboard/string.h"
+
+namespace cobalt {
+namespace overlay_info {
+namespace {
+
+const auto kDelimiter = OverlayInfoRegistry::kDelimiter;
+
+class OverlayInfoRegistryImpl {
+ public:
+  static OverlayInfoRegistryImpl* GetInstance();
+
+  void Disable();
+
+  void Register(const char* category, const char* str);
+  void Register(const char* category, const void* data, size_t data_size);
+  void RetrieveAndClear(std::vector<uint8_t>* infos);
+
+ private:
+  // Reserve enough data for |infos_| to avoid extra allcations.
+  static const size_t kReservedSize = 4096;
+
+  OverlayInfoRegistryImpl() = default;
+
+  // To make our private constructor available to
+  // Singleton<OverlayInfoRegistryImpl,
+  // LeakySingletonTraits<OverlayInfoRegistryImpl>>.
+  friend struct DefaultSingletonTraits<OverlayInfoRegistryImpl>;
+
+  bool enabled_ = true;
+  starboard::Mutex mutex_;
+  std::vector<uint8_t> infos_;
+};
+
+// static
+OverlayInfoRegistryImpl* OverlayInfoRegistryImpl::GetInstance() {
+  return Singleton<OverlayInfoRegistryImpl,
+                   LeakySingletonTraits<OverlayInfoRegistryImpl>>::get();
+}
+
+void OverlayInfoRegistryImpl::Disable() {
+  starboard::ScopedLock scoped_lock(mutex_);
+  enabled_ = false;
+  infos_.clear();
+}
+
+void OverlayInfoRegistryImpl::Register(const char* category, const char* str) {
+  auto length = SbStringGetLength(str);
+  Register(category, reinterpret_cast<const uint8_t*>(str), length);
+}
+
+void OverlayInfoRegistryImpl::Register(const char* category, const void* data,
+                                       size_t data_size) {
+  DCHECK(SbStringFindCharacter(
+             category, static_cast<char>(OverlayInfoRegistry::kDelimiter)) ==
+         NULL)
+      << "Category " << category
+      << " cannot contain the delimiter:" << OverlayInfoRegistry::kDelimiter;
+  auto category_size = SbStringGetLength(category);
+  auto total_size = category_size + 1 + data_size;
+
+  DCHECK_GT(category_size, 0u);
+  // Use |kMaxSizeOfData + 0| to avoid link error caused by DCHECK_LE.
+  DCHECK_LE(total_size, OverlayInfoRegistry::kMaxSizeOfData + 0);
+
+  starboard::ScopedLock scoped_lock(mutex_);
+  // Use |kMaxNumberOfPendingOverlayInfo + 0| to avoid link error caused by
+  // DCHECK_LE.
+  DCHECK_LE(infos_.size() + total_size,
+            OverlayInfoRegistry::kMaxNumberOfPendingOverlayInfo + 0);
+  if (enabled_) {
+    infos_.push_back(static_cast<uint8_t>(total_size));
+    infos_.insert(infos_.end(), category, category + category_size);
+    infos_.push_back(kDelimiter);
+    infos_.insert(infos_.end(), static_cast<const uint8_t*>(data),
+                  static_cast<const uint8_t*>(data) + data_size);
+  }
+}
+
+void OverlayInfoRegistryImpl::RetrieveAndClear(std::vector<uint8_t>* infos) {
+  DCHECK(infos);
+
+  starboard::ScopedLock scoped_lock(mutex_);
+  if (enabled_) {
+    infos->swap(infos_);
+    infos_.clear();
+    infos_.reserve(kReservedSize);
+  }
+}
+
+}  // namespace
+
+void OverlayInfoRegistry::Disable() {
+  OverlayInfoRegistryImpl::GetInstance()->Disable();
+}
+
+void OverlayInfoRegistry::Register(const char* category, const char* str) {
+  OverlayInfoRegistryImpl::GetInstance()->Register(category, str);
+}
+
+void OverlayInfoRegistry::Register(const char* category, const void* data,
+                                   size_t data_size) {
+  OverlayInfoRegistryImpl::GetInstance()->Register(category, data, data_size);
+}
+
+void OverlayInfoRegistry::RetrieveAndClear(std::vector<uint8_t>* infos) {
+  OverlayInfoRegistryImpl::GetInstance()->RetrieveAndClear(infos);
+}
+
+}  // namespace overlay_info
+}  // namespace cobalt
diff --git a/src/cobalt/overlay_info/overlay_info_registry.h b/src/cobalt/overlay_info/overlay_info_registry.h
new file mode 100644
index 0000000..88dba0f
--- /dev/null
+++ b/src/cobalt/overlay_info/overlay_info_registry.h
@@ -0,0 +1,68 @@
+// Copyright 2018 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_OVERLAY_INFO_OVERLAY_INFO_REGISTRY_H_
+#define COBALT_OVERLAY_INFO_OVERLAY_INFO_REGISTRY_H_
+
+#include <vector>
+
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace overlay_info {
+
+// This class allows register of arbitrary overlay information in the form of
+// string or binary data from anywhere inside Cobalt.  It also allows a consumer
+// to retrieve and clear all registered info, such info will be displayed as
+// overlay.
+// The class is thread safe and all its methods can be accessed from any thread.
+// On average it expects to have less than 10 such info registered per frame.
+//
+// The binary data or string are stored in the following format:
+// [<one byte size> <size bytes data>]*
+// Each data entry contains a string category and some binary data, separated by
+// the delimiter '<'.
+// For example, the overlay infos ("media", "pts"), ("renderer", "fps\x60"), and
+// ("dom", "keydown") will be stored as
+//   '\x09', 'm', 'e', 'd', 'i', 'a', '<', 'p', 't', 's',
+//   '\x0d', 'r', 'e', 'n', 'd', 'e', 'r', 'e', 'r', '<', 'f', 'p', 's', '\x60',
+//   '\x0b', 'd', 'o', 'm', '<', 'k', 'e', 'y', 'd', 'o', 'w', 'n',
+// and the size of the vector will be 36.  Note that C strings won't be NULL
+// terminated and their sizes are calculated by the size of the data.
+class OverlayInfoRegistry {
+ public:
+  static const uint8_t kDelimiter = '<';  // ':' doesn't work with some scanners
+  // The size of category and data combined should be less than or equal to
+  // kMaxSizeOfData - 1.  The extra room of one byte is used by the delimiter.
+  static const size_t kMaxSizeOfData = 255;
+  // The app DCHECKs if the number of pending overlay exceeds the following
+  // value.
+  static const size_t kMaxNumberOfPendingOverlayInfo = 1024;
+
+  static void Disable();
+
+  // |category| cannot contain ':'.  The sum of size of |category| and |string|
+  // cannot exceed 254.  It leaves room for the delimiter.
+  static void Register(const char* category, const char* str);
+  // |category| cannot contain ':'.  The sum of size of |category| and |data|
+  // cannot exceed 254.  It leaves room for the delimiter.
+  static void Register(const char* category, const void* data,
+                       size_t data_size);
+  static void RetrieveAndClear(std::vector<uint8_t>* infos);
+};
+
+}  // namespace overlay_info
+}  // namespace cobalt
+
+#endif  // COBALT_OVERLAY_INFO_OVERLAY_INFO_REGISTRY_H_
diff --git a/src/cobalt/overlay_info/overlay_info_registry_test.cc b/src/cobalt/overlay_info/overlay_info_registry_test.cc
new file mode 100644
index 0000000..56d5d65
--- /dev/null
+++ b/src/cobalt/overlay_info/overlay_info_registry_test.cc
@@ -0,0 +1,182 @@
+// Copyright 2018 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/overlay_info/overlay_info_registry.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "starboard/memory.h"
+#include "starboard/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace overlay_info {
+
+namespace {
+
+typedef std::pair<std::string, std::vector<uint8_t>> ValuePair;
+
+// See comment of OverlayInfoRegistry for format of |info|.
+std::vector<ValuePair> ParseOverlayInfo(std::vector<uint8_t> info) {
+  std::vector<ValuePair> parsed_infos;
+
+  while (!info.empty()) {
+    // Parse the size
+    size_t size = info[0];
+    info.erase(info.begin());
+
+    CHECK_LE(size, info.size());
+
+    // Parse the category name
+    const auto kDelimiter = OverlayInfoRegistry::kDelimiter;
+    auto iter = std::find(info.begin(), info.end(), kDelimiter);
+    CHECK(iter != info.end());
+
+    const auto category_length = iter - info.begin();
+    CHECK_LE(static_cast<size_t>(category_length), size);
+
+    std::string category(
+        reinterpret_cast<char*>(info.data()),
+        reinterpret_cast<char*>(info.data()) + category_length);
+
+    // Parse the data
+    std::vector<uint8_t> data(info.begin() + category_length + 1,
+                              info.begin() + size);
+    info.erase(info.begin(), info.begin() + size);
+
+    parsed_infos.push_back(std::make_pair(category, data));
+  }
+
+  CHECK(info.empty());
+  return parsed_infos;
+}
+
+bool IsSame(const std::vector<uint8_t>& data, const std::string& str) {
+  return data.size() == str.size() &&
+         SbMemoryCompare(data.data(), str.c_str(), data.size()) == 0;
+}
+
+}  // namespace
+
+TEST(OverlayInfoRegistryTest, RetrieveOnEmptyData) {
+  std::vector<uint8_t> infos('a');
+  OverlayInfoRegistry::RetrieveAndClear(&infos);
+  EXPECT_TRUE(infos.empty());
+
+  std::vector<uint8_t> infos1('a');
+  OverlayInfoRegistry::RetrieveAndClear(&infos1);
+  EXPECT_TRUE(infos1.empty());
+}
+
+TEST(OverlayInfoRegistryTest, RegisterSingleStringAndRetrieve) {
+  const char kCategory[] = "category";
+  const size_t kMaxStringLength = 20;
+
+  for (size_t i = 0; i < kMaxStringLength; ++i) {
+    std::string value(i, 'q');
+
+    OverlayInfoRegistry::Register(kCategory, value.c_str());
+
+    std::vector<uint8_t> infos('a');
+    OverlayInfoRegistry::RetrieveAndClear(&infos);
+    auto parsed_infos = ParseOverlayInfo(infos);
+    EXPECT_EQ(parsed_infos.size(), 1);
+    EXPECT_EQ(parsed_infos[0].first, kCategory);
+    EXPECT_TRUE(IsSame(parsed_infos[0].second, value));
+
+    OverlayInfoRegistry::RetrieveAndClear(&infos);
+    EXPECT_TRUE(infos.empty());
+  }
+}
+
+TEST(OverlayInfoRegistryTest, RegisterSingleBinaryStringAndRetrieve) {
+  const char kCategory[] = "category";
+  const size_t kMaxDataSize = 20;
+
+  for (size_t i = 0; i < kMaxDataSize; ++i) {
+    std::vector<uint8_t> value(i, static_cast<uint8_t>(i % 2));
+
+    OverlayInfoRegistry::Register(kCategory, value.data(), value.size());
+
+    std::vector<uint8_t> infos('a');
+    OverlayInfoRegistry::RetrieveAndClear(&infos);
+    auto parsed_infos = ParseOverlayInfo(infos);
+    EXPECT_EQ(parsed_infos.size(), 1);
+    EXPECT_EQ(parsed_infos[0].first, kCategory);
+    EXPECT_TRUE(parsed_infos[0].second == value);
+
+    OverlayInfoRegistry::RetrieveAndClear(&infos);
+    EXPECT_TRUE(infos.empty());
+  }
+}
+
+TEST(OverlayInfoRegistryTest, RegisterMultipleStringsAndRetrieve) {
+  const char kCategory[] = "c";
+  const size_t kMaxStringLength = 20;
+
+  std::vector<std::string> values;
+
+  for (size_t i = 0; i < kMaxStringLength; ++i) {
+    values.push_back(std::string(i, 'x'));
+
+    OverlayInfoRegistry::Register(kCategory, values.back().c_str());
+  }
+
+  std::vector<uint8_t> infos('a');
+  OverlayInfoRegistry::RetrieveAndClear(&infos);
+  auto parsed_infos = ParseOverlayInfo(infos);
+  OverlayInfoRegistry::RetrieveAndClear(&infos);
+  EXPECT_TRUE(infos.empty());
+
+  ASSERT_EQ(parsed_infos.size(), kMaxStringLength);
+
+  for (size_t i = 0; i < kMaxStringLength; ++i) {
+    EXPECT_EQ(parsed_infos[i].first, kCategory);
+    EXPECT_TRUE(IsSame(parsed_infos[i].second, values[i]));
+  }
+}
+
+TEST(OverlayInfoRegistryTest, RegisterMultipleBinaryDataAndRetrieve) {
+  const char kCategory[] = "c";
+  const size_t kMaxDataSize = 20;
+
+  std::vector<std::vector<uint8_t>> values;
+
+  for (size_t i = 0; i < kMaxDataSize; ++i) {
+    values.push_back(std::vector<uint8_t>(i, static_cast<uint8_t>(i % 2)));
+
+    OverlayInfoRegistry::Register(kCategory, values.back().data(),
+                                  values.back().size());
+  }
+
+  std::vector<uint8_t> infos('a');
+  OverlayInfoRegistry::RetrieveAndClear(&infos);
+  auto parsed_infos = ParseOverlayInfo(infos);
+  OverlayInfoRegistry::RetrieveAndClear(&infos);
+  EXPECT_TRUE(infos.empty());
+
+  ASSERT_EQ(parsed_infos.size(), kMaxDataSize);
+
+  for (size_t i = 0; i < kMaxDataSize; ++i) {
+    EXPECT_EQ(parsed_infos[i].first, kCategory);
+    EXPECT_TRUE(parsed_infos[i].second == values[i]);
+  }
+}
+
+}  // namespace overlay_info
+}  // namespace cobalt
diff --git a/src/cobalt/overlay_info/qr_code_overlay.cc b/src/cobalt/overlay_info/qr_code_overlay.cc
new file mode 100644
index 0000000..7d91e84
--- /dev/null
+++ b/src/cobalt/overlay_info/qr_code_overlay.cc
@@ -0,0 +1,229 @@
+// Copyright 2018 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/overlay_info/qr_code_overlay.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "cobalt/overlay_info/overlay_info_registry.h"
+#include "cobalt/render_tree/animations/animate_node.h"
+#include "third_party/QR-Code-generator/cpp/QrCode.hpp"
+
+namespace cobalt {
+namespace overlay_info {
+
+namespace {
+
+using qrcodegen::QrCode;
+using render_tree::Image;
+using render_tree::ImageNode;
+
+const int kModuleDimensionInPixels = 4;
+const int kPixelSizeInBytes = 4;
+const uint32_t kBlack = 0x00000000;
+const uint32_t kWhite = 0xFFFFFFFF;
+const uint32_t kBorderColor = kWhite;
+const int kCodeBorderInPixels = 16;
+const int kScreenMarginInPixels = 128;
+
+int64_t s_frame_count_ = 0;
+
+void DrawRect(int width, int height, int pitch_in_bytes, uint32_t color,
+              uint8_t* target_buffer) {
+  while (height > 0) {
+    uint32_t* pixels = reinterpret_cast<uint32_t*>(target_buffer);
+    for (int i = 0; i < width; ++i) {
+      pixels[i] = color;
+    }
+    target_buffer += pitch_in_bytes;
+    --height;
+  }
+}
+
+void DrawQrCode(const QrCode& qr_code, int pitch_in_bytes,
+                uint8_t* target_buffer) {
+  uint8_t* row_data = target_buffer;
+  for (int row = 0; row < qr_code.getSize(); ++row) {
+    uint8_t* column_data = row_data;
+    for (int column = 0; column < qr_code.getSize(); ++column) {
+      DrawRect(kModuleDimensionInPixels, kModuleDimensionInPixels,
+               pitch_in_bytes, qr_code.getModule(row, column) ? kBlack : kWhite,
+               column_data);
+      column_data += kPixelSizeInBytes * kModuleDimensionInPixels;
+    }
+
+    row_data += pitch_in_bytes * kModuleDimensionInPixels;
+  }
+}
+
+scoped_refptr<Image> CreateImageForQrCodes(
+    const std::vector<QrCode>& qr_codes, const math::Size& screen_size,
+    render_tree::ResourceProvider* resource_provider) {
+  TRACE_EVENT0("cobalt::overlay_info", "CreateImageForQrCodes()");
+
+  int max_code_size = 0;
+
+  for (auto& qr_code : qr_codes) {
+    max_code_size = std::max(max_code_size, qr_code.getSize());
+  }
+
+  int column =
+      (screen_size.width() - kScreenMarginInPixels * 2 - kCodeBorderInPixels) /
+      (max_code_size * kModuleDimensionInPixels + kCodeBorderInPixels);
+  column = std::min(column, static_cast<int>(qr_codes.size()));
+  int row = (static_cast<int>(qr_codes.size()) + column - 1) / column;
+
+  int image_width = column * max_code_size * kModuleDimensionInPixels +
+                    kCodeBorderInPixels * (column + 1);
+  int image_height = row * max_code_size * kModuleDimensionInPixels +
+                     kCodeBorderInPixels * (row + 1);
+
+  auto image_data = resource_provider->AllocateImageData(
+      math::Size(image_width, image_height), render_tree::kPixelFormatRGBA8,
+      render_tree::kAlphaFormatOpaque);
+  DCHECK(image_data);
+  auto image_desc = image_data->GetDescriptor();
+
+  size_t qr_code_index = 0;
+  auto row_data = image_data->GetMemory();
+  for (int i = 0; i < row; ++i) {
+    // Draw the top border of all qr codes in the row.
+    DrawRect(image_width, kCodeBorderInPixels, image_desc.pitch_in_bytes,
+             kBorderColor, row_data);
+    row_data += kCodeBorderInPixels * image_desc.pitch_in_bytes;
+    auto column_data = row_data;
+
+    for (int j = 0; j < column; ++j) {
+      // Draw the left border.
+      DrawRect(kCodeBorderInPixels, max_code_size * kModuleDimensionInPixels,
+               image_desc.pitch_in_bytes, kBorderColor, column_data);
+      column_data += kCodeBorderInPixels * kPixelSizeInBytes;
+      if (qr_code_index < qr_codes.size()) {
+        // Draw qr code.
+        DrawQrCode(qr_codes[qr_code_index], image_desc.pitch_in_bytes,
+                   column_data);
+        ++qr_code_index;
+      }
+      column_data +=
+          max_code_size * kModuleDimensionInPixels * kPixelSizeInBytes;
+    }
+
+    // Draw the right border of the row.
+    DrawRect(kCodeBorderInPixels, max_code_size * kModuleDimensionInPixels,
+             image_desc.pitch_in_bytes, kBorderColor, column_data);
+
+    row_data +=
+        max_code_size * kModuleDimensionInPixels * image_desc.pitch_in_bytes;
+  }
+
+  // Draw the bottom border of all qr code.
+  DrawRect(image_width, kCodeBorderInPixels, image_desc.pitch_in_bytes,
+           kBorderColor, row_data);
+
+  return resource_provider->CreateImage(image_data.Pass());
+}
+
+void AnimateCB(math::Size screen_size,
+               render_tree::ResourceProvider* resource_provider,
+               render_tree::ImageNode::Builder* image_node,
+               base::TimeDelta time) {
+  UNREFERENCED_PARAMETER(time);
+  DCHECK(image_node);
+
+  TRACE_EVENT0("cobalt::overlay_info", "AnimateCB()");
+
+  OverlayInfoRegistry::Register("overlay_info", &s_frame_count_,
+                                sizeof(s_frame_count_));
+  ++s_frame_count_;
+
+  std::vector<uint8_t> infos;
+  OverlayInfoRegistry::RetrieveAndClear(&infos);
+
+  if (infos.empty()) {
+    image_node->source = NULL;
+    return;
+  }
+
+  // TODO: Explore the option to store the whole |infos| binary into one giant
+  //       QR code or combine the values into several large QR codes.
+  std::vector<QrCode> qr_codes;
+  for (size_t i = 0; i < infos.size(); ++i) {
+    DCHECK_LT(infos[i] + i, infos.size());
+    std::vector<uint8_t> data(infos.begin() + i + 1,
+                              infos.begin() + i + 1 + infos[i]);
+    qr_codes.emplace_back(QrCode::encodeBinary(data, QrCode::Ecc::LOW));
+    i += infos[i];
+  }
+
+  image_node->source =
+      CreateImageForQrCodes(qr_codes, screen_size, resource_provider);
+  auto image_size = image_node->source->GetSize();
+  image_node->destination_rect =
+      math::RectF(kScreenMarginInPixels, kScreenMarginInPixels,
+                  image_size.width(), image_size.height());
+}
+
+}  // namespace
+
+QrCodeOverlay::QrCodeOverlay(
+    const math::Size& screen_size,
+    render_tree::ResourceProvider* resource_provider,
+    const RenderTreeProducedCB& render_tree_produced_cb)
+    : render_tree_produced_cb_(render_tree_produced_cb),
+      screen_size_(screen_size),
+      resource_provider_(resource_provider) {
+  DCHECK_GT(screen_size.width(), 0);
+  DCHECK_GT(screen_size.height(), 0);
+  DCHECK(!render_tree_produced_cb_.is_null());
+
+  UpdateRenderTree();
+}
+
+void QrCodeOverlay::SetSize(const math::Size& size) {
+  DCHECK_GT(size.width(), 0);
+  DCHECK_GT(size.height(), 0);
+
+  screen_size_ = size;
+  UpdateRenderTree();
+}
+
+void QrCodeOverlay::SetResourceProvider(
+    render_tree::ResourceProvider* resource_provider) {
+  resource_provider_ = resource_provider;
+  UpdateRenderTree();
+}
+
+void QrCodeOverlay::UpdateRenderTree() {
+  TRACE_EVENT0("cobalt::overlay_info", "QrCodeOverlay::UpdateRenderTree()");
+
+  if (resource_provider_ == NULL) {
+    return;
+  }
+
+  scoped_refptr<ImageNode> image_node = new ImageNode(NULL);
+  render_tree::animations::AnimateNode::Builder animate_node_builder;
+
+  animate_node_builder.Add(
+      image_node, base::Bind(AnimateCB, screen_size_, resource_provider_));
+
+  render_tree_produced_cb_.Run(new render_tree::animations::AnimateNode(
+      animate_node_builder, image_node));
+}
+
+}  // namespace overlay_info
+}  // namespace cobalt
diff --git a/src/cobalt/overlay_info/qr_code_overlay.h b/src/cobalt/overlay_info/qr_code_overlay.h
new file mode 100644
index 0000000..8068b54
--- /dev/null
+++ b/src/cobalt/overlay_info/qr_code_overlay.h
@@ -0,0 +1,52 @@
+// Copyright 2018 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_OVERLAY_INFO_QR_CODE_OVERLAY_H_
+#define COBALT_OVERLAY_INFO_QR_CODE_OVERLAY_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/time.h"
+#include "cobalt/math/size.h"
+#include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/render_tree/resource_provider.h"
+
+namespace cobalt {
+namespace overlay_info {
+
+class QrCodeOverlay {
+ public:
+  typedef base::Callback<void(const scoped_refptr<render_tree::Node>&)>
+      RenderTreeProducedCB;
+
+  QrCodeOverlay(const math::Size& screen_size,
+                render_tree::ResourceProvider* resource_provider,
+                const RenderTreeProducedCB& render_tree_produced_cb);
+
+  void SetSize(const math::Size& size);
+  void SetResourceProvider(render_tree::ResourceProvider* resource_provider);
+
+ private:
+  void UpdateRenderTree();
+
+  RenderTreeProducedCB render_tree_produced_cb_;
+  math::Size screen_size_;
+  render_tree::ResourceProvider* resource_provider_;
+};
+
+}  // namespace overlay_info
+}  // namespace cobalt
+
+#endif  // COBALT_OVERLAY_INFO_QR_CODE_OVERLAY_H_
diff --git a/src/cobalt/page_visibility/page_visibility.gyp b/src/cobalt/page_visibility/page_visibility.gyp
index 015f78ac..8c7905e 100644
--- a/src/cobalt/page_visibility/page_visibility.gyp
+++ b/src/cobalt/page_visibility/page_visibility.gyp
@@ -62,7 +62,7 @@
       'variables': {
         'executable_name': 'page_visibility_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/render_tree/image.h b/src/cobalt/render_tree/image.h
index acca4ed..f12c35f 100644
--- a/src/cobalt/render_tree/image.h
+++ b/src/cobalt/render_tree/image.h
@@ -126,6 +126,9 @@
   // A YUV image where the Y channel is stored as a single-channel image plane
   // and the U and V channels are interleaved in a second image plane.
   kMultiPlaneImageFormatYUV2PlaneBT709,
+  // A YUV image where each channel, Y, U and V, is stored as a separate
+  // single-channel 10bit unnormalized image plane.
+  kMultiPlaneImageFormatYUV3Plane10BitBT2020,
 };
 
 // Like the ImageDataDescriptor object, a MultiPlaneImageDataDescriptor
diff --git a/src/cobalt/render_tree/render_tree.gyp b/src/cobalt/render_tree/render_tree.gyp
index 55c827c..2be2a1c 100644
--- a/src/cobalt/render_tree/render_tree.gyp
+++ b/src/cobalt/render_tree/render_tree.gyp
@@ -119,7 +119,7 @@
       'variables': {
         'executable_name': 'render_tree_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 51fb5d7..3ec36c1 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -43,8 +43,13 @@
 
 // The stack size to be used for the renderer thread.  This is must be large
 // enough to support recursing on the render tree.
+#if defined(COBALT_BUILD_TYPE_DEBUG)
+const int kRendererThreadStackSize =
+    256 * 1024 + base::kAsanAdditionalStackSize;
+#else
 const int kRendererThreadStackSize =
     128 * 1024 + base::kAsanAdditionalStackSize;
+#endif
 
 // How many entries the rasterize periodic timer will contain before updating.
 const size_t kRasterizePeriodicTimerEntriesPerUpdate = 60;
@@ -201,7 +206,7 @@
 }
 
 void Pipeline::RasterizeToRGBAPixels(
-    const Submission& render_tree_submission,
+    const scoped_refptr<render_tree::Node>& render_tree_root,
     const RasterizationCompleteCallback& complete) {
   TRACK_MEMORY_SCOPE("Renderer");
   TRACE_EVENT0("cobalt::renderer", "Pipeline::RasterizeToRGBAPixels()");
@@ -210,7 +215,7 @@
     rasterizer_thread_.message_loop()->PostTask(
         FROM_HERE,
         base::Bind(&Pipeline::RasterizeToRGBAPixels, base::Unretained(this),
-                   CollectAnimations(render_tree_submission), complete));
+                   render_tree_root, complete));
     return;
   }
   // Create a new target that is the same dimensions as the display target.
@@ -218,8 +223,12 @@
       graphics_context_->CreateDownloadableOffscreenRenderTarget(
           render_target_->GetSize());
 
+  scoped_refptr<render_tree::Node> animate_node =
+      new render_tree::animations::AnimateNode(render_tree_root);
+
+  Submission submission = Submission(animate_node);
   // Rasterize this submission into the newly created target.
-  RasterizeSubmissionToRenderTarget(render_tree_submission, offscreen_target);
+  RasterizeSubmissionToRenderTarget(submission, offscreen_target);
 
   // Load the texture's pixel data into a CPU memory buffer and return it.
   complete.Run(graphics_context_->DownloadPixelDataAsRGBA(offscreen_target),
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 5eb6228..18f073f 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -90,8 +90,9 @@
   // |render_tree_submission| will be rasterized into a new offscreen surface.
   // The RGBA pixel data will be extracted from this surface, and |complete|
   // will be called with the pixel data and the dimensions of the image.
-  void RasterizeToRGBAPixels(const Submission& render_tree_submission,
-                             const RasterizationCompleteCallback& complete);
+  void RasterizeToRGBAPixels(
+      const scoped_refptr<render_tree::Node>& render_tree_root,
+      const RasterizationCompleteCallback& complete);
 
   // Inserts a fence that ensures the rasterizer rasterizes up until the
   // submission time proceeding queuing additional submissions.  This is useful
diff --git a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
index e51412b..929e47f 100644
--- a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
@@ -115,7 +115,7 @@
       'variables': {
         'executable_name': 'blitter_rasterizer_tests',
       },
-      'includes': [ '../../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
index 0435764..c563200 100644
--- a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
@@ -82,6 +82,24 @@
     1.164f, 0.0f,   1.793f, -0.96925f, 1.164f, -0.213f, -0.533f, 0.30025f,
     1.164f, 2.112f, 0.0f,   -1.12875f, 0.0f,   0.0f,    0.0f,    1.0};
 
+// Used for 10bit unnormalized YUV images.
+const float k10BitBT2020ColorMatrix[16] = {64 * 1.163746465f,
+                                           -64 * 0.028815145f,
+                                           64 * 2.823537589f,
+                                           -1.470095f,
+                                           64 * 1.164383561f,
+                                           -64 * 0.258509894f,
+                                           64 * 0.379693635f,
+                                           -0.133366f,
+                                           64 * 1.164383561f,
+                                           64 * 2.385315708f,
+                                           64 * 0.021554502f,
+                                           -1.276209f,
+                                           0.0f,
+                                           0.0f,
+                                           0.0f,
+                                           1.0f};
+
 const float* GetColorMatrixForImageType(
     TexturedMeshRenderer::Image::Type type) {
   switch (type) {
@@ -93,6 +111,9 @@
     case TexturedMeshRenderer::Image::YUV_UYVY_422_BT709: {
       return kBT709ColorMatrix;
     } break;
+    case TexturedMeshRenderer::Image::YUV_3PLANE_10BIT_BT2020: {
+      return k10BitBT2020ColorMatrix;
+    } break;
     default: { NOTREACHED(); }
   }
   return NULL;
@@ -522,11 +543,30 @@
             color_matrix, texture_infos,
             CreateFragmentShader(texture_target, texture_infos));
       } break;
+      case Image::YUV_3PLANE_10BIT_BT2020:
       case Image::YUV_3PLANE_BT709: {
         std::vector<TextureInfo> texture_infos;
+#if SB_API_VERSION >= 7 && defined(GL_RED_EXT)
+        if (image.textures[0].texture->GetFormat() == GL_RED_EXT) {
+          texture_infos.push_back(TextureInfo("y", "r"));
+        } else {
+          texture_infos.push_back(TextureInfo("y", "a"));
+        }
+        if (image.textures[1].texture->GetFormat() == GL_RED_EXT) {
+          texture_infos.push_back(TextureInfo("u", "r"));
+        } else {
+          texture_infos.push_back(TextureInfo("u", "a"));
+        }
+        if (image.textures[2].texture->GetFormat() == GL_RED_EXT) {
+          texture_infos.push_back(TextureInfo("v", "r"));
+        } else {
+          texture_infos.push_back(TextureInfo("v", "a"));
+        }
+#else   // SB_API_VERSION >= 7 && defined(GL_RED_EXT)
         texture_infos.push_back(TextureInfo("y", "a"));
         texture_infos.push_back(TextureInfo("u", "a"));
         texture_infos.push_back(TextureInfo("v", "a"));
+#endif  // SB_API_VERSION >= 7 && defined(GL_RED_EXT)
         result = MakeBlitProgram(
             color_matrix, texture_infos,
             CreateFragmentShader(texture_target, texture_infos));
diff --git a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h
index 3831d4d..38e1024 100644
--- a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h
+++ b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h
@@ -52,6 +52,9 @@
       // YUV BT709 image where the Y, U and V components are all on different
       // textures.
       YUV_3PLANE_BT709,
+      // YUV BT2020 image where the Y, U and V components are all on different
+      // 10bit unnormalized textures.
+      YUV_3PLANE_10BIT_BT2020,
       // 1 texture is used that contains RGBA pixels.
       RGBA,
       // 1 texture plane is used where Y is sampled twice for each UV sample
@@ -69,6 +72,7 @@
       switch (type) {
         case YUV_2PLANE_BT709:
           return 2;
+        case YUV_3PLANE_10BIT_BT2020:
         case YUV_3PLANE_BT709:
           return 3;
         case RGBA:
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index a8cf645..0f17aa0 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -340,6 +340,15 @@
           hardware_image->GetHardwareFrontendImage(1), stereo_mode);
       result.textures[2] = GetTextureFromHardwareFrontendImage(
           hardware_image->GetHardwareFrontendImage(2), stereo_mode);
+    } else if (hardware_image->GetFormat() ==
+               render_tree::kMultiPlaneImageFormatYUV3Plane10BitBT2020) {
+      result.type = egl::TexturedMeshRenderer::Image::YUV_3PLANE_10BIT_BT2020;
+      result.textures[0] = GetTextureFromHardwareFrontendImage(
+          hardware_image->GetHardwareFrontendImage(0), stereo_mode);
+      result.textures[1] = GetTextureFromHardwareFrontendImage(
+          hardware_image->GetHardwareFrontendImage(1), stereo_mode);
+      result.textures[2] = GetTextureFromHardwareFrontendImage(
+          hardware_image->GetHardwareFrontendImage(2), stereo_mode);
     }
   } else {
     NOTREACHED();
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index 3d936c4..1dbc767 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -169,6 +169,9 @@
       return 1;
     case kSbDecodeTargetFormat2PlaneYUVNV12:
       return 2;
+#if SB_API_VERSION >= SB_10_BIT_YUV_I420_DECODE_TARGET_SUPPORT_API_VERSION
+    case kSbDecodeTargetFormat3Plane10BitYUVI420:
+#endif
     case kSbDecodeTargetFormat3PlaneYUVI420:
       return 3;
     default:
@@ -229,8 +232,16 @@
       }
 #endif  // SB_API_VERSION >= 7
     } break;
+#if SB_API_VERSION >= SB_10_BIT_YUV_I420_DECODE_TARGET_SUPPORT_API_VERSION
+    case kSbDecodeTargetFormat3Plane10BitYUVI420:
+#endif
     case kSbDecodeTargetFormat3PlaneYUVI420: {
       DCHECK_LT(plane, 3);
+#if SB_API_VERSION >= 7 && defined(GL_RED_EXT)
+      if (plane_info->gl_texture_format == GL_RED_EXT) {
+        return GL_RED_EXT;
+      }
+#endif  // SB_API_VERSION >= 7 && defined(GL_RED_EXT)
       return GL_ALPHA;
     } break;
     default: {
@@ -249,6 +260,11 @@
     case kSbDecodeTargetFormat3PlaneYUVI420: {
       return render_tree::kMultiPlaneImageFormatYUV3PlaneBT709;
     } break;
+#if SB_API_VERSION >= SB_10_BIT_YUV_I420_DECODE_TARGET_SUPPORT_API_VERSION
+    case kSbDecodeTargetFormat3Plane10BitYUVI420: {
+      return render_tree::kMultiPlaneImageFormatYUV3Plane10BitBT2020;
+    } break;
+#endif
     default: { NOTREACHED(); }
   }
   return render_tree::kMultiPlaneImageFormatYUV2PlaneBT709;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
index a851ce6..091f7bd 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
@@ -57,7 +57,7 @@
       'variables': {
         'executable_name': 'filter_fuzz_stub',
       },
-      'includes': [ '../../../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.h b/src/cobalt/renderer/render_tree_pixel_tester.h
index d0dd42c..d892244 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.h
+++ b/src/cobalt/renderer/render_tree_pixel_tester.h
@@ -88,10 +88,11 @@
 
   render_tree::ResourceProvider* GetResourceProvider() const;
 
- private:
   scoped_array<uint8_t> RasterizeRenderTree(
       const scoped_refptr<cobalt::render_tree::Node>& test_tree) const;
+  const math::Size& GetTargetSize() const { return test_surface_->GetSize(); }
 
+ private:
   scoped_ptr<cobalt::renderer::backend::GraphicsSystem> graphics_system_;
   scoped_ptr<cobalt::renderer::backend::GraphicsContext> graphics_context_;
   scoped_ptr<cobalt::renderer::rasterizer::Rasterizer> rasterizer_;
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index b7752ee..ea26345 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -126,7 +126,7 @@
       'variables': {
         'executable_name': 'renderer_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
@@ -152,7 +152,7 @@
       'variables': {
         'executable_name': 'renderer_benchmark',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/renderer/sandbox/sandbox.gyp b/src/cobalt/renderer/sandbox/sandbox.gyp
index 7cf08e7..53493fd 100644
--- a/src/cobalt/renderer/sandbox/sandbox.gyp
+++ b/src/cobalt/renderer/sandbox/sandbox.gyp
@@ -45,7 +45,7 @@
       'variables': {
         'executable_name': 'renderer_sandbox',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
@@ -76,7 +76,7 @@
       'variables': {
         'executable_name': 'scaling_text_sandbox',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/samples/simple_example/simple_example.gyp b/src/cobalt/samples/simple_example/simple_example.gyp
index feec6c2..774a144 100644
--- a/src/cobalt/samples/simple_example/simple_example.gyp
+++ b/src/cobalt/samples/simple_example/simple_example.gyp
@@ -76,7 +76,7 @@
       'variables': {
         'executable_name': 'simple_example',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     # This target will create a test for simple_example.
@@ -123,7 +123,7 @@
       'variables': {
         'executable_name': 'simple_example_test',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/script/array_buffer.h b/src/cobalt/script/array_buffer.h
new file mode 100644
index 0000000..1e00f6f
--- /dev/null
+++ b/src/cobalt/script/array_buffer.h
@@ -0,0 +1,108 @@
+// Copyright 2018 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_ARRAY_BUFFER_H_
+#define COBALT_SCRIPT_ARRAY_BUFFER_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 {
+
+class GlobalEnvironment;
+class PreallocatedArrayBufferData;
+
+// http://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-objects
+class ArrayBuffer {
+ public:
+  // http://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-length
+  static Handle<ArrayBuffer> New(GlobalEnvironment* global_environment,
+                                 size_t byte_length);
+
+  // A convenience constructor that will allocate a new |ArrayBuffer| of size
+  // |byte_length|, and then copy |data| (which must be an allocation of size
+  // |byte_length|) into the array buffer.
+  static Handle<ArrayBuffer> New(GlobalEnvironment* global_environment,
+                                 void* data, size_t byte_length) {
+    Handle<ArrayBuffer> array_buffer = New(global_environment, byte_length);
+    SbMemoryCopy(array_buffer->Data(), data, byte_length);
+    return array_buffer;
+  }
+
+  // Create a new |ArrayBuffer| from existing block of memory.  See
+  // |PreallocatedArrayBufferData| for details.
+  static Handle<ArrayBuffer> New(GlobalEnvironment* global_environment,
+                                 PreallocatedArrayBufferData* data);
+
+  virtual ~ArrayBuffer() {}
+
+  // http://www.ecma-international.org/ecma-262/6.0/#sec-get-arraybuffer.prototype.bytelength
+  virtual size_t ByteLength() const = 0;
+
+  // Access the raw underlying data held by the |ArrayBuffer|.
+  virtual void* Data() const = 0;
+};
+
+// A scoped RAII wrapper to facilitate creating |ArrayBuffer|s from
+// preallocated data (via moving the data into the an array buffer, NOT
+// copying it).
+//
+// Usage looks something like:
+//
+//   // Allocate the data by constructing a |PreallocatedArrayBufferData|
+//   PreallocatedArrayBufferData data(16);
+//
+//   // Manipulate the data however you want.
+//   static_cast<uint8_t*>(data.data())[0] = 0xFF;
+//
+//   // Create a new |ArrayBuffer| using |data|.
+//   auto array_buffer = ArrayBuffer::New(env, &data);
+//
+//   // |PreallocatedData| now no longer holds anything, data should now be
+//   // accessed from the ArrayBuffer itself.
+//   DCHECK_EQ(data.data(), nullptr);
+class PreallocatedArrayBufferData {
+ public:
+  explicit PreallocatedArrayBufferData(size_t byte_length);
+  ~PreallocatedArrayBufferData();
+
+  PreallocatedArrayBufferData(PreallocatedArrayBufferData&& other) = default;
+  PreallocatedArrayBufferData& operator=(PreallocatedArrayBufferData&& other) =
+      default;
+
+  void* data() const { return data_; }
+  size_t byte_length() const { return byte_length_; }
+
+ private:
+  PreallocatedArrayBufferData(const PreallocatedArrayBufferData&) = delete;
+  void operator=(const PreallocatedArrayBufferData&) = delete;
+
+  void Release() {
+    data_ = nullptr;
+    byte_length_ = 0u;
+  }
+
+  void* data_;
+  size_t byte_length_;
+
+  friend ArrayBuffer;
+};
+
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_ARRAY_BUFFER_H_
diff --git a/src/cobalt/script/array_buffer_view.h b/src/cobalt/script/array_buffer_view.h
new file mode 100644
index 0000000..6a4657e
--- /dev/null
+++ b/src/cobalt/script/array_buffer_view.h
@@ -0,0 +1,39 @@
+// Copyright 2018 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_ARRAY_BUFFER_VIEW_H_
+#define COBALT_SCRIPT_ARRAY_BUFFER_VIEW_H_
+
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/script_value.h"
+
+namespace cobalt {
+namespace script {
+
+// https://heycam.github.io/webidl/#ArrayBufferView
+class ArrayBufferView {
+ public:
+  virtual ~ArrayBufferView() {}
+
+  virtual Handle<ArrayBuffer> Buffer() const = 0;
+
+  virtual size_t ByteOffset() const = 0;
+  virtual size_t ByteLength() const = 0;
+  virtual void* RawData() const = 0;
+};
+
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_ARRAY_BUFFER_VIEW_H_
diff --git a/src/cobalt/script/data_view.h b/src/cobalt/script/data_view.h
new file mode 100644
index 0000000..33e614b
--- /dev/null
+++ b/src/cobalt/script/data_view.h
@@ -0,0 +1,37 @@
+// Copyright 2018 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_DATA_VIEW_H_
+#define COBALT_SCRIPT_DATA_VIEW_H_
+
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/script_value.h"
+
+namespace cobalt {
+namespace script {
+
+// http://www.ecma-international.org/ecma-262/6.0/#sec-dataview-constructor
+class DataView : public ArrayBufferView {
+ public:
+  // http://www.ecma-international.org/ecma-262/6.0/#sec-dataview-buffer-byteoffset-bytelength
+  static Handle<DataView> New(GlobalEnvironment* global_environment,
+                              Handle<ArrayBuffer> array_buffer,
+                              size_t byte_offset, size_t byte_length);
+};
+
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_DATA_VIEW_H_
diff --git a/src/cobalt/script/fake_global_environment.h b/src/cobalt/script/fake_global_environment.h
index 15c8db2..8017741 100644
--- a/src/cobalt/script/fake_global_environment.h
+++ b/src/cobalt/script/fake_global_environment.h
@@ -27,13 +27,12 @@
  public:
   void CreateGlobalObject() override {}
   bool EvaluateScript(const scoped_refptr<SourceCode>& /*script_utf8*/,
-                      bool /*mute_errors*/,
                       std::string* /*out_result*/) override {
     return false;
   }
   bool EvaluateScript(
       const scoped_refptr<SourceCode>& /*script_utf8*/,
-      const scoped_refptr<Wrappable>& /*owning_object*/, bool /*mute_errors*/,
+      const scoped_refptr<Wrappable>& /*owning_object*/,
       base::optional<ValueHandleHolder::Reference>* /*out_value_handle*/)
       override {
     return false;
@@ -47,6 +46,8 @@
       const scoped_refptr<Wrappable>& /*wrappable*/) override {}
   void AllowGarbageCollection(
       const scoped_refptr<Wrappable>& /*wrappable*/) override {}
+  void AddRoot(Traceable* /*traceable*/) override {}
+  void RemoveRoot(Traceable* /*traceable*/) override {}
   void DisableEval(const std::string& /*message*/) override {}
   void EnableEval() override {}
   void DisableJit() override {}
diff --git a/src/cobalt/script/global_environment.h b/src/cobalt/script/global_environment.h
index b2836f2..8272039 100644
--- a/src/cobalt/script/global_environment.h
+++ b/src/cobalt/script/global_environment.h
@@ -59,7 +59,6 @@
   // If out_result is non-NULL, it will be set to hold the result of the script
   // evaluation if the script succeeds, or an exception message if it fails.
   virtual bool EvaluateScript(const scoped_refptr<SourceCode>& script_utf8,
-                              bool mute_errors,
                               std::string* out_result_utf8) = 0;
 
   // Evaluate the JavaScript source code. Returns true on success,
@@ -68,7 +67,7 @@
   // of the script that is owned by |owner|.
   virtual bool EvaluateScript(
       const scoped_refptr<SourceCode>& script_utf8,
-      const scoped_refptr<Wrappable>& owning_object, bool mute_errors,
+      const scoped_refptr<Wrappable>& owning_object,
       base::optional<ValueHandleHolder::Reference>* out_value_handle) = 0;
 
   // Returns the stack trace as a vector of individual frames.
@@ -95,6 +94,14 @@
   virtual void AllowGarbageCollection(
       const scoped_refptr<Wrappable>& wrappable) = 0;
 
+  // Register |traceable| as a member of the root set, i.e., an a priori
+  // reachable node.  In a manner similar to |PreventGarbageCollection|,
+  // multiple calls are supported.
+  virtual void AddRoot(Traceable* traceable) = 0;
+
+  // Undo a registration from |AddRoot|.
+  virtual void RemoveRoot(Traceable* traceable) = 0;
+
   // Disable eval() and specify the message to report in the exception.
   virtual void DisableEval(const std::string& message) = 0;
 
diff --git a/src/cobalt/script/javascript_engine.h b/src/cobalt/script/javascript_engine.h
index b96a432..69a8f14 100644
--- a/src/cobalt/script/javascript_engine.h
+++ b/src/cobalt/script/javascript_engine.h
@@ -72,9 +72,6 @@
   // Returns true if the error handler could be installed. False otherwise.
   virtual bool RegisterErrorHandler(ErrorHandler handler) = 0;
 
-  // Adjusts the memory threshold to force garbage collection.
-  virtual void SetGcThreshold(int64_t bytes) = 0;
-
   // Get the current |HeapStatistics| measurements for this engine.  Note that
   // engines will implement this to the best of their ability, but will likely
   // be unable to provide perfectly accurate values.
@@ -85,6 +82,10 @@
   friend class scoped_ptr<JavaScriptEngine>;
 };
 
+// Returns the name and version of the JavaScript engine being used, joined
+// by "/". Example output (when using V8) will look like "V8/6.5.254.28".
+std::string GetJavaScriptEngineNameAndVersion();
+
 }  // namespace script
 }  // namespace cobalt
 
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp
index 51afe27..e0ea8ed 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45.gyp
+++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -23,10 +23,15 @@
         'conversion_helpers.h',
         'convert_callback_return_value.h',
         'interface_data.h',
+        'mozjs_array_buffer.cc',
+        'mozjs_array_buffer.h',
+        'mozjs_array_buffer_view.h',
         'mozjs_callback_function.h',
         'mozjs_callback_interface.cc',
         'mozjs_callback_interface.h',
         'mozjs_callback_interface_holder.h',
+        'mozjs_data_view.cc',
+        'mozjs_data_view.h',
         'mozjs_engine.cc',
         'mozjs_engine.h',
         'mozjs_exception_state.cc',
@@ -41,9 +46,11 @@
         'mozjs_source_code.h',
         'mozjs_tracer.cc',
         'mozjs_tracer.h',
+        'mozjs_typed_arrays.cc',
+        'mozjs_typed_arrays.h',
         'mozjs_user_object_holder.h',
-        'mozjs_value_handle.h',
         'mozjs_value_handle.cc',
+        'mozjs_value_handle.h',
         'mozjs_wrapper_handle.h',
         'native_promise.h',
         'promise_wrapper.cc',
diff --git a/src/cobalt/script/mozjs-45/mozjs_array_buffer.cc b/src/cobalt/script/mozjs-45/mozjs_array_buffer.cc
new file mode 100644
index 0000000..0fc3e48
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_array_buffer.cc
@@ -0,0 +1,79 @@
+// Copyright 2018 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-45/mozjs_array_buffer.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+
+// TODO: These can be merged into common code (using SbMemoryAllocate
+// directly) once AllocationMetadata is removed.
+PreallocatedArrayBufferData::PreallocatedArrayBufferData(size_t byte_length) {
+  data_ = js_malloc(byte_length);
+  byte_length_ = byte_length;
+}
+
+PreallocatedArrayBufferData::~PreallocatedArrayBufferData() {
+  if (data_) {
+    js_free(data_);
+    data_ = nullptr;
+    byte_length_ = 0;
+  }
+}
+
+// static
+Handle<ArrayBuffer> ArrayBuffer::New(GlobalEnvironment* global_environment,
+                                     size_t byte_length) {
+  auto* mozjs_global_environment =
+      base::polymorphic_downcast<mozjs::MozjsGlobalEnvironment*>(
+          global_environment);
+  JSContext* context = mozjs_global_environment->context();
+  JS::RootedObject global_object(context,
+                                 mozjs_global_environment->global_object());
+  JSAutoRequest auto_request(context);
+  JSAutoCompartment auto_compartment(context, global_object);
+  JS::RootedValue array_buffer(context);
+  array_buffer.setObjectOrNull(JS_NewArrayBuffer(context, byte_length));
+  DCHECK(array_buffer.isObject());
+  return Handle<ArrayBuffer>(
+      new mozjs::MozjsUserObjectHolder<mozjs::MozjsArrayBuffer>(context,
+                                                                array_buffer));
+}
+
+// static
+Handle<ArrayBuffer> ArrayBuffer::New(GlobalEnvironment* global_environment,
+                                     PreallocatedArrayBufferData* data) {
+  auto* mozjs_global_environment =
+      base::polymorphic_downcast<mozjs::MozjsGlobalEnvironment*>(
+          global_environment);
+  JSContext* context = mozjs_global_environment->context();
+  JS::RootedObject global_object(context,
+                                 mozjs_global_environment->global_object());
+  JSAutoRequest auto_request(context);
+  JSAutoCompartment auto_compartment(context, global_object);
+
+  JS::RootedValue array_buffer(context);
+  array_buffer.setObjectOrNull(JS_NewArrayBufferWithContents(
+      context, data->byte_length(), data->data()));
+  DCHECK(array_buffer.isObject());
+  data->Release();
+  return Handle<ArrayBuffer>(
+      new mozjs::MozjsUserObjectHolder<mozjs::MozjsArrayBuffer>(context,
+                                                                array_buffer));
+}
+
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_array_buffer.h b/src/cobalt/script/mozjs-45/mozjs_array_buffer.h
new file mode 100644
index 0000000..92c17fa
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_array_buffer.h
@@ -0,0 +1,108 @@
+// Copyright 2018 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_45_MOZJS_ARRAY_BUFFER_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_ARRAY_BUFFER_H_
+
+#include "base/logging.h"
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "third_party/mozjs-45/js/src/jsfriendapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+class MozjsArrayBuffer final : public ArrayBuffer {
+ public:
+  using BaseType = ArrayBuffer;
+
+  MozjsArrayBuffer(JSContext* context, JS::HandleValue value)
+      : context_(context), weak_heap_object_(context, value) {
+    DCHECK(value.isObject());
+    DCHECK(JS_IsArrayBufferObject(&value.toObject()));
+  }
+
+  JSObject* handle() const { return weak_heap_object_.GetObject(); }
+  const JS::Value& value() const { return weak_heap_object_.GetValue(); }
+  bool WasCollected() const { return weak_heap_object_.WasCollected(); }
+
+  size_t ByteLength() const override {
+    return JS_GetArrayBufferByteLength(weak_heap_object_.GetObject());
+  }
+
+  void* Data() const override {
+    bool is_shared_memory;
+    JS::AutoCheckCannotGC no_gc;
+    return JS_GetArrayBufferData(weak_heap_object_.GetObject(),
+                                 &is_shared_memory, no_gc);
+  }
+
+ private:
+  JSContext* context_;
+  WeakHeapObject weak_heap_object_;
+};
+
+template <>
+struct TypeTraits<ArrayBuffer> {
+  using ConversionType = MozjsUserObjectHolder<MozjsArrayBuffer>;
+  using ReturnType = const ScriptValue<ArrayBuffer>*;
+};
+
+inline void ToJSValue(JSContext* context,
+                      const ScriptValue<ArrayBuffer>* array_buffer_value,
+                      JS::MutableHandleValue out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+
+  if (!array_buffer_value) {
+    out_value.set(JS::NullValue());
+    return;
+  }
+
+  const auto* mozjs_array_buffer_value = base::polymorphic_downcast<
+      const MozjsUserObjectHolder<MozjsArrayBuffer>*>(array_buffer_value);
+  out_value.setObject(*mozjs_array_buffer_value->js_object());
+}
+
+inline void FromJSValue(
+    JSContext* context, JS::HandleValue value, int conversion_flags,
+    ExceptionState* exception_state,
+    MozjsUserObjectHolder<MozjsArrayBuffer>* out_array_buffer) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_array_buffer);
+
+  if (!value.isObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!JS_IsArrayBufferObject(&value.toObject())) {
+    exception_state->SetSimpleException(kTypeError,
+                                        "Expected object of type ArrayBuffer");
+    return;
+  }
+
+  *out_array_buffer = MozjsUserObjectHolder<MozjsArrayBuffer>(context, value);
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_MOZJS_45_MOZJS_ARRAY_BUFFER_H_
diff --git a/src/cobalt/script/mozjs-45/mozjs_array_buffer_view.h b/src/cobalt/script/mozjs-45/mozjs_array_buffer_view.h
new file mode 100644
index 0000000..1927d3a
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_array_buffer_view.h
@@ -0,0 +1,138 @@
+// Copyright 2018 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_45_MOZJS_ARRAY_BUFFER_VIEW_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_ARRAY_BUFFER_VIEW_H_
+
+#include "base/logging.h"
+#include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "third_party/mozjs-45/js/src/jsfriendapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+class MozjsArrayBufferView final : public ArrayBufferView {
+ public:
+  using BaseType = ArrayBufferView;
+
+  MozjsArrayBufferView(JSContext* context, JS::HandleValue value)
+      : context_(context), weak_heap_object_(context, value) {
+    DCHECK(value.isObject());
+    DCHECK(JS_IsArrayBufferViewObject(&value.toObject()));
+  }
+
+  JSObject* handle() const { return weak_heap_object_.GetObject(); }
+  const JS::Value& value() const { return weak_heap_object_.GetValue(); }
+  bool WasCollected() const { return weak_heap_object_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_heap_object_.Trace(js_tracer); }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    JSAutoRequest auto_request(context_);
+    JS::RootedObject array_buffer_view(context_, weak_heap_object_.GetObject());
+    bool is_shared_memory;
+    JSObject* object = JS_GetArrayBufferViewBuffer(context_, array_buffer_view,
+                                                   &is_shared_memory);
+    JS::RootedValue value(context_);
+    value.setObject(*object);
+    return Handle<ArrayBuffer>(
+        new MozjsUserObjectHolder<MozjsArrayBuffer>(context_, value));
+  }
+
+  size_t ByteOffset() const override {
+    JSObject* object = weak_heap_object_.GetObject();
+    if (JS_IsTypedArrayObject(object)) {
+      return JS_GetTypedArrayByteOffset(object);
+    } else if (JS_IsDataViewObject(object)) {
+      return JS_GetDataViewByteOffset(object);
+    }
+
+    // Typed arrays and DataView are the only classes (in the JavaScript
+    // sense) that implement ArrayBufferView.
+    NOTREACHED();
+    return 0u;
+  }
+
+  size_t ByteLength() const override {
+    return JS_GetArrayBufferViewByteLength(weak_heap_object_.GetObject());
+  }
+
+  void* RawData() const override {
+    bool is_shared_memory;
+    JS::AutoCheckCannotGC no_gc;
+    return JS_GetArrayBufferViewData(weak_heap_object_.GetObject(),
+                                     &is_shared_memory, no_gc);
+  }
+
+ private:
+  JSContext* context_;
+  WeakHeapObject weak_heap_object_;
+};
+
+template <>
+struct TypeTraits<ArrayBufferView> {
+  using ConversionType = MozjsUserObjectHolder<MozjsArrayBufferView>;
+  using ReturnType = const ScriptValue<ArrayBufferView>*;
+};
+
+inline void ToJSValue(
+    JSContext* context,
+    const ScriptValue<ArrayBufferView>* array_buffer_view_value,
+    JS::MutableHandleValue out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+
+  if (!array_buffer_view_value) {
+    out_value.set(JS::NullValue());
+    return;
+  }
+
+  const auto* mozjs_array_buffer_view_value = base::polymorphic_downcast<
+      const MozjsUserObjectHolder<MozjsArrayBufferView>*>(
+      array_buffer_view_value);
+  out_value.setObject(*mozjs_array_buffer_view_value->js_object());
+}
+
+inline void FromJSValue(
+    JSContext* context, JS::HandleValue value, int conversion_flags,
+    ExceptionState* exception_state,
+    MozjsUserObjectHolder<MozjsArrayBufferView>* out_array_buffer_view) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_array_buffer_view);
+
+  if (!value.isObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!JS_IsArrayBufferViewObject(&value.toObject())) {
+    exception_state->SetSimpleException(
+        kTypeError, "Expected object of type ArrayBufferView");
+    return;
+  }
+
+  *out_array_buffer_view =
+      MozjsUserObjectHolder<MozjsArrayBufferView>(context, value);
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_MOZJS_45_MOZJS_ARRAY_BUFFER_VIEW_H_
diff --git a/src/cobalt/script/mozjs-45/mozjs_data_view.cc b/src/cobalt/script/mozjs-45/mozjs_data_view.cc
new file mode 100644
index 0000000..506165f
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_data_view.cc
@@ -0,0 +1,51 @@
+// Copyright 2018 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-45/mozjs_data_view.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+
+// static
+Handle<DataView> DataView::New(GlobalEnvironment* global_environment,
+                               Handle<ArrayBuffer> array_buffer,
+                               size_t byte_offset, size_t byte_length) {
+  auto* mozjs_global_environment =
+      base::polymorphic_downcast<mozjs::MozjsGlobalEnvironment*>(
+          global_environment);
+  JSContext* context = mozjs_global_environment->context();
+  JS::RootedObject global_object(context,
+                                 mozjs_global_environment->global_object());
+  JSAutoRequest auto_request(context);
+  JSAutoCompartment auto_compartment(context, global_object);
+
+  const auto* mozjs_array_buffer = base::polymorphic_downcast<
+      const mozjs::MozjsUserObjectHolder<mozjs::MozjsArrayBuffer>*>(
+      array_buffer.GetScriptValue());
+  DCHECK(mozjs_array_buffer->js_value().isObject());
+  JS::RootedObject rooted_array_buffer(context,
+                                       mozjs_array_buffer->js_object());
+  JS::RootedValue data_view(context);
+  data_view.setObjectOrNull(
+      JS_NewDataView(context, rooted_array_buffer, byte_offset, byte_length));
+  DCHECK(data_view.isObject());
+  return Handle<DataView>(
+      new mozjs::MozjsUserObjectHolder<mozjs::MozjsDataView>(context,
+                                                             data_view));
+}
+
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_data_view.h b/src/cobalt/script/mozjs-45/mozjs_data_view.h
new file mode 100644
index 0000000..33ad5e2
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_data_view.h
@@ -0,0 +1,124 @@
+// Copyright 2018 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_45_MOZJS_DATA_VIEW_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_DATA_VIEW_H_
+
+#include "base/logging.h"
+#include "cobalt/script/data_view.h"
+#include "cobalt/script/mozjs-45/mozjs_array_buffer.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "third_party/mozjs-45/js/src/jsfriendapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+class MozjsDataView final : public DataView {
+ public:
+  using BaseType = DataView;
+
+  MozjsDataView(JSContext* context, JS::HandleValue value)
+      : context_(context), weak_heap_object_(context, value) {
+    DCHECK(value.isObject());
+    DCHECK(JS_IsDataViewObject(&value.toObject()));
+  }
+
+  JSObject* handle() const { return weak_heap_object_.GetObject(); }
+  const JS::Value& value() const { return weak_heap_object_.GetValue(); }
+  bool WasCollected() const { return weak_heap_object_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_heap_object_.Trace(js_tracer); }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    JSAutoRequest auto_request(context_);
+    JS::RootedObject array_buffer_view(context_, weak_heap_object_.GetObject());
+    bool is_shared_memory;
+    JSObject* object = JS_GetArrayBufferViewBuffer(context_, array_buffer_view,
+                                                   &is_shared_memory);
+    JS::RootedValue value(context_);
+    value.setObject(*object);
+    return Handle<ArrayBuffer>(
+        new MozjsUserObjectHolder<MozjsArrayBuffer>(context_, value));
+  }
+
+  size_t ByteOffset() const override {
+    return JS_GetDataViewByteOffset(weak_heap_object_.GetObject());
+  }
+
+  size_t ByteLength() const override {
+    return JS_GetDataViewByteLength(weak_heap_object_.GetObject());
+  }
+
+  void* RawData() const override {
+    JS::AutoCheckCannotGC no_gc;
+    return JS_GetDataViewData(weak_heap_object_.GetObject(), no_gc);
+  }
+
+ protected:
+  JSContext* context_;
+  WeakHeapObject weak_heap_object_;
+};
+
+template <>
+struct TypeTraits<DataView> {
+  using ConversionType = MozjsUserObjectHolder<MozjsDataView>;
+  using ReturnType = const ScriptValue<DataView>*;
+};
+
+inline void ToJSValue(JSContext* context,
+                      const ScriptValue<DataView>* array_buffer_view_value,
+                      JS::MutableHandleValue out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+
+  if (!array_buffer_view_value) {
+    out_value.set(JS::NullValue());
+    return;
+  }
+
+  const auto* mozjs_array_buffer_view_value =
+      base::polymorphic_downcast<const MozjsUserObjectHolder<MozjsDataView>*>(
+          array_buffer_view_value);
+  out_value.setObject(*mozjs_array_buffer_view_value->js_object());
+}
+
+inline void FromJSValue(
+    JSContext* context, JS::HandleValue value, int conversion_flags,
+    ExceptionState* exception_state,
+    MozjsUserObjectHolder<MozjsDataView>* out_array_buffer_view) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_array_buffer_view);
+
+  if (!value.isObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!JS_IsDataViewObject(&value.toObject())) {
+    exception_state->SetSimpleException(kTypeError,
+                                        "Expected object of type DataView");
+    return;
+  }
+
+  *out_array_buffer_view = MozjsUserObjectHolder<MozjsDataView>(context, value);
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_MOZJS_45_MOZJS_DATA_VIEW_H_
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.cc b/src/cobalt/script/mozjs-45/mozjs_engine.cc
index 5825d73..1086500 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.cc
@@ -24,6 +24,7 @@
 #include "cobalt/browser/stack_size_constants.h"
 #include "cobalt/script/mozjs-45/mozjs_global_environment.h"
 #include "starboard/once.h"
+#include "third_party/mozjs-45/cobalt_config/include/js-confdefs.h"
 #include "third_party/mozjs-45/js/public/Initialization.h"
 #include "third_party/mozjs-45/js/src/jsapi.h"
 #include "third_party/mozjs-45/js/src/vm/Runtime.h"
@@ -189,11 +190,6 @@
   return true;
 }
 
-void MozjsEngine::SetGcThreshold(int64_t bytes) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  runtime_->gc.setMaxMallocBytes(static_cast<size_t>(bytes));
-}
-
 HeapStatistics MozjsEngine::GetHeapStatistics() {
   DCHECK(thread_checker_.CalledOnValidThread());
   // There is unfortunately no easy way to get used vs total in SpiderMonkey,
@@ -262,5 +258,9 @@
   return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine(options));
 }
 
+std::string GetJavaScriptEngineNameAndVersion() {
+  return std::string("SpiderMonkey/") + MOZILLA_VERSION;
+}
+
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.h b/src/cobalt/script/mozjs-45/mozjs_engine.h
index b1d01ad..54b3267 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.h
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.h
@@ -35,7 +35,6 @@
   void CollectGarbage() override;
   void AdjustAmountOfExternalAllocatedMemory(int64_t bytes) override;
   bool RegisterErrorHandler(ErrorHandler handler) override;
-  void SetGcThreshold(int64_t bytes) override;
   HeapStatistics GetHeapStatistics() override;
 
  private:
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 0bb5c7e..12ada40 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -212,7 +212,7 @@
 }
 
 bool MozjsGlobalEnvironment::EvaluateScript(
-    const scoped_refptr<SourceCode>& source_code, bool mute_errors,
+    const scoped_refptr<SourceCode>& source_code,
     std::string* out_result_utf8) {
   TRACK_MEMORY_SCOPE("Javascript");
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -225,8 +225,7 @@
   std::string error_message;
   last_error_message_ = &error_message;
 
-  bool success =
-      EvaluateScriptInternal(source_code, mute_errors, &result_value);
+  bool success = EvaluateScriptInternal(source_code, &result_value);
   if (out_result_utf8) {
     if (success) {
       MozjsExceptionState exception_state(context_);
@@ -244,7 +243,7 @@
 
 bool MozjsGlobalEnvironment::EvaluateScript(
     const scoped_refptr<SourceCode>& source_code,
-    const scoped_refptr<Wrappable>& owning_object, bool mute_errors,
+    const scoped_refptr<Wrappable>& owning_object,
     base::optional<ValueHandleHolder::Reference>* out_value_handle) {
   TRACK_MEMORY_SCOPE("Javascript");
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -252,8 +251,7 @@
   JSAutoCompartment auto_compartment(context_, global_object_proxy_);
   JS::AutoSaveExceptionState auto_save_exception_state(context_);
   JS::RootedValue result_value(context_);
-  bool success =
-      EvaluateScriptInternal(source_code, mute_errors, &result_value);
+  bool success = EvaluateScriptInternal(source_code, &result_value);
   if (success && out_value_handle) {
     MozjsValueHandleHolder mozjs_value_holder(context_, result_value);
     out_value_handle->emplace(owning_object.get(), mozjs_value_holder);
@@ -262,7 +260,7 @@
 }
 
 bool MozjsGlobalEnvironment::EvaluateScriptInternal(
-    const scoped_refptr<SourceCode>& source_code, bool mute_errors,
+    const scoped_refptr<SourceCode>& source_code,
     JS::MutableHandleValue out_result) {
   TRACK_MEMORY_SCOPE("Javascript");
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -289,7 +287,7 @@
 
   JS::CompileOptions options(context_);
   options.setFileAndLine(location.file_path.c_str(), location.line_number)
-      .setMutedErrors(mute_errors);
+      .setMutedErrors(mozjs_source_code->is_muted());
   bool success =
       JS::Evaluate(context_, options, inflated_buffer, length, out_result);
   if (!success && context_->isExceptionPending()) {
@@ -307,7 +305,7 @@
   scoped_refptr<SourceCode> source_code =
       new MozjsSourceCode(source, base::SourceLocation(filename, 1, 1));
   std::string result;
-  bool success = EvaluateScript(source_code, false /*mute_errors*/, &result);
+  bool success = EvaluateScript(source_code, &result);
   if (!success) {
     DLOG(FATAL) << result;
   }
@@ -353,6 +351,14 @@
   }
 }
 
+void MozjsGlobalEnvironment::AddRoot(Traceable* traceable) {
+  roots_.insert(traceable);
+}
+
+void MozjsGlobalEnvironment::RemoveRoot(Traceable* traceable) {
+  roots_.erase(traceable);
+}
+
 void MozjsGlobalEnvironment::DisableEval(const std::string& message) {
   DCHECK(thread_checker_.CalledOnValidThread());
   eval_disabled_message_.emplace(message);
@@ -413,21 +419,21 @@
 }
 
 // static
-void MozjsGlobalEnvironment::TraceFunction(JSTracer* tracer, void* data) {
+void MozjsGlobalEnvironment::TraceFunction(JSTracer* js_tracer, void* data) {
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(data);
   if (global_environment->global_object_proxy_) {
-    JS_CallObjectTracer(tracer, &global_environment->global_object_proxy_,
+    JS_CallObjectTracer(js_tracer, &global_environment->global_object_proxy_,
                         "MozjsGlobalEnvironment");
   }
 
   for (auto& interface_data : global_environment->cached_interface_data_) {
     if (interface_data.prototype) {
-      JS_CallObjectTracer(tracer, &interface_data.prototype,
+      JS_CallObjectTracer(js_tracer, &interface_data.prototype,
                           "MozjsGlobalEnvironment");
     }
     if (interface_data.interface_object) {
-      JS_CallObjectTracer(tracer, &interface_data.interface_object,
+      JS_CallObjectTracer(js_tracer, &interface_data.interface_object,
                           "MozjsGlobalEnvironment");
     }
   }
@@ -436,9 +442,15 @@
   for (auto& pair : kept_alive_objects_) {
     auto& counted_heap_object = pair.second;
     DCHECK_GT(counted_heap_object.count, 0);
-    JS_CallObjectTracer(tracer, &counted_heap_object.heap_object,
+    JS_CallObjectTracer(js_tracer, &counted_heap_object.heap_object,
                         "MozjsGlobalEnvironment");
   }
+
+  MozjsTracer mozjs_tracer(js_tracer);
+  for (Traceable* root : global_environment->roots_) {
+    mozjs_tracer.Trace(root);
+    mozjs_tracer.DrainFrontier();
+  }
 }
 
 void MozjsGlobalEnvironment::EvaluateAutomatics() {
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index da75983..b184ec0 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -17,6 +17,7 @@
 
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include "base/hash_tables.h"
@@ -65,12 +66,12 @@
       const scoped_refptr<GlobalInterface>& global_interface,
       EnvironmentSettings* environment_settings);
 
-  bool EvaluateScript(const scoped_refptr<SourceCode>& script, bool mute_errors,
+  bool EvaluateScript(const scoped_refptr<SourceCode>& script,
                       std::string* out_result_utf8) override;
 
   bool EvaluateScript(
       const scoped_refptr<SourceCode>& script_utf8,
-      const scoped_refptr<Wrappable>& owning_object, bool mute_errors,
+      const scoped_refptr<Wrappable>& owning_object,
       base::optional<ValueHandleHolder::Reference>* out_value_handle) override;
 
   std::vector<StackFrame> GetStackTrace(int max_frames) override;
@@ -82,6 +83,10 @@
   void AllowGarbageCollection(
       const scoped_refptr<Wrappable>& wrappable) override;
 
+  void AddRoot(Traceable* traceable) override;
+
+  void RemoveRoot(Traceable* traceable) override;
+
   void DisableEval(const std::string& message) override;
 
   void EnableEval() override;
@@ -169,7 +174,6 @@
   void EvaluateAutomatics();
 
   bool EvaluateScriptInternal(const scoped_refptr<SourceCode>& source_code,
-                              bool mute_errors,
                               JS::MutableHandleValue out_result);
 
   void EvaluateEmbeddedScript(const unsigned char* data, size_t size,
@@ -190,6 +194,7 @@
   EnvironmentSettings* environment_settings_;
   // TODO: Should be |std::unordered_set| once C++11 is enabled.
   base::hash_set<Traceable*> visited_traceables_;
+  std::unordered_multiset<Traceable*> roots_;
 
   // Store the result of "Promise" immediately after evaluating the
   // promise polyfill in order to defend against application JavaScript
diff --git a/src/cobalt/script/mozjs-45/mozjs_source_code.cc b/src/cobalt/script/mozjs-45/mozjs_source_code.cc
index 85c936a..72e2430 100644
--- a/src/cobalt/script/mozjs-45/mozjs_source_code.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_source_code.cc
@@ -17,10 +17,11 @@
 namespace cobalt {
 namespace script {
 
+// static
 scoped_refptr<SourceCode> SourceCode::CreateSourceCode(
-    const std::string& script_utf8,
-    const base::SourceLocation& script_location) {
-  return new mozjs::MozjsSourceCode(script_utf8, script_location);
+    const std::string& script_utf8, const base::SourceLocation& script_location,
+    bool mute_errors) {
+  return new mozjs::MozjsSourceCode(script_utf8, script_location, mute_errors);
 }
 
 }  // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_source_code.h b/src/cobalt/script/mozjs-45/mozjs_source_code.h
index 566b591..5e25cae 100644
--- a/src/cobalt/script/mozjs-45/mozjs_source_code.h
+++ b/src/cobalt/script/mozjs-45/mozjs_source_code.h
@@ -34,14 +34,20 @@
 class MozjsSourceCode : public SourceCode {
  public:
   MozjsSourceCode(const std::string& source_utf8,
-                  const base::SourceLocation& source_location)
-      : source_utf8_(source_utf8), location_(source_location) {}
+                  const base::SourceLocation& source_location,
+                  bool is_muted = false)
+      : source_utf8_(source_utf8),
+        location_(source_location),
+        is_muted_(is_muted) {}
+
   const std::string& source_utf8() const { return source_utf8_; }
   const base::SourceLocation& location() const { return location_; }
+  bool is_muted() const { return is_muted_; }
 
  private:
   std::string source_utf8_;
   base::SourceLocation location_;
+  bool is_muted_;
 };
 
 }  // namespace mozjs
diff --git a/src/cobalt/script/mozjs-45/mozjs_tracer.cc b/src/cobalt/script/mozjs-45/mozjs_tracer.cc
index 99f4f5f..a64ccf5 100644
--- a/src/cobalt/script/mozjs-45/mozjs_tracer.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_tracer.cc
@@ -80,13 +80,17 @@
   DCHECK(JS_ContextIterator(js_tracer_->runtime(), &context) == nullptr);
 }
 
-void MozjsTracer::TraceFrom(Wrappable* wrappable) {
+void MozjsTracer::TraceFrom(Traceable* traceable) {
   DCHECK(frontier_.empty());
-  frontier_.push_back(wrappable);
+  frontier_.push_back(traceable);
+  DrainFrontier();
+}
+
+void MozjsTracer::DrainFrontier() {
   while (!frontier_.empty()) {
-    Traceable* traceable = frontier_.back();
+    Traceable* back = frontier_.back();
     frontier_.pop_back();
-    traceable->TraceMembers(this);
+    back->TraceMembers(this);
   }
 }
 
diff --git a/src/cobalt/script/mozjs-45/mozjs_tracer.h b/src/cobalt/script/mozjs-45/mozjs_tracer.h
index 22180b1..23aad96 100644
--- a/src/cobalt/script/mozjs-45/mozjs_tracer.h
+++ b/src/cobalt/script/mozjs-45/mozjs_tracer.h
@@ -36,6 +36,7 @@
 class MozjsTracer : public ::cobalt::script::Tracer {
  public:
   explicit MozjsTracer(JSTracer* js_tracer) : js_tracer_(js_tracer) {}
+  ~MozjsTracer() { DCHECK_EQ(frontier_.size(), 0); }
 
   void Trace(Traceable* traceable) override;
 
@@ -43,7 +44,13 @@
   // |wrappable|.  For any reachable |Traceable|s that have corresponding
   // wrappers, feed those wrappers to the SpiderMonkey-side tracer.  In the
   // case that they don't, add them to |frontier_|, and trace them ourselves.
-  void TraceFrom(Wrappable* wrappable);
+  void TraceFrom(Traceable* traceable);
+
+  // Trace all remaining items in |frontier_|.  This is mainly to facilitate
+  // the use case of beginning a tracing session at a |Traceable| that you
+  // don't know is a |Wrappable|, so you would like to begin tracing it with
+  // |Trace|.
+  void DrainFrontier();
 
   // This is primarly exposed for |MozjsUserObjectHolder|'s implementation of
   // |TraceMembers|.
diff --git a/src/cobalt/script/mozjs-45/mozjs_typed_arrays.cc b/src/cobalt/script/mozjs-45/mozjs_typed_arrays.cc
new file mode 100644
index 0000000..0c71bd3
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_typed_arrays.cc
@@ -0,0 +1,81 @@
+// Copyright 2018 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-45/mozjs_typed_arrays.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+
+namespace {
+
+template <typename T>
+struct InterfaceToMozjs;
+
+#define COBALT_SCRIPT_DEFINE_INTERFACE_TO_MOZJS(array, ctype)                 \
+  template <>                                                                 \
+  struct InterfaceToMozjs<array> {                                            \
+    using Result = mozjs::Mozjs##array;                                       \
+    static constexpr auto JS_NewArrayWithBuffer = &JS_New##array##WithBuffer; \
+  };
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_DEFINE_INTERFACE_TO_MOZJS)
+#undef COBALT_SCRIPT_DEFINE_INTERFACE_TO_MOZJS
+
+}  // namespace
+
+// static
+template <typename CType, bool IsClamped>
+Handle<TypedArrayImpl<CType, IsClamped>> TypedArrayImpl<CType, IsClamped>::New(
+    GlobalEnvironment* global_environment, Handle<ArrayBuffer> array_buffer,
+    size_t byte_offset, size_t length) {
+  using TypedArrayImplType = TypedArrayImpl<CType, IsClamped>;
+  using MozjsTypedArrayImplType =
+      typename InterfaceToMozjs<TypedArrayImplType>::Result;
+  static constexpr auto JS_NewArrayWithBuffer =
+      InterfaceToMozjs<TypedArrayImplType>::JS_NewArrayWithBuffer;
+
+  auto* mozjs_global_environment =
+      base::polymorphic_downcast<mozjs::MozjsGlobalEnvironment*>(
+          global_environment);
+  JSContext* context = mozjs_global_environment->context();
+  JS::RootedObject global_object(context,
+                                 mozjs_global_environment->global_object());
+  JSAutoRequest auto_request(context);
+  JSAutoCompartment auto_compartment(context, global_object);
+
+  const auto* mozjs_array_buffer = base::polymorphic_downcast<
+      const mozjs::MozjsUserObjectHolder<mozjs::MozjsArrayBuffer>*>(
+      array_buffer.GetScriptValue());
+  DCHECK(mozjs_array_buffer->js_value().isObject());
+  JS::RootedObject rooted_array_buffer(context,
+                                       mozjs_array_buffer->js_object());
+  JS::RootedValue typed_array(context);
+  typed_array.setObjectOrNull(
+      JS_NewArrayWithBuffer(context, rooted_array_buffer, byte_offset, length));
+  DCHECK(typed_array.isObject());
+  return Handle<TypedArrayImplType>(
+      new mozjs::MozjsUserObjectHolder<MozjsTypedArrayImplType>(context,
+                                                                typed_array));
+}
+
+#define COBALT_SCRIPT_INSTANTIATE(array, ctype)                            \
+  template Handle<array> array::New(GlobalEnvironment* global_environment, \
+                                    Handle<ArrayBuffer> array_buffer,      \
+                                    size_t byte_offset, size_t length);
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_INSTANTIATE)
+#undef COBALT_SCRIPT_INSTANTIATE
+
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_typed_arrays.h b/src/cobalt/script/mozjs-45/mozjs_typed_arrays.h
new file mode 100644
index 0000000..3fa8dbc
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_typed_arrays.h
@@ -0,0 +1,238 @@
+// Copyright 2018 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_45_MOZJS_TYPED_ARRAYS_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_TYPED_ARRAYS_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/exception_message.h"
+#include "cobalt/script/mozjs-45/mozjs_array_buffer.h"
+#include "cobalt/script/script_exception.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/typed_arrays.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+class MozjsTypedArray final : public TypedArray {
+ public:
+  using BaseType = TypedArray;
+
+  MozjsTypedArray(JSContext* context, JS::HandleValue value)
+      : context_(context), weak_heap_object_(context, value) {
+    DCHECK(value.isObject());
+    DCHECK(JS_IsTypedArrayObject(&value.toObject()));
+  }
+
+  JSObject* handle() const { return weak_heap_object_.GetObject(); }
+  const JS::Value& value() const { return weak_heap_object_.GetValue(); }
+  bool WasCollected() const { return weak_heap_object_.WasCollected(); }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    JSAutoRequest auto_request(context_);
+    JS::RootedObject array_buffer_view(context_, weak_heap_object_.GetObject());
+    bool is_shared_memory;
+    JSObject* object = JS_GetArrayBufferViewBuffer(context_, array_buffer_view,
+                                                   &is_shared_memory);
+    JS::RootedValue value(context_);
+    value.setObject(*object);
+    return Handle<ArrayBuffer>(
+        new MozjsUserObjectHolder<MozjsArrayBuffer>(context_, value));
+  }
+
+  size_t Length() const override {
+    return JS_GetTypedArrayLength(weak_heap_object_.GetObject());
+  }
+  size_t ByteOffset() const override {
+    return JS_GetTypedArrayByteOffset(weak_heap_object_.GetObject());
+  }
+  size_t ByteLength() const override {
+    return JS_GetTypedArrayByteLength(weak_heap_object_.GetObject());
+  }
+
+  void* RawData() const override {
+    bool is_shared_memory;
+    JS::AutoCheckCannotGC no_gc;
+    return JS_GetArrayBufferViewData(weak_heap_object_.GetObject(),
+                                     &is_shared_memory, no_gc);
+  }
+
+ private:
+  JSContext* context_;
+  WeakHeapObject weak_heap_object_;
+};
+
+template <>
+struct TypeTraits<TypedArray> {
+  using ConversionType = MozjsUserObjectHolder<MozjsTypedArray>;
+  using ReturnType = const ScriptValue<TypedArray>*;
+};
+
+inline void ToJSValue(JSContext* context,
+                      const ScriptValue<TypedArray>* typed_array_value,
+                      JS::MutableHandleValue out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+
+  if (!typed_array_value) {
+    out_value.set(JS::NullValue());
+    return;
+  }
+
+  const auto* mozjs_typed_array_value =
+      base::polymorphic_downcast<const MozjsUserObjectHolder<MozjsTypedArray>*>(
+          typed_array_value);
+  out_value.setObject(*mozjs_typed_array_value->js_object());
+}
+
+inline void FromJSValue(
+    JSContext* context, JS::HandleValue value, int conversion_flags,
+    ExceptionState* exception_state,
+    MozjsUserObjectHolder<MozjsTypedArray>* out_typed_array) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_typed_array);
+
+  if (!value.isObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!JS_IsTypedArrayObject(&value.toObject())) {
+    exception_state->SetSimpleException(kTypeError,
+                                        "Expected object of type TypedArray");
+    return;
+  }
+
+  *out_typed_array = MozjsUserObjectHolder<MozjsTypedArray>(context, value);
+}
+
+template <typename CType, bool IsClamped, typename BaseTypeName,
+          bool (*JS_IsFunction)(JSObject*),
+          void (*js_GetArrayLengthAndDataFunction)(JSObject*, uint32_t*, bool*,
+                                                   CType**),
+          CType* (*JS_GetArrayDataFunction)(JSObject*, bool*,
+                                            const JS::AutoCheckCannotGC&)>
+class MozjsTypedArrayImpl final : public BaseTypeName {
+ public:
+  using BaseType = BaseTypeName;
+
+  MozjsTypedArrayImpl(JSContext* context, JS::HandleValue value)
+      : context_(context), weak_heap_object_(context, value) {
+    DCHECK(value.isObject());
+    DCHECK(JS_IsFunction(&value.toObject()));
+  }
+
+  JSObject* handle() const { return weak_heap_object_.GetObject(); }
+  const JS::Value& value() const { return weak_heap_object_.GetValue(); }
+  bool WasCollected() const { return weak_heap_object_.WasCollected(); }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    JSAutoRequest auto_request(context_);
+    JS::RootedObject array_buffer_view(context_, weak_heap_object_.GetObject());
+    bool is_shared_memory;
+    JSObject* object = JS_GetArrayBufferViewBuffer(context_, array_buffer_view,
+                                                   &is_shared_memory);
+    JS::RootedValue value(context_);
+    value.setObject(*object);
+    return Handle<ArrayBuffer>(
+        new MozjsUserObjectHolder<MozjsArrayBuffer>(context_, value));
+  }
+
+  size_t Length() const override {
+    uint32_t length;
+    bool is_shared_memory;
+    CType* data;
+    js_GetArrayLengthAndDataFunction(weak_heap_object_.GetObject(), &length,
+                                     &is_shared_memory, &data);
+    return length;
+  }
+
+  size_t ByteOffset() const override {
+    return JS_GetTypedArrayByteOffset(weak_heap_object_.GetObject());
+  }
+
+  size_t ByteLength() const override {
+    return JS_GetTypedArrayByteLength(weak_heap_object_.GetObject());
+  }
+
+  CType* Data() const override {
+    bool is_shared_memory;
+    JS::AutoCheckCannotGC no_gc;
+    return JS_GetArrayDataFunction(weak_heap_object_.GetObject(),
+                                   &is_shared_memory, no_gc);
+  }
+
+  void* RawData() const override { return static_cast<void*>(Data()); }
+
+ private:
+  JSContext* context_;
+  WeakHeapObject weak_heap_object_;
+};
+
+#define COBALT_SCRIPT_USING_MOZJS_ARRAY(array, ctype)        \
+  using Mozjs##array =                                       \
+      MozjsTypedArrayImpl<ctype, false, array, JS_Is##array, \
+                          js::Get##array##LengthAndData, JS_Get##array##Data>;
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_USING_MOZJS_ARRAY)
+#undef COBALT_SCRIPT_USING_MOZJS_ARRAY
+
+#define COBALT_SCRIPT_CONVERSION_BOILERPLATE(array, ctype)                  \
+  template <>                                                               \
+  struct TypeTraits<array> {                                                \
+    using ConversionType = MozjsUserObjectHolder<Mozjs##array>;             \
+    using ReturnType = const ScriptValue<array>*;                           \
+  };                                                                        \
+                                                                            \
+  inline void ToJSValue(JSContext* context,                                 \
+                        const ScriptValue<array>* array_value,              \
+                        JS::MutableHandleValue out_value) {                 \
+    TRACK_MEMORY_SCOPE("Javascript");                                       \
+    if (!array_value) {                                                     \
+      out_value.set(JS::NullValue());                                       \
+      return;                                                               \
+    }                                                                       \
+    const auto* mozjs_array_value = base::polymorphic_downcast<             \
+        const MozjsUserObjectHolder<Mozjs##array>*>(array_value);           \
+    out_value.setObject(*mozjs_array_value->js_object());                   \
+  }                                                                         \
+                                                                            \
+  inline void FromJSValue(JSContext* context, JS::HandleValue value,        \
+                          int conversion_flags,                             \
+                          ExceptionState* exception_state,                  \
+                          MozjsUserObjectHolder<Mozjs##array>* out_array) { \
+    TRACK_MEMORY_SCOPE("Javascript");                                       \
+    DCHECK_EQ(0, conversion_flags);                                         \
+    DCHECK(out_array);                                                      \
+    if (!value.isObject()) {                                                \
+      exception_state->SetSimpleException(kNotObjectType);                  \
+      return;                                                               \
+    }                                                                       \
+    if (!JS_Is##array(&value.toObject())) {                                 \
+      exception_state->SetSimpleException(kTypeError,                       \
+                                          "Expected object of type array"); \
+      return;                                                               \
+    }                                                                       \
+    *out_array = MozjsUserObjectHolder<Mozjs##array>(context, value);       \
+  }
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_CONVERSION_BOILERPLATE)
+#undef COBALT_SCRIPT_CONVERSION_BOILERPLATE
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_MOZJS_45_MOZJS_TYPED_ARRAYS_H_
diff --git a/src/cobalt/script/script.gyp b/src/cobalt/script/script.gyp
index 2443254..4fb5832 100644
--- a/src/cobalt/script/script.gyp
+++ b/src/cobalt/script/script.gyp
@@ -20,9 +20,12 @@
       'target_name': 'script',
       'type': 'static_library',
       'sources': [
+        'array_buffer.h',
+        'array_buffer_view.h',
         'call_frame.h',
         'callback_function.h',
         'callback_interface_traits.h',
+        'data_view.h',
         'environment_settings.h',
         'error_report.h',
         'exception_message.cc',
@@ -51,6 +54,7 @@
         'stack_frame.cc',
         'stack_frame.h',
         'tracer.h',
+        'typed_arrays.h',
         'union_type.h',
         'union_type_internal.h',
         'util/stack_trace_helpers.h',
diff --git a/src/cobalt/script/script_runner.cc b/src/cobalt/script/script_runner.cc
index 436ad49..ebfa0b0 100644
--- a/src/cobalt/script/script_runner.cc
+++ b/src/cobalt/script/script_runner.cc
@@ -83,7 +83,7 @@
     const std::string& script_utf8, const base::SourceLocation& script_location,
     bool mute_errors, bool* out_succeeded) {
   scoped_refptr<SourceCode> source_code =
-      SourceCode::CreateSourceCode(script_utf8, script_location);
+      SourceCode::CreateSourceCode(script_utf8, script_location, mute_errors);
   if (out_succeeded) {
     *out_succeeded = false;
   }
@@ -92,7 +92,7 @@
     return "";
   }
   std::string result;
-  if (!global_environment_->EvaluateScript(source_code, mute_errors, &result)) {
+  if (!global_environment_->EvaluateScript(source_code, &result)) {
     LOG(WARNING) << "Failed to execute JavaScript: " << result;
 #if defined(HANDLE_CORE_DUMP)
     script_runner_log.Get().IncrementFailCount();
diff --git a/src/cobalt/script/source_code.h b/src/cobalt/script/source_code.h
index 08d2cee..f564dbc 100644
--- a/src/cobalt/script/source_code.h
+++ b/src/cobalt/script/source_code.h
@@ -27,12 +27,11 @@
 // evaluated by a JavaScriptEngine
 class SourceCode : public base::RefCounted<SourceCode> {
  public:
-  // Convert the utf8 encoded string to an object that represents script that
-  // can be evaluated.
-  // Defined in the engine's implementation.
+  // Convert the UTF-8 encoded string to an object that represents script that
+  // can be evaluated. Defined in the engine's implementation.
   static scoped_refptr<SourceCode> CreateSourceCode(
       const std::string& script_utf8,
-      const base::SourceLocation& script_location);
+      const base::SourceLocation& script_location, bool is_muted = false);
 
  protected:
   SourceCode() {}
diff --git a/src/cobalt/script/standalone_javascript_runner.cc b/src/cobalt/script/standalone_javascript_runner.cc
index 9c80eee..a71d2d1 100644
--- a/src/cobalt/script/standalone_javascript_runner.cc
+++ b/src/cobalt/script/standalone_javascript_runner.cc
@@ -69,8 +69,7 @@
 
   // Execute the script and get the results of execution.
   std::string result;
-  bool success = global_environment_->EvaluateScript(
-      source, false /*mute_errors*/, &result);
+  bool success = global_environment_->EvaluateScript(source, &result);
   // Echo the results to stdout.
   if (!success) {
     std::cout << "Exception: ";
diff --git a/src/cobalt/script/tracer.h b/src/cobalt/script/tracer.h
index 3e7f771..c4fdcab 100644
--- a/src/cobalt/script/tracer.h
+++ b/src/cobalt/script/tracer.h
@@ -29,6 +29,8 @@
 
 class Traceable {
  public:
+  virtual ~Traceable() {}
+
   // Trace all native |Traceable|s accessible by the |Traceable|.  Any class
   // that owns a |Traceable| must implement this interface and trace each of
   // its |Traceable| members.  Note that here, "owns" means is accessible from
diff --git a/src/cobalt/script/typed_arrays.h b/src/cobalt/script/typed_arrays.h
new file mode 100644
index 0000000..e77b985
--- /dev/null
+++ b/src/cobalt/script/typed_arrays.h
@@ -0,0 +1,74 @@
+// Copyright 2018 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_TYPED_ARRAYS_H_
+#define COBALT_SCRIPT_TYPED_ARRAYS_H_
+
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/script_value.h"
+
+namespace cobalt {
+namespace script {
+
+class TypedArray : public ArrayBufferView {
+ public:
+  // http://www.ecma-international.org/ecma-262/6.0/#sec-%typedarray%-length
+  virtual size_t Length() const = 0;
+};
+
+template <typename CType, bool IsClamped = false>
+class TypedArrayImpl : public TypedArray {
+ public:
+  // http://www.ecma-international.org/ecma-262/6.0/#sec-%typedarray%-buffer-byteoffset-length
+  static Handle<TypedArrayImpl> New(GlobalEnvironment* global_environment,
+                                    Handle<ArrayBuffer> array_buffer,
+                                    size_t byte_offset, size_t length);
+
+  // http://www.ecma-international.org/ecma-262/6.0/#sec-%typedarray%-length
+  static Handle<TypedArrayImpl> New(GlobalEnvironment* global_environment,
+                                    size_t length) {
+    Handle<ArrayBuffer> array_buffer =
+        ArrayBuffer::New(global_environment, length * sizeof(CType));
+    return New(global_environment, array_buffer, 0, length);
+  }
+
+  virtual CType* Data() const = 0;
+};
+
+using Int8Array = TypedArrayImpl<int8_t>;
+using Uint8Array = TypedArrayImpl<uint8_t>;
+using Uint8ClampedArray = TypedArrayImpl<uint8_t, true>;
+using Int16Array = TypedArrayImpl<int16_t>;
+using Uint16Array = TypedArrayImpl<uint16_t>;
+using Int32Array = TypedArrayImpl<int32_t>;
+using Uint32Array = TypedArrayImpl<uint32_t>;
+using Float32Array = TypedArrayImpl<float>;
+using Float64Array = TypedArrayImpl<double>;
+
+#define COBALT_SCRIPT_TYPED_ARRAY_LIST(F) \
+  F(Int8Array, int8_t)                    \
+  F(Uint8Array, uint8_t)                  \
+  F(Uint8ClampedArray, uint8_t)           \
+  F(Int16Array, int16_t)                  \
+  F(Uint16Array, uint16_t)                \
+  F(Int32Array, int32_t)                  \
+  F(Uint32Array, uint32_t)                \
+  F(Float32Array, float)                  \
+  F(Float64Array, double)
+
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_TYPED_ARRAYS_H_
diff --git a/src/cobalt/script/v8c/v8c.gyp b/src/cobalt/script/v8c/v8c.gyp
index c063add..6493718 100644
--- a/src/cobalt/script/v8c/v8c.gyp
+++ b/src/cobalt/script/v8c/v8c.gyp
@@ -33,10 +33,15 @@
         'type_traits.h',
         'union_type_conversion_forward.h',
         'union_type_conversion_impl.h',
+        'v8c_array_buffer.cc',
+        'v8c_array_buffer.h',
+        'v8c_array_buffer_view.h',
         'v8c_callback_function.h',
         'v8c_callback_interface.cc',
         'v8c_callback_interface.h',
         'v8c_callback_interface_holder.h',
+        'v8c_data_view.cc',
+        'v8c_data_view.h',
         'v8c_engine.cc',
         'v8c_engine.h',
         'v8c_exception_state.cc',
@@ -50,9 +55,11 @@
         'v8c_script_value_factory.h',
         'v8c_source_code.cc',
         'v8c_source_code.h',
+        'v8c_typed_arrays.cc',
+        'v8c_typed_arrays.h',
         'v8c_user_object_holder.h',
-        'v8c_value_handle.h',
         'v8c_value_handle.cc',
+        'v8c_value_handle.h',
         'v8c_wrapper_handle.h',
         'wrapper_factory.cc',
         'wrapper_factory.h',
diff --git a/src/cobalt/script/v8c/v8c_array_buffer.cc b/src/cobalt/script/v8c/v8c_array_buffer.cc
new file mode 100644
index 0000000..9e00afc
--- /dev/null
+++ b/src/cobalt/script/v8c/v8c_array_buffer.cc
@@ -0,0 +1,66 @@
+// Copyright 2018 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/v8c/v8c_array_buffer.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+
+PreallocatedArrayBufferData::PreallocatedArrayBufferData(size_t byte_length) {
+  data_ = SbMemoryAllocate(byte_length);
+  byte_length_ = byte_length;
+}
+
+PreallocatedArrayBufferData::~PreallocatedArrayBufferData() {
+  if (data_) {
+    SbMemoryDeallocate(data_);
+    data_ = nullptr;
+    byte_length_ = 0;
+  }
+}
+
+// static
+Handle<ArrayBuffer> ArrayBuffer::New(GlobalEnvironment* global_environment,
+                                     size_t byte_length) {
+  auto* v8c_global_environment =
+      base::polymorphic_downcast<v8c::V8cGlobalEnvironment*>(
+          global_environment);
+  v8::Isolate* isolate = v8c_global_environment->isolate();
+  v8c::EntryScope entry_scope(isolate);
+  v8::Local<v8::ArrayBuffer> array_buffer =
+      v8::ArrayBuffer::New(isolate, byte_length);
+  return Handle<ArrayBuffer>(
+      new v8c::V8cUserObjectHolder<v8c::V8cArrayBuffer>(isolate, array_buffer));
+}
+
+// static
+Handle<ArrayBuffer> ArrayBuffer::New(GlobalEnvironment* global_environment,
+                                     PreallocatedArrayBufferData* data) {
+  auto* v8c_global_environment =
+      base::polymorphic_downcast<v8c::V8cGlobalEnvironment*>(
+          global_environment);
+  v8::Isolate* isolate = v8c_global_environment->isolate();
+  v8c::EntryScope entry_scope(isolate);
+  v8::Local<v8::ArrayBuffer> array_buffer =
+      v8::ArrayBuffer::New(isolate, data->data(), data->byte_length(),
+                           v8::ArrayBufferCreationMode::kInternalized);
+  data->Release();
+  return Handle<ArrayBuffer>(
+      new v8c::V8cUserObjectHolder<v8c::V8cArrayBuffer>(isolate, array_buffer));
+}
+
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c_array_buffer.h b/src/cobalt/script/v8c/v8c_array_buffer.h
new file mode 100644
index 0000000..a878ef7
--- /dev/null
+++ b/src/cobalt/script/v8c/v8c_array_buffer.h
@@ -0,0 +1,103 @@
+// Copyright 2018 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_V8C_V8C_ARRAY_BUFFER_H_
+#define COBALT_SCRIPT_V8C_V8C_ARRAY_BUFFER_H_
+
+#include "base/logging.h"
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/scoped_persistent.h"
+#include "cobalt/script/v8c/type_traits.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
+#include "cobalt/script/v8c/v8c_user_object_holder.h"
+#include "v8/include/v8.h"
+
+namespace cobalt {
+namespace script {
+namespace v8c {
+
+class V8cArrayBuffer final : public ScopedPersistent<v8::Value>,
+                             public ArrayBuffer {
+ public:
+  using BaseType = ArrayBuffer;
+
+  V8cArrayBuffer(v8::Isolate* isolate, v8::Local<v8::Value> value)
+      : isolate_(isolate), ScopedPersistent(isolate, value) {
+    DCHECK(value->IsArrayBuffer());
+  }
+
+  size_t ByteLength() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteLength();
+  }
+
+  void* Data() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->GetContents().Data();
+  }
+
+ private:
+  v8::Local<v8::ArrayBuffer> GetInternal() const {
+    return this->NewLocal(isolate_).As<v8::ArrayBuffer>();
+  }
+
+  v8::Isolate* isolate_;
+};
+
+template <>
+struct TypeTraits<ArrayBuffer> {
+  using ConversionType = V8cUserObjectHolder<V8cArrayBuffer>;
+  using ReturnType = const ScriptValue<ArrayBuffer>*;
+};
+
+inline void ToJSValue(v8::Isolate* isolate,
+                      const ScriptValue<ArrayBuffer>* array_buffer_value,
+                      v8::Local<v8::Value>* out_value) {
+  if (!array_buffer_value) {
+    *out_value = v8::Null(isolate);
+    return;
+  }
+
+  const auto* v8c_array_buffer_value =
+      base::polymorphic_downcast<const V8cUserObjectHolder<V8cArrayBuffer>*>(
+          array_buffer_value);
+  *out_value = v8c_array_buffer_value->v8_value();
+}
+
+inline void FromJSValue(v8::Isolate* isolate, v8::Local<v8::Value> value,
+                        int conversion_flags, ExceptionState* exception_state,
+                        V8cUserObjectHolder<V8cArrayBuffer>* out_array_buffer) {
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_array_buffer);
+
+  if (!value->IsObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!value->IsArrayBuffer()) {
+    exception_state->SetSimpleException(kTypeError,
+                                        "Expected object of type ArrayBuffer");
+    return;
+  }
+
+  *out_array_buffer = V8cUserObjectHolder<V8cArrayBuffer>(isolate, value);
+}
+
+}  // namespace v8c
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_V8C_V8C_ARRAY_BUFFER_H_
diff --git a/src/cobalt/script/v8c/v8c_array_buffer_view.h b/src/cobalt/script/v8c/v8c_array_buffer_view.h
new file mode 100644
index 0000000..e92ad7e
--- /dev/null
+++ b/src/cobalt/script/v8c/v8c_array_buffer_view.h
@@ -0,0 +1,121 @@
+// Copyright 2018 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_V8C_V8C_ARRAY_BUFFER_VIEW_H_
+#define COBALT_SCRIPT_V8C_V8C_ARRAY_BUFFER_VIEW_H_
+
+#include "base/logging.h"
+#include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/type_traits.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
+#include "cobalt/script/v8c/v8c_user_object_holder.h"
+#include "cobalt/script/v8c/weak_heap_object.h"
+
+namespace cobalt {
+namespace script {
+namespace v8c {
+
+class V8cArrayBufferView final : public ScopedPersistent<v8::Value>,
+                                 public ArrayBufferView {
+ public:
+  using BaseType = ArrayBufferView;
+
+  V8cArrayBufferView(v8::Isolate* isolate, JS::HandleValue value)
+      : isolate_(isolate), weak_heap_object_(isolate, value) {
+    DCHECK(value->IsArrayBufferView());
+  }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    return Handle<ArrayBuffer>(new V8cUserObjectHolder<V8cArrayBuffer>(
+        isolate_, GetInternal()->Buffer()));
+  }
+
+  size_t ByteOffset() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteOffset();
+  }
+
+  size_t ByteLength() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteLength();
+  }
+
+  void* RawData() const override {
+    EntryScope entry_scope(isolate_);
+    auto self = GetInternal();
+    void* data = self->Buffer()->GetContents().Data();
+    uint8_t* incremented_data =
+        static_cast<uint8_t*>(data) + self->ByteOffset();
+    return static_cast<void*>(incremented_data);
+  }
+
+ private:
+  v8::Local<v8::ArrayBufferView> GetInternal() const {
+    return this->NewLocal(isolate_).As<v8::ArrayBufferView>();
+  }
+
+  v8::Isolate* isolate_;
+};
+
+template <>
+struct TypeTraits<ArrayBufferView> {
+  using ConversionType = V8cUserObjectHolder<V8cArrayBufferView>;
+  using ReturnType = const ScriptValue<ArrayBufferView>*;
+};
+
+inline void ToJSValue(
+    v8::Isolate* isolate,
+    const ScriptValue<ArrayBufferView>* array_buffer_view_value,
+    v8::Local<v8::Value>* out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+
+  if (!array_buffer_view_value) {
+    *out_value = v8::Null(isolate);
+    return;
+  }
+
+  const auto* v8c_array_buffer_view_value = base::polymorphic_downcast<
+      const V8cUserObjectHolder<V8cArrayBufferView>*>(array_buffer_view_value);
+  *out_value = v8c_array_buffer_view_value->v8_value();
+}
+
+inline void FromJSValue(
+    v8::Isolate* isolate, v8::Local<v8::Value> value, int conversion_flags,
+    ExceptionState* exception_state,
+    V8cUserObjectHolder<V8cArrayBufferView>* out_array_buffer_view) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_array_buffer_view);
+
+  if (!value->IsObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!value->IsArrayBufferView()) {
+    exception_state->SetSimpleException(
+        kTypeError, "Expected object of type ArrayBufferView");
+    return;
+  }
+
+  *out_array_buffer_view =
+      V8cUserObjectHolder<V8cArrayBufferView>(isolate, value);
+}
+
+}  // namespace v8c
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_V8C_V8C_ARRAY_BUFFER_VIEW_H_
diff --git a/src/cobalt/script/v8c/v8c_data_view.cc b/src/cobalt/script/v8c/v8c_data_view.cc
new file mode 100644
index 0000000..d735ae8
--- /dev/null
+++ b/src/cobalt/script/v8c/v8c_data_view.cc
@@ -0,0 +1,47 @@
+// Copyright 2018 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/v8c/v8c_data_view.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+
+// static
+Handle<DataView> DataView::New(GlobalEnvironment* global_environment,
+                               Handle<ArrayBuffer> array_buffer,
+                               size_t byte_offset, size_t byte_length) {
+  auto* v8c_global_environment =
+      base::polymorphic_downcast<v8c::V8cGlobalEnvironment*>(
+          global_environment);
+  v8::Isolate* isolate = v8c_global_environment->isolate();
+
+  v8c::EntryScope entry_scope(isolate);
+
+  const auto* v8c_array_buffer = base::polymorphic_downcast<
+      const v8c::V8cUserObjectHolder<v8c::V8cArrayBuffer>*>(
+      array_buffer.GetScriptValue());
+
+  v8::Local<v8::ArrayBuffer> array_buffer_value =
+      v8c_array_buffer->v8_value().As<v8::ArrayBuffer>();
+  v8::Local<v8::DataView> data_view =
+      v8::DataView::New(array_buffer_value, byte_offset, byte_length);
+
+  return Handle<DataView>(
+      new v8c::V8cUserObjectHolder<v8c::V8cDataView>(isolate, data_view));
+}
+
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c_data_view.h b/src/cobalt/script/v8c/v8c_data_view.h
new file mode 100644
index 0000000..896b83ed
--- /dev/null
+++ b/src/cobalt/script/v8c/v8c_data_view.h
@@ -0,0 +1,118 @@
+// Copyright 2018 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_V8C_V8C_DATA_VIEW_H_
+#define COBALT_SCRIPT_V8C_V8C_DATA_VIEW_H_
+
+#include "base/logging.h"
+#include "cobalt/script/data_view.h"
+#include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/scoped_persistent.h"
+#include "cobalt/script/v8c/type_traits.h"
+#include "cobalt/script/v8c/v8c_array_buffer.h"
+#include "cobalt/script/v8c/v8c_user_object_holder.h"
+#include "v8/include/v8.h"
+
+namespace cobalt {
+namespace script {
+namespace v8c {
+
+class V8cDataView final : public ScopedPersistent<v8::Value>, public DataView {
+ public:
+  using BaseType = DataView;
+
+  V8cDataView(v8::Isolate* isolate, v8::Local<v8::Value> value)
+      : isolate_(isolate), ScopedPersistent(isolate, value) {
+    DCHECK(value->IsDataView());
+  }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    EntryScope entry_scope(isolate_);
+    return Handle<ArrayBuffer>(new V8cUserObjectHolder<V8cArrayBuffer>(
+        isolate_, GetInternal()->Buffer()));
+  }
+
+  size_t ByteOffset() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteOffset();
+  }
+
+  size_t ByteLength() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteLength();
+  }
+
+  void* RawData() const override {
+    EntryScope entry_scope(isolate_);
+    auto self = GetInternal();
+    void* data = self->Buffer()->GetContents().Data();
+    uint8_t* incremented_data =
+        static_cast<uint8_t*>(data) + self->ByteOffset();
+    return static_cast<void*>(incremented_data);
+  }
+
+ private:
+  v8::Local<v8::DataView> GetInternal() const {
+    return this->NewLocal(isolate_).As<v8::DataView>();
+  }
+
+  v8::Isolate* isolate_;
+};
+
+template <>
+struct TypeTraits<DataView> {
+  using ConversionType = V8cUserObjectHolder<V8cDataView>;
+  using ReturnType = const ScriptValue<DataView>*;
+};
+
+inline void ToJSValue(v8::Isolate* isolate,
+                      const ScriptValue<DataView>* array_buffer_view_value,
+                      v8::Local<v8::Value>* out_value) {
+  if (!array_buffer_view_value) {
+    *out_value = v8::Null(isolate);
+    return;
+  }
+
+  const auto* v8c_array_buffer_view_value =
+      base::polymorphic_downcast<const V8cUserObjectHolder<V8cDataView>*>(
+          array_buffer_view_value);
+  *out_value = v8c_array_buffer_view_value->v8_value();
+}
+
+inline void FromJSValue(
+    v8::Isolate* isolate, v8::Local<v8::Value> value, int conversion_flags,
+    ExceptionState* exception_state,
+    V8cUserObjectHolder<V8cDataView>* out_array_buffer_view) {
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_array_buffer_view);
+
+  if (!value->IsObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!value->IsDataView()) {
+    exception_state->SetSimpleException(kTypeError,
+                                        "Expected object of type DataView");
+    return;
+  }
+
+  *out_array_buffer_view = V8cUserObjectHolder<V8cDataView>(isolate, value);
+}
+
+}  // namespace v8c
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_V8C_V8C_DATA_VIEW_H_
diff --git a/src/cobalt/script/v8c/v8c_engine.cc b/src/cobalt/script/v8c/v8c_engine.cc
index a8c956a..d1c7046 100644
--- a/src/cobalt/script/v8c/v8c_engine.cc
+++ b/src/cobalt/script/v8c/v8c_engine.cc
@@ -171,8 +171,6 @@
   return true;
 }
 
-void V8cEngine::SetGcThreshold(int64_t bytes) { NOTIMPLEMENTED(); }
-
 HeapStatistics V8cEngine::GetHeapStatistics() {
   v8::HeapStatistics v8_heap_statistics;
   isolate_->GetHeapStatistics(&v8_heap_statistics);
@@ -189,5 +187,9 @@
   return make_scoped_ptr<JavaScriptEngine>(new v8c::V8cEngine(options));
 }
 
+std::string GetJavaScriptEngineNameAndVersion() {
+  return std::string("V8/") + v8::V8::GetVersion();
+}
+
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c_engine.h b/src/cobalt/script/v8c/v8c_engine.h
index 1acfae9..3384f5b 100644
--- a/src/cobalt/script/v8c/v8c_engine.h
+++ b/src/cobalt/script/v8c/v8c_engine.h
@@ -43,7 +43,6 @@
   void CollectGarbage() override;
   void AdjustAmountOfExternalAllocatedMemory(int64_t bytes) override;
   bool RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) override;
-  void SetGcThreshold(int64_t bytes) override;
   HeapStatistics GetHeapStatistics() override;
 
   v8::Isolate* isolate() const { return isolate_; }
diff --git a/src/cobalt/script/v8c/v8c_global_environment.cc b/src/cobalt/script/v8c/v8c_global_environment.cc
index 7f4ab04..c8d4f49 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.cc
+++ b/src/cobalt/script/v8c/v8c_global_environment.cc
@@ -55,6 +55,13 @@
   return string;
 }
 
+std::string ToStringOrNull(v8::Local<v8::Value> value) {
+  if (value.IsEmpty() || !value->IsString()) {
+    return "";
+  }
+  return *v8::String::Utf8Value(value.As<v8::String>());
+}
+
 }  // namespace
 
 V8cGlobalEnvironment::V8cGlobalEnvironment(v8::Isolate* isolate)
@@ -71,6 +78,17 @@
 
   isolate_->SetAllowCodeGenerationFromStringsCallback(
       AllowCodeGenerationFromStringsCallback);
+
+  isolate_->SetAllowWasmCodeGenerationCallback(
+      [](v8::Local<v8::Context> context, v8::Local<v8::String> source) {
+        return false;
+      });
+
+  isolate_->AddMessageListenerWithErrorLevel(
+      MessageHandler,
+      v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
+          v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
+          v8::Isolate::kMessageLog);
 }
 
 V8cGlobalEnvironment::~V8cGlobalEnvironment() {
@@ -95,7 +113,7 @@
 }
 
 bool V8cGlobalEnvironment::EvaluateScript(
-    const scoped_refptr<SourceCode>& source_code, bool mute_errors,
+    const scoped_refptr<SourceCode>& source_code,
     std::string* out_result_utf8) {
   TRACK_MEMORY_SCOPE("Javascript");
   TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateScript()");
@@ -106,11 +124,15 @@
   v8::TryCatch try_catch(isolate_);
 
   v8::Local<v8::Value> result;
-  if (!EvaluateScriptInternal(source_code, mute_errors).ToLocal(&result)) {
+  if (!EvaluateScriptInternal(source_code).ToLocal(&result)) {
     if (!try_catch.HasCaught()) {
       LOG(WARNING) << "Script evaluation failed with no JavaScript exception.";
       return false;
     }
+    // The MessageHandler appears to never get called under a |v8::TryCatch|
+    // block, even if we re-throw it.  We work around this by manually passing
+    // it to the MessageHandler.
+    MessageHandler(try_catch.Message(), try_catch.Exception());
     if (out_result_utf8) {
       *out_result_utf8 = ExceptionToString(try_catch);
     }
@@ -118,7 +140,9 @@
   }
 
   if (out_result_utf8) {
-    *out_result_utf8 = *v8::String::Utf8Value(isolate_, result);
+    V8cExceptionState exception_state(isolate_);
+    FromJSValue(isolate_, result, kNoConversionFlags, &exception_state,
+                out_result_utf8);
   }
 
   return true;
@@ -126,7 +150,7 @@
 
 bool V8cGlobalEnvironment::EvaluateScript(
     const scoped_refptr<SourceCode>& source_code,
-    const scoped_refptr<Wrappable>& owning_object, bool mute_errors,
+    const scoped_refptr<Wrappable>& owning_object,
     base::optional<ValueHandleHolder::Reference>* out_value_handle) {
   TRACK_MEMORY_SCOPE("Javascript");
   TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateScript()");
@@ -137,10 +161,14 @@
   v8::TryCatch try_catch(isolate_);
 
   v8::Local<v8::Value> result;
-  if (!EvaluateScriptInternal(source_code, mute_errors).ToLocal(&result)) {
+  if (!EvaluateScriptInternal(source_code).ToLocal(&result)) {
     if (!try_catch.HasCaught()) {
       LOG(WARNING) << "Script evaluation failed with no JavaScript exception.";
     }
+    // The MessageHandler appears to never get called under a |v8::TryCatch|
+    // block, even if we re-throw it.  We work around this by manually passing
+    // it to the MessageHandler.
+    MessageHandler(try_catch.Message(), try_catch.Exception());
     return false;
   }
 
@@ -178,6 +206,14 @@
   return result;
 }
 
+void V8cGlobalEnvironment::AddRoot(Traceable* traceable) {
+  V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->AddRoot(traceable);
+}
+
+void V8cGlobalEnvironment::RemoveRoot(Traceable* traceable) {
+  V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->RemoveRoot(traceable);
+}
+
 void V8cGlobalEnvironment::PreventGarbageCollection(
     const scoped_refptr<Wrappable>& wrappable) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -307,13 +343,48 @@
     global_environment->report_eval_.Run();
   }
   // This callback should only be called while code generation from strings is
-  // not allowed from within V8, so this should always be false.
+  // not allowed from within V8, so this should always be false.  Note that
+  // WebAssembly code generation will fall back to this callback if a
+  // WebAssembly callback has not been explicitly set, however we *have* set
+  // one.
   DCHECK_EQ(context->IsCodeGenerationFromStringsAllowed(), false);
   return context->IsCodeGenerationFromStringsAllowed();
 }
 
+// static
+void V8cGlobalEnvironment::MessageHandler(v8::Local<v8::Message> message,
+                                          v8::Local<v8::Value> data) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  V8cGlobalEnvironment* global_environment =
+      V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (isolate->GetEnteredContext().IsEmpty()) {
+    return;
+  }
+  if (message->ErrorLevel() != v8::Isolate::kMessageError) {
+    return;
+  }
+
+  v8::Local<v8::Context> context = isolate->GetEnteredContext();
+  ErrorReport error_report;
+  error_report.message = *v8::String::Utf8Value(message->Get());
+  error_report.filename = ToStringOrNull(message->GetScriptResourceName());
+  int line_number = 0;
+  int column_number = 0;
+  if (message->GetLineNumber(context).To(&line_number) &&
+      message->GetStartColumn(context).To(&column_number)) {
+    column_number++;
+  }
+  error_report.line_number = line_number;
+  error_report.column_number = column_number;
+  error_report.is_muted = message->IsSharedCrossOrigin();
+  error_report.error.reset(new V8cValueHandleHolder(isolate, data));
+  if (!global_environment->report_error_callback_.is_null()) {
+    global_environment->report_error_callback_.Run(error_report);
+  }
+}
+
 v8::MaybeLocal<v8::Value> V8cGlobalEnvironment::EvaluateScriptInternal(
-    const scoped_refptr<SourceCode>& source_code, bool mute_errors) {
+    const scoped_refptr<SourceCode>& source_code) {
   TRACK_MEMORY_SCOPE("Javascript");
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -343,7 +414,7 @@
       /*resource_column_offset=*/
       v8::Integer::New(isolate_, source_location.column_number - 1),
       /*resource_is_shared_cross_origin=*/
-      v8::Boolean::New(isolate_, !mute_errors));
+      v8::Boolean::New(isolate_, !v8c_source_code->is_muted()));
 
   v8::Local<v8::String> source;
   if (!v8::String::NewFromUtf8(isolate_, v8c_source_code->source_utf8().c_str(),
@@ -386,7 +457,7 @@
   scoped_refptr<SourceCode> source_code =
       new V8cSourceCode(source, base::SourceLocation(filename, 1, 1));
   std::string result;
-  bool success = EvaluateScript(source_code, false /*mute_errors*/, &result);
+  bool success = EvaluateScript(source_code, &result);
   if (!success) {
     DLOG(FATAL) << result;
   }
diff --git a/src/cobalt/script/v8c/v8c_global_environment.h b/src/cobalt/script/v8c/v8c_global_environment.h
index 251599d..05d0094 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.h
+++ b/src/cobalt/script/v8c/v8c_global_environment.h
@@ -60,12 +60,12 @@
       const scoped_refptr<GlobalInterface>& global_interface,
       EnvironmentSettings* environment_settings);
 
-  bool EvaluateScript(const scoped_refptr<SourceCode>& script, bool mute_errors,
+  bool EvaluateScript(const scoped_refptr<SourceCode>& script,
                       std::string* out_result_utf8) override;
 
   bool EvaluateScript(
       const scoped_refptr<SourceCode>& script_utf8,
-      const scoped_refptr<Wrappable>& owning_object, bool mute_errors,
+      const scoped_refptr<Wrappable>& owning_object,
       base::optional<ValueHandleHolder::Reference>* out_value_handle) override;
 
   std::vector<StackFrame> GetStackTrace(int max_frames) override;
@@ -77,6 +77,10 @@
   void AllowGarbageCollection(
       const scoped_refptr<Wrappable>& wrappable) override;
 
+  void AddRoot(Traceable* traceable) override;
+
+  void RemoveRoot(Traceable* traceable) override;
+
   void DisableEval(const std::string& message) override;
 
   void EnableEval() override;
@@ -135,8 +139,11 @@
   static bool AllowCodeGenerationFromStringsCallback(
       v8::Local<v8::Context> context, v8::Local<v8::String> source);
 
+  static void MessageHandler(v8::Local<v8::Message> message,
+                             v8::Local<v8::Value> data);
+
   v8::MaybeLocal<v8::Value> EvaluateScriptInternal(
-      const scoped_refptr<SourceCode>& source_code, bool mute_errors);
+      const scoped_refptr<SourceCode>& source_code);
 
   void EvaluateEmbeddedScript(const unsigned char* data, size_t size,
                               const char* filename);
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.cc b/src/cobalt/script/v8c/v8c_heap_tracer.cc
index 927603d..e48836a 100644
--- a/src/cobalt/script/v8c/v8c_heap_tracer.cc
+++ b/src/cobalt/script/v8c/v8c_heap_tracer.cc
@@ -49,6 +49,10 @@
   // manually decide to trace the from the global object.
   MaybeAddToFrontier(
       V8cGlobalEnvironment::GetFromIsolate(isolate_)->global_wrappable());
+
+  for (Traceable* traceable : roots_) {
+    MaybeAddToFrontier(traceable);
+  }
 }
 
 bool V8cHeapTracer::AdvanceTracing(double deadline_in_ms,
@@ -64,6 +68,7 @@
 
     Traceable* traceable = frontier_.back();
     frontier_.pop_back();
+    DCHECK(traceable);
 
     if (traceable->IsWrappable()) {
       Wrappable* wrappable = base::polymorphic_downcast<Wrappable*>(traceable);
@@ -109,19 +114,20 @@
 size_t V8cHeapTracer::NumberOfWrappersToTrace() { return frontier_.size(); }
 
 void V8cHeapTracer::Trace(Traceable* traceable) {
-  if (!traceable) {
-    return;
-  }
   MaybeAddToFrontier(traceable);
 }
 
 void V8cHeapTracer::AddReferencedObject(Wrappable* owner,
                                         ScopedPersistent<v8::Value>* value) {
+  DCHECK(owner);
+  DCHECK(value);
   auto it = reference_map_.insert({owner, value});
 }
 
 void V8cHeapTracer::RemoveReferencedObject(Wrappable* owner,
                                            ScopedPersistent<v8::Value>* value) {
+  DCHECK(owner);
+  DCHECK(value);
   auto pair_range = reference_map_.equal_range(owner);
   auto it = std::find_if(
       pair_range.first, pair_range.second,
@@ -130,7 +136,22 @@
   reference_map_.erase(it);
 }
 
+void V8cHeapTracer::AddRoot(Traceable* traceable) {
+  DCHECK(traceable);
+  roots_.insert(traceable);
+}
+
+void V8cHeapTracer::RemoveRoot(Traceable* traceable) {
+  DCHECK(traceable);
+  auto it = roots_.find(traceable);
+  DCHECK(it != roots_.end());
+  roots_.erase(it);
+}
+
 void V8cHeapTracer::MaybeAddToFrontier(Traceable* traceable) {
+  if (!traceable) {
+    return;
+  }
   if (!visited_.insert(traceable).second) {
     return;
   }
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.h b/src/cobalt/script/v8c/v8c_heap_tracer.h
index 32c799c..4cb90da 100644
--- a/src/cobalt/script/v8c/v8c_heap_tracer.h
+++ b/src/cobalt/script/v8c/v8c_heap_tracer.h
@@ -52,6 +52,9 @@
   void RemoveReferencedObject(Wrappable* owner,
                               ScopedPersistent<v8::Value>* value);
 
+  void AddRoot(Traceable* traceable);
+  void RemoveRoot(Traceable* traceable);
+
  private:
   void MaybeAddToFrontier(Traceable* traceable);
 
@@ -62,6 +65,10 @@
   std::unordered_set<Traceable*> visited_;
   std::unordered_multimap<Wrappable*, ScopedPersistent<v8::Value>*>
       reference_map_;
+
+  // TODO: A "counted" multiset approach here would be a bit nicer than
+  // std::multiset.
+  std::unordered_multiset<Traceable*> roots_;
 };
 
 }  // namespace v8c
diff --git a/src/cobalt/script/v8c/v8c_source_code.cc b/src/cobalt/script/v8c/v8c_source_code.cc
index 053da7f..4886444 100644
--- a/src/cobalt/script/v8c/v8c_source_code.cc
+++ b/src/cobalt/script/v8c/v8c_source_code.cc
@@ -17,10 +17,11 @@
 namespace cobalt {
 namespace script {
 
+// static
 scoped_refptr<SourceCode> SourceCode::CreateSourceCode(
-    const std::string& script_utf8,
-    const base::SourceLocation& script_location) {
-  return new v8c::V8cSourceCode(script_utf8, script_location);
+    const std::string& script_utf8, const base::SourceLocation& script_location,
+    bool is_muted) {
+  return new v8c::V8cSourceCode(script_utf8, script_location, is_muted);
 }
 
 }  // namespace script
diff --git a/src/cobalt/script/v8c/v8c_source_code.h b/src/cobalt/script/v8c/v8c_source_code.h
index f014afa..19bc747 100644
--- a/src/cobalt/script/v8c/v8c_source_code.h
+++ b/src/cobalt/script/v8c/v8c_source_code.h
@@ -30,14 +30,20 @@
 class V8cSourceCode : public SourceCode {
  public:
   V8cSourceCode(const std::string& source_utf8,
-                const base::SourceLocation& source_location)
-      : source_utf8_(source_utf8), location_(source_location) {}
+                const base::SourceLocation& source_location,
+                bool is_muted = false)
+      : source_utf8_(source_utf8),
+        location_(source_location),
+        is_muted_(is_muted) {}
+
   const std::string& source_utf8() const { return source_utf8_; }
   const base::SourceLocation& location() const { return location_; }
+  bool is_muted() const { return is_muted_; }
 
  private:
   std::string source_utf8_;
   base::SourceLocation location_;
+  bool is_muted_;
 };
 
 }  // namespace v8c
diff --git a/src/cobalt/script/v8c/v8c_typed_arrays.cc b/src/cobalt/script/v8c/v8c_typed_arrays.cc
new file mode 100644
index 0000000..0a8ef5d
--- /dev/null
+++ b/src/cobalt/script/v8c/v8c_typed_arrays.cc
@@ -0,0 +1,73 @@
+// Copyright 2018 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/v8c/v8c_typed_arrays.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+
+namespace {
+
+template <typename T>
+struct InterfaceToV8c;
+
+#define COBALT_SCRIPT_DEFINE_INTERFACE_TO_V8C(array, ctype) \
+  template <>                                               \
+  struct InterfaceToV8c<array> {                            \
+    using Result = v8c::V8c##array;                         \
+    using V8Type = v8::array;                               \
+  };
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_DEFINE_INTERFACE_TO_V8C)
+#undef COBALT_SCRIPT_DEFINE_INTERFACE_TO_V8C
+
+}  // namespace
+
+// static
+template <typename CType, bool IsClamped>
+Handle<TypedArrayImpl<CType, IsClamped>> TypedArrayImpl<CType, IsClamped>::New(
+    GlobalEnvironment* global_environment, Handle<ArrayBuffer> array_buffer,
+    size_t byte_offset, size_t length) {
+  using TypedArrayImplType = TypedArrayImpl<CType, IsClamped>;
+  using V8cTypedArrayImplType =
+      typename InterfaceToV8c<TypedArrayImplType>::Result;
+  using V8Type = typename InterfaceToV8c<TypedArrayImplType>::V8Type;
+
+  auto* v8c_global_environment =
+      base::polymorphic_downcast<v8c::V8cGlobalEnvironment*>(
+          global_environment);
+  v8::Isolate* isolate = v8c_global_environment->isolate();
+  v8c::EntryScope entry_scope(isolate);
+  const auto* v8c_array_buffer = base::polymorphic_downcast<
+      const v8c::V8cUserObjectHolder<v8c::V8cArrayBuffer>*>(
+      array_buffer.GetScriptValue());
+  v8::Local<v8::ArrayBuffer> array_buffer_value =
+      v8c_array_buffer->v8_value().As<v8::ArrayBuffer>();
+  v8::Local<V8Type> typed_array =
+      V8Type::New(array_buffer_value, byte_offset, length);
+  return Handle<TypedArrayImplType>(
+      new v8c::V8cUserObjectHolder<V8cTypedArrayImplType>(isolate,
+                                                          typed_array));
+}
+
+#define COBALT_SCRIPT_INSTANTIATE(array, ctype)                            \
+  template Handle<array> array::New(GlobalEnvironment* global_environment, \
+                                    Handle<ArrayBuffer> array_buffer,      \
+                                    size_t byte_offset, size_t length);
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_INSTANTIATE)
+#undef COBALT_SCRIPT_INSTANTIATE
+
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c_typed_arrays.h b/src/cobalt/script/v8c/v8c_typed_arrays.h
new file mode 100644
index 0000000..b0d3496
--- /dev/null
+++ b/src/cobalt/script/v8c/v8c_typed_arrays.h
@@ -0,0 +1,221 @@
+// Copyright 2018 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_V8C_V8C_TYPED_ARRAYS_H_
+#define COBALT_SCRIPT_V8C_V8C_TYPED_ARRAYS_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/exception_message.h"
+#include "cobalt/script/script_exception.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/typed_arrays.h"
+#include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/v8c_array_buffer.h"
+
+namespace cobalt {
+namespace script {
+namespace v8c {
+
+class V8cTypedArray final : public ScopedPersistent<v8::Value>,
+                            public TypedArray {
+ public:
+  using BaseType = TypedArray;
+
+  V8cTypedArray(v8::Isolate* isolate, v8::Local<v8::Value> value)
+      : isolate_(isolate), ScopedPersistent(isolate, value) {
+    DCHECK(value->IsTypedArray());
+  }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    EntryScope entry_scope(isolate_);
+    v8::Local<v8::ArrayBuffer> array_buffer = GetInternal()->Buffer();
+    return Handle<ArrayBuffer>(
+        new V8cUserObjectHolder<V8cArrayBuffer>(isolate_, array_buffer));
+  }
+
+  size_t Length() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->Length();
+  }
+  size_t ByteOffset() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteOffset();
+  }
+  size_t ByteLength() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteLength();
+  }
+
+  void* RawData() const override {
+    EntryScope entry_scope(isolate_);
+    auto self = GetInternal();
+    void* data = self->Buffer()->GetContents().Data();
+    uint8_t* incremented_data =
+        static_cast<uint8_t*>(data) + self->ByteOffset();
+    return static_cast<void*>(incremented_data);
+  }
+
+ private:
+  v8::Local<v8::TypedArray> GetInternal() const {
+    return this->NewLocal(isolate_).As<v8::TypedArray>();
+  }
+
+  v8::Isolate* isolate_;
+};
+
+template <>
+struct TypeTraits<TypedArray> {
+  using ConversionType = V8cUserObjectHolder<V8cTypedArray>;
+  using ReturnType = const ScriptValue<TypedArray>*;
+};
+
+inline void ToJSValue(v8::Isolate* isolate,
+                      const ScriptValue<TypedArray>* typed_array_value,
+                      v8::Local<v8::Value>* out_value) {
+  if (!typed_array_value) {
+    *out_value = v8::Null(isolate);
+    return;
+  }
+
+  const auto* v8c_typed_array_value =
+      base::polymorphic_downcast<const V8cUserObjectHolder<V8cTypedArray>*>(
+          typed_array_value);
+  *out_value = v8c_typed_array_value->v8_value();
+}
+
+inline void FromJSValue(v8::Isolate* isolate, v8::Local<v8::Value> value,
+                        int conversion_flags, ExceptionState* exception_state,
+                        V8cUserObjectHolder<V8cTypedArray>* out_typed_array) {
+  DCHECK_EQ(0, conversion_flags);
+  DCHECK(out_typed_array);
+
+  if (!value->IsObject()) {
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+
+  if (!value->IsTypedArray()) {
+    exception_state->SetSimpleException(kTypeError,
+                                        "Expected object of type TypedArray");
+    return;
+  }
+
+  *out_typed_array =
+      V8cUserObjectHolder<V8cTypedArray>(isolate, value.As<v8::TypedArray>());
+}
+
+template <typename CType, typename BaseTypeName, typename V8Type,
+          bool (v8::Value::*V8ValueIsTypeFunction)() const>
+class V8cTypedArrayImpl final : public ScopedPersistent<v8::Value>,
+                                public BaseTypeName {
+ public:
+  using BaseType = BaseTypeName;
+
+  V8cTypedArrayImpl(v8::Isolate* isolate, v8::Local<v8::Value> value)
+      : isolate_(isolate), ScopedPersistent(isolate, value) {
+    DCHECK(((**value).*(V8ValueIsTypeFunction))());
+  }
+
+  Handle<ArrayBuffer> Buffer() const override {
+    EntryScope entry_scope(isolate_);
+    v8::Local<v8::ArrayBuffer> array_buffer = GetInternal()->Buffer();
+    return Handle<ArrayBuffer>(
+        new V8cUserObjectHolder<V8cArrayBuffer>(isolate_, array_buffer));
+  }
+
+  size_t Length() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->Length();
+  }
+  size_t ByteOffset() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteOffset();
+  }
+  size_t ByteLength() const override {
+    EntryScope entry_scope(isolate_);
+    return GetInternal()->ByteLength();
+  }
+
+  void* RawData() const override {
+    EntryScope entry_scope(isolate_);
+    auto self = GetInternal();
+    void* data = self->Buffer()->GetContents().Data();
+    uint8_t* incremented_data =
+        static_cast<uint8_t*>(data) + self->ByteOffset();
+    return static_cast<void*>(incremented_data);
+  }
+
+  CType* Data() const override { return static_cast<CType*>(RawData()); }
+
+ private:
+  v8::Local<V8Type> GetInternal() const {
+    return this->NewLocal(isolate_).template As<V8Type>();
+  }
+
+  v8::Isolate* isolate_;
+};
+
+#define COBALT_SCRIPT_USING_V8C_ARRAY(array, ctype) \
+  using V8c##array =                                \
+      V8cTypedArrayImpl<ctype, array, v8::array, &v8::Value::Is##array>;
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_USING_V8C_ARRAY)
+#undef COBALT_SCRIPT_USING_V8C_ARRAY
+
+#define COBALT_SCRIPT_CONVERSION_BOILERPLATE(array, ctype)                  \
+  template <>                                                               \
+  struct TypeTraits<array> {                                                \
+    using ConversionType = V8cUserObjectHolder<V8c##array>;                 \
+    using ReturnType = const ScriptValue<array>*;                           \
+  };                                                                        \
+                                                                            \
+  inline void ToJSValue(v8::Isolate* isolate,                               \
+                        const ScriptValue<array>* array_value,              \
+                        v8::Local<v8::Value>* out_value) {                  \
+    if (!array_value) {                                                     \
+      *out_value = v8::Null(isolate);                                       \
+      return;                                                               \
+    }                                                                       \
+    const auto* v8c_array_value =                                           \
+        base::polymorphic_downcast<const V8cUserObjectHolder<V8c##array>*>( \
+            array_value);                                                   \
+    *out_value = v8c_array_value->v8_value();                               \
+  }                                                                         \
+                                                                            \
+  inline void FromJSValue(v8::Isolate* isolate, v8::Local<v8::Value> value, \
+                          int conversion_flags,                             \
+                          ExceptionState* exception_state,                  \
+                          V8cUserObjectHolder<V8c##array>* out_array) {     \
+    DCHECK_EQ(0, conversion_flags);                                         \
+    DCHECK(out_array);                                                      \
+    if (!value->IsObject()) {                                               \
+      exception_state->SetSimpleException(kNotObjectType);                  \
+      return;                                                               \
+    }                                                                       \
+    if (!value->Is##array()) {                                              \
+      exception_state->SetSimpleException(kTypeError,                       \
+                                          "Expected object of type array"); \
+      return;                                                               \
+    }                                                                       \
+    *out_array = V8cUserObjectHolder<V8c##array>(isolate, value);           \
+  }
+COBALT_SCRIPT_TYPED_ARRAY_LIST(COBALT_SCRIPT_CONVERSION_BOILERPLATE)
+#undef COBALT_SCRIPT_CONVERSION_BOILERPLATE
+
+}  // namespace v8c
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_V8C_V8C_TYPED_ARRAYS_H_
diff --git a/src/cobalt/script/wrappable.h b/src/cobalt/script/wrappable.h
index e2acbf6..470e5ce 100644
--- a/src/cobalt/script/wrappable.h
+++ b/src/cobalt/script/wrappable.h
@@ -61,13 +61,6 @@
 
   virtual base::SourceLocation GetInlineSourceLocation() const = 0;
 
-  // If this function returns true, the JavaScript engine will keep the wrapper
-  // for this Wrappable from being garbage collected even if there are no strong
-  // references to it. If this Wrappable is no longer referenced from anything
-  // other than the wrapper, the wrappable will be garbage collected despite
-  // this (which will result in the Wrappable being destructed as well.)
-  virtual bool ShouldKeepWrapperAlive() { return false; }
-
   // Our implementation of the |Traceable| interface.  All |Wrappable|s that
   // own any |Traceable|s must override |TraceMembers| and trace them.
   void TraceMembers(Tracer* /*tracer*/) override {}
diff --git a/src/cobalt/site/docs/reference/starboard/configuration-public.md b/src/cobalt/site/docs/reference/starboard/configuration-public.md
index 05a8332..73354ab 100644
--- a/src/cobalt/site/docs/reference/starboard/configuration-public.md
+++ b/src/cobalt/site/docs/reference/starboard/configuration-public.md
@@ -104,10 +104,12 @@
 | Properties |
 | :--- |
 | **`SB_HAS_QUIRK_SEEK_TO_KEYFRAME`**<br><br>After a seek is triggerred, the default behavior is to append video frames from the last key frame before the seek time and append audio frames from the seek time because usually all audio frames are key frames.  On platforms that cannot decode video frames without displaying them, this will cause the video being played without audio for several seconds after seeking.  When the following macro is defined, the app will append audio frames start from the timestamp that is before the timestamp of the video key frame being appended.<br><br>By default, this property is undefined. |
+| **`SB_HAS_QUIRK_SUPPORT_INT16_AUDIO_SAMPLES`**<br><br>The implementation is allowed to support `kSbMediaAudioSampleTypeInt16` only when this macro is defined. |
 | **`SB_HAS_QUIRK_NO_FFS`**<br><br>dlmalloc will use the ffs intrinsic if available.  Platforms on which this is not available should define the following quirk.<br><br>By default, this property is undefined. |
 | **`SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND`**<br><br>The maximum audio bitrate the platform can decode.  The following value equals to 5M bytes per seconds which is more than enough for compressed audio.<br><br>The default value in the Stub implementation is `(40 * 1024 * 1024)` |
 | **`SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND`**<br><br>The maximum video bitrate the platform can decode.  The following value equals to 25M bytes per seconds which is more than enough for compressed video.<br><br>The default value in the Stub implementation is `(200 * 1024 * 1024)` |
 | **`SB_HAS_MEDIA_WEBM_VP9_SUPPORT`**<br><br>Specifies whether this platform has webm/vp9 support.  This should be set to non-zero on platforms with webm/vp9 support.<br><br>The default value in the Stub implementation is `0` |
+| **`SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING`**<br><br>Specifies whether this platform updates audio frames asynchronously.  In such case an extra parameter will be added to `SbAudioSinkConsumeFramesFunc()` to indicate the absolute time that the consumed audio frames are reported.<br><br>The default value in the Stub implementation is `0` |
 | **`SB_MEDIA_THREAD_STACK_SIZE`**<br><br>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.<br><br>The default value in the Stub implementation is `0U` |
 
 
diff --git a/src/cobalt/speech/sandbox/sandbox.gyp b/src/cobalt/speech/sandbox/sandbox.gyp
index 82f36b4..f238410 100644
--- a/src/cobalt/speech/sandbox/sandbox.gyp
+++ b/src/cobalt/speech/sandbox/sandbox.gyp
@@ -48,7 +48,7 @@
       'variables': {
         'executable_name': 'speech_sandbox',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/speech/speech_recognition_event.cc b/src/cobalt/speech/speech_recognition_event.cc
index 23cfa4c..e5c3a88 100644
--- a/src/cobalt/speech/speech_recognition_event.cc
+++ b/src/cobalt/speech/speech_recognition_event.cc
@@ -26,10 +26,9 @@
       return base::Tokens::result();
     case SpeechRecognitionEvent::kNoMatch:
       return base::Tokens::nomatch();
-    default:
-      NOTREACHED() << "Invalid SpeechRecognitionEvent::Type";
-      return base::Tokens::nomatch();
   }
+  NOTREACHED() << "Invalid SpeechRecognitionEvent::Type";
+  return base::Tokens::nomatch();
 }
 }  // namespace
 
diff --git a/src/cobalt/storage/savegame_starboard.cc b/src/cobalt/storage/savegame_starboard.cc
index 6792cfd..a2fe362 100644
--- a/src/cobalt/storage/savegame_starboard.cc
+++ b/src/cobalt/storage/savegame_starboard.cc
@@ -60,13 +60,23 @@
       record->Read(reinterpret_cast<char*>(bytes.data()), size);
   bytes.resize(
       static_cast<size_t>(std::max(static_cast<int64_t>(0), bytes_read)));
-  return bytes_read == size;
+
+  bool success = (bytes_read == size);
+  if (success) {
+    DLOG(INFO) << "Successfully read storage record.";
+  }
+  return success;
 }
 
 bool WriteRecord(const scoped_ptr<starboard::StorageRecord>& record,
                  const Savegame::ByteVector& bytes) {
   int64_t byte_count = static_cast<int64_t>(bytes.size());
-  return record->Write(reinterpret_cast<const char*>(bytes.data()), byte_count);
+  bool success =
+      record->Write(reinterpret_cast<const char*>(bytes.data()), byte_count);
+  if (success) {
+    DLOG(INFO) << "Successfully wrote storage record.";
+  }
+  return success;
 }
 
 scoped_ptr<starboard::StorageRecord> CreateRecord(
@@ -182,6 +192,8 @@
 
   if (buffer.size() == 0) {
     // We migrated nothing successfully.
+    DLOG(INFO) << "Migrated storage record data successfully (but trivially, "
+               << "there was no data).";
     return true;
   }
 
@@ -199,6 +211,8 @@
 
   // Now cleanup the fallback record.
   fallback_record->Delete();
+  DLOG(INFO) << "Migrated storage record data successfully for user id: "
+             << options_.id;
   return true;
 }
 
diff --git a/src/cobalt/storage/storage.gyp b/src/cobalt/storage/storage.gyp
index fbf2ce9..5d1d6e0 100644
--- a/src/cobalt/storage/storage.gyp
+++ b/src/cobalt/storage/storage.gyp
@@ -74,7 +74,7 @@
       'variables': {
         'executable_name': 'storage_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
     {
       'target_name': 'storage_upgrade_copy_test_data',
diff --git a/src/cobalt/system_window/system_window.cc b/src/cobalt/system_window/system_window.cc
index 181cf09..f9aab51 100644
--- a/src/cobalt/system_window/system_window.cc
+++ b/src/cobalt/system_window/system_window.cc
@@ -121,7 +121,13 @@
           pressure = std::max(pressure, 0.5f);
         }
         break;
-      default:
+      case InputEvent::kKeyDown:
+      case InputEvent::kKeyUp:
+      case InputEvent::kKeyMove:
+      case InputEvent::kInput:
+      case InputEvent::kPointerUp:
+      case InputEvent::kTouchpadUp:
+      case InputEvent::kWheel:
         break;
     }
   }
diff --git a/src/cobalt/tools/variable_rewrites.dict b/src/cobalt/tools/variable_rewrites.dict
index a11e3fe..e57d766 100644
--- a/src/cobalt/tools/variable_rewrites.dict
+++ b/src/cobalt/tools/variable_rewrites.dict
@@ -33,7 +33,6 @@
     "enable_file_scheme",
     "enable_network_logging",
     "enable_remote_debugging",
-    "enable_screenshot",
     "enable_webdriver",
     "enable_spdy",
     "enable_xhr_header_filtering",
diff --git a/src/cobalt/trace_event/trace_event.gyp b/src/cobalt/trace_event/trace_event.gyp
index 1d48de3..02c62df 100644
--- a/src/cobalt/trace_event/trace_event.gyp
+++ b/src/cobalt/trace_event/trace_event.gyp
@@ -59,7 +59,7 @@
       'variables': {
         'executable_name': 'trace_event_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
@@ -111,7 +111,7 @@
       'variables': {
         'executable_name': 'sample_benchmark',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/web_animations/web_animations.gyp b/src/cobalt/web_animations/web_animations.gyp
index c600a0f..ad133b8 100644
--- a/src/cobalt/web_animations/web_animations.gyp
+++ b/src/cobalt/web_animations/web_animations.gyp
@@ -72,7 +72,7 @@
       'variables': {
         'executable_name': 'web_animations_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/webdriver/protocol/log_entry.cc b/src/cobalt/webdriver/protocol/log_entry.cc
index f94ce21..4e7c0db 100644
--- a/src/cobalt/webdriver/protocol/log_entry.cc
+++ b/src/cobalt/webdriver/protocol/log_entry.cc
@@ -30,10 +30,9 @@
       return "WARNING";
     case LogEntry::kSevere:
       return "SEVERE";
-    default:
-      NOTREACHED();
-      return "DEBUG";
   }
+  NOTREACHED();
+  return "DEBUG";
 }
 }  // namespace
 
diff --git a/src/cobalt/webdriver/script_executor.cc b/src/cobalt/webdriver/script_executor.cc
index 609fbc3..508448a 100644
--- a/src/cobalt/webdriver/script_executor.cc
+++ b/src/cobalt/webdriver/script_executor.cc
@@ -81,8 +81,7 @@
 
   // Evaluate the harness initialization script.
   std::string result;
-  if (!global_environment->EvaluateScript(source, false /*mute_errors*/,
-                                          &result)) {
+  if (!global_environment->EvaluateScript(source, &result)) {
     return NULL;
   }
 
diff --git a/src/cobalt/webdriver/script_executor_params.cc b/src/cobalt/webdriver/script_executor_params.cc
index b00e7e6..d67145c 100644
--- a/src/cobalt/webdriver/script_executor_params.cc
+++ b/src/cobalt/webdriver/script_executor_params.cc
@@ -40,7 +40,7 @@
           function.c_str(), base::SourceLocation("[webdriver]", 1, 1));
 
   if (!global_environment->EvaluateScript(function_source, params.get(),
-                                          false /*mute_errors*/,
+
                                           &params->function_object_)) {
     DLOG(ERROR) << "Failed to create Function object";
   }
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc
index fbb6259..b4c4c60 100644
--- a/src/cobalt/webdriver/web_driver_module.cc
+++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -141,17 +141,19 @@
 
 // Helper struct for getting a PNG screenshot synchronously.
 struct ScreenshotResultContext {
-  ScreenshotResultContext() : num_bytes(0), complete_event(true, false) {}
-  scoped_array<uint8> png_data;
-  size_t num_bytes;
+  ScreenshotResultContext() : complete_event(true, false) {}
+  scoped_refptr<loader::image::EncodedStaticImage> compressed_file;
   base::WaitableEvent complete_event;
 };
 
 // Callback function to be called when PNG encoding is complete.
 void OnPNGEncodeComplete(ScreenshotResultContext* context,
-                         scoped_array<uint8> png_data, size_t num_bytes) {
-  context->png_data = png_data.Pass();
-  context->num_bytes = num_bytes;
+                         const scoped_refptr<loader::image::EncodedStaticImage>&
+                             compressed_image_data) {
+  DCHECK(context);
+  DCHECK(compressed_image_data->GetImageFormat() ==
+         loader::image::EncodedStaticImage::ImageFormat::kPNG);
+  context->compressed_file = compressed_image_data;
   context->complete_event.Signal();
 }
 
@@ -744,8 +746,11 @@
   get_screenshot_function_.Run(
       base::Bind(&OnPNGEncodeComplete, base::Unretained(&context)));
   context.complete_event.Wait();
+  DCHECK(context.compressed_file);
 
-  if (context.num_bytes == 0 || !context.png_data.get()) {
+  uint32 file_size_in_bytes =
+      context.compressed_file->GetEstimatedSizeInBytes();
+  if (file_size_in_bytes == 0 || !context.compressed_file->GetMemory()) {
     return CommandResult(protocol::Response::kUnknownError,
                          "Failed to take screenshot.");
   }
@@ -755,8 +760,9 @@
   {
     // base64 encode the contents of the file to be returned to the client.
     if (!base::Base64Encode(
-            base::StringPiece(reinterpret_cast<char*>(context.png_data.get()),
-                              context.num_bytes),
+            base::StringPiece(
+                reinterpret_cast<char*>(context.compressed_file->GetMemory()),
+                file_size_in_bytes),
             &encoded)) {
       return CommandResult(protocol::Response::kUnknownError,
                            "Failed to base64 encode screenshot file contents.");
diff --git a/src/cobalt/webdriver/web_driver_module.h b/src/cobalt/webdriver/web_driver_module.h
index d2bef0d..5cf2a18 100644
--- a/src/cobalt/webdriver/web_driver_module.h
+++ b/src/cobalt/webdriver/web_driver_module.h
@@ -21,6 +21,7 @@
 #include "base/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/threading/thread_checker.h"
+#include "cobalt/dom/window.h"
 #include "cobalt/webdriver/dispatcher.h"
 #include "cobalt/webdriver/protocol/button.h"
 #include "cobalt/webdriver/protocol/capabilities.h"
@@ -43,7 +44,8 @@
  public:
   typedef base::Callback<scoped_ptr<SessionDriver>(const protocol::SessionId&)>
       CreateSessionDriverCB;
-  typedef base::Callback<void(scoped_array<uint8>, size_t)>
+  typedef base::Callback<void(
+      const scoped_refptr<loader::image::EncodedStaticImage>& image_data)>
       ScreenshotCompleteCallback;
   typedef base::Callback<void(const ScreenshotCompleteCallback&)>
       GetScreenshotFunction;
diff --git a/src/cobalt/webdriver/webdriver_test.gyp b/src/cobalt/webdriver/webdriver_test.gyp
index a71648a..9342fdc 100644
--- a/src/cobalt/webdriver/webdriver_test.gyp
+++ b/src/cobalt/webdriver/webdriver_test.gyp
@@ -43,7 +43,7 @@
       'variables': {
         'executable_name': 'webdriver_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ]
 }
diff --git a/src/cobalt/websocket/web_socket_impl.cc b/src/cobalt/websocket/web_socket_impl.cc
index 9381765..f27bdcf 100644
--- a/src/cobalt/websocket/web_socket_impl.cc
+++ b/src/cobalt/websocket/web_socket_impl.cc
@@ -476,7 +476,6 @@
     case net::WebSocketFrameHeader::kOpCodeContinuation:
     case net::WebSocketFrameHeader::kOpCodeText:
     case net::WebSocketFrameHeader::kOpCodeBinary:
-    default:
       NOTREACHED() << "Invalid case " << header_pointer->opcode;
 
       CloseInfo close_info(net::kWebSocketErrorInternalServerError,
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index 1fe61a7..dfc26e6 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -79,7 +79,7 @@
       'variables': {
         'executable_name': 'websocket_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp
index d055477..7305802 100644
--- a/src/cobalt/xhr/xhr.gyp
+++ b/src/cobalt/xhr/xhr.gyp
@@ -82,7 +82,7 @@
       'variables': {
         'executable_name': 'xhr_test',
       },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index e3506b0..6da2124 100644
--- a/src/cobalt/xhr/xml_http_request.cc
+++ b/src/cobalt/xhr/xml_http_request.cc
@@ -384,16 +384,20 @@
     FireProgressEvent(upload_, base::Tokens::loadstart());
   }
 
-  StartRequest(request_body_text_);
+  // The loadstart callback may abort or modify the XHR request in some way.
+  // 11.3. If state is not opened or the send() flag is unset, then return.
+  if (state_ == kOpened && sent_) {
+    StartRequest(request_body_text_);
 
-  // Start the timeout timer running, if applicable.
-  send_start_time_ = base::TimeTicks::Now();
-  if (timeout_ms_) {
-    StartTimer(base::TimeDelta());
+    // Start the timeout timer running, if applicable.
+    send_start_time_ = base::TimeTicks::Now();
+    if (timeout_ms_) {
+      StartTimer(base::TimeDelta());
+    }
+    // Timer for throttling progress events.
+    upload_last_progress_time_ = base::TimeTicks();
+    last_progress_time_ = base::TimeTicks();
   }
-  // Timer for throttling progress events.
-  upload_last_progress_time_ = base::TimeTicks();
-  last_progress_time_ = base::TimeTicks();
 }
 
 void XMLHttpRequest::Fetch(const FetchUpdateCallbackArg& fetch_callback,
diff --git a/src/content/browser/speech/speech.gyp b/src/content/browser/speech/speech.gyp
index 4213d67..e41cfad 100644
--- a/src/content/browser/speech/speech.gyp
+++ b/src/content/browser/speech/speech.gyp
@@ -52,7 +52,7 @@
       'variables': {
         'executable_name': 'speech_test',
       },
-      'includes': [ '../../../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/crypto/crypto.gyp b/src/crypto/crypto.gyp
index 9409336..a6d89e5 100644
--- a/src/crypto/crypto.gyp
+++ b/src/crypto/crypto.gyp
@@ -112,7 +112,7 @@
           'variables': {
             'executable_name': 'crypto_unittests',
           },
-          'includes': [ '../starboard/build/deploy.gypi' ],
+          'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
         },
       ],
     }],
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index c62ffb6..2658632 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -126,7 +126,7 @@
       'variables': {
         'executable_name': 'nb_test',
       },
-      'includes': [ '../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
 
     {
diff --git a/src/net/net.gyp b/src/net/net.gyp
index c02cbf1..8d79dbf 100644
--- a/src/net/net.gyp
+++ b/src/net/net.gyp
@@ -1200,7 +1200,7 @@
       'variables': {
         'executable_name': 'net_unittests',
       },
-      'includes': [ '../starboard/build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/sql/sql.gyp b/src/sql/sql.gyp
index ef71147..b803859 100644
--- a/src/sql/sql.gyp
+++ b/src/sql/sql.gyp
@@ -96,7 +96,7 @@
           'variables': {
             'executable_name': 'sql_unittests',
           },
-          'includes': [ '../starboard/build/deploy.gypi' ],
+          'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
         },
       ],
     }],
diff --git a/src/starboard/audio_sink.h b/src/starboard/audio_sink.h
index 790741a..2b19e9a 100644
--- a/src/starboard/audio_sink.h
+++ b/src/starboard/audio_sink.h
@@ -63,7 +63,15 @@
 
 // Callback used to report frames consumed.  The consumed frames will be
 // removed from the source frame buffer to free space for new audio frames.
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+// When |frames_consumed| is updated asynchnously and the last time that it has
+// been updated is known, it can be passed in |frames_consumed_at| so the audio
+// time calculating can be more accurate.
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 typedef void (*SbAudioSinkConsumeFramesFunc)(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                             SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                              void* context);
 
 // Well-defined value for an invalid audio sink.
@@ -81,7 +89,11 @@
 // audio sink, and returns an opaque handle to the audio sink.
 //
 // If the particular platform doesn't support the requested audio sink, the
-// function returns kSbAudioSinkInvalid without calling any of the callbacks.
+// function returns |kSbAudioSinkInvalid| without calling any of the callbacks.
+// If there is a platform limitation on how many audio sinks can coexist
+// simultaneously, then calls made to this function that attempt to exceed
+// that limit must return |kSbAudioSinkInvalid|. Multiple calls to
+// SbAudioSinkCreate must not cause a crash.
 //
 // |channels|: The number of audio channels, such as left and right channels
 // in stereo audio.
diff --git a/src/starboard/build/deploy.gypi b/src/starboard/build/deploy.gypi
index 048591b..b60ff77 100644
--- a/src/starboard/build/deploy.gypi
+++ b/src/starboard/build/deploy.gypi
@@ -31,7 +31,7 @@
 #      'variables': {
 #        'executable_name': 'target',
 #      },
-#      'includes': [ '../build/deploy.gypi' ],
+#      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
 #    },
 #    ...
 #
@@ -51,7 +51,7 @@
 #      'variables': {
 #        'executable_name': 'nplb',
 #      },
-#      'includes': [ '../build/deploy.gypi' ],
+#      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
 #    },
 
 {
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index 43c1c1a..87415a2 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -18,7 +18,7 @@
 import logging
 import os
 
-import _env  # pylint: disable=unused-import
+import _env  # pylint: disable=unused-import, relative-import
 from starboard.build.application_configuration import ApplicationConfiguration
 from starboard.tools import environment
 from starboard.tools import paths
@@ -121,8 +121,9 @@
         configuration_class = _GetApplicationConfigurationClass(
             configuration_path)
         if configuration_class:
-          logging.info('Using platform-specific ApplicationConfiguration for '
-                       '%s.', application_name)
+          logging.info(
+              'Using platform-specific ApplicationConfiguration for '
+              '%s.', application_name)
           break
 
       if not configuration_class:
@@ -216,6 +217,19 @@
         # Whether to build with clang's Thread Sanitizer instrumentation.
         'use_tsan': use_tsan,
 
+        # Which JavaScript engine to use.  Currently, both SpiderMonkey 45 and
+        # V8 are supported.  Note that V8 can only be used on platforms that
+        # support JIT.
+        'javascript_engine': 'mozjs-45',
+
+        # Disable JIT and run in interpreter-only mode by default. It can be
+        # set to 1 to run in JIT mode.  For SpiderMonkey in particular, we
+        # have found that disabling JIT often results in faster JavaScript
+        # execution and lower memory usage.  Setting this to 0 for engine that
+        # requires JIT, or 1 on a platform that does not support JIT, is a
+        # usage error.
+        'cobalt_enable_jit': 0,
+
         # TODO: Remove these compatibility variables.
         'cobalt_config': config_name,
         'cobalt_fastbuild': 0,
@@ -289,6 +303,7 @@
     return [
         'nplb',
         'nplb_blitter_pixel_tests',
+        'player_filter_tests',
         'starboard_platform_tests',
     ]
 
diff --git a/src/starboard/client_porting/eztime/eztime.gyp b/src/starboard/client_porting/eztime/eztime.gyp
index 70f51e6..0b6b559 100644
--- a/src/starboard/client_porting/eztime/eztime.gyp
+++ b/src/starboard/client_porting/eztime/eztime.gyp
@@ -22,35 +22,11 @@
         'eztime.h',
       ],
       'dependencies': [
-        '<(DEPTH)/starboard/starboard.gyp:starboard',
+        '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
         '<(DEPTH)/starboard/client_porting/icu_init/icu_init.gyp:icu_init',
         '<(DEPTH)/third_party/icu/icu.gyp:icui18n',
         '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
       ],
     },
-    {
-      'target_name': 'eztime_test',
-      'type': '<(gtest_target_type)',
-      'sources': [
-        '<(DEPTH)/starboard/common/test_main.cc',
-        'test_constants.h',
-        'eztime_test.cc',
-      ],
-      'dependencies': [
-        '<(DEPTH)/testing/gmock.gyp:gmock',
-        '<(DEPTH)/testing/gtest.gyp:gtest',
-        'eztime',
-      ],
-    },
-    {
-      'target_name': 'eztime_test_deploy',
-      'type': 'none',
-      'dependencies': [
-        'eztime_test',
-      ],
-      'variables': {
-        'executable_name': 'eztime_test',
-      },
-    },
   ],
 }
diff --git a/src/starboard/client_porting/eztime/eztime_test.gyp b/src/starboard/client_porting/eztime/eztime_test.gyp
new file mode 100644
index 0000000..710803b
--- /dev/null
+++ b/src/starboard/client_porting/eztime/eztime_test.gyp
@@ -0,0 +1,44 @@
+# Copyright 2018 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'eztime_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        '<(DEPTH)/starboard/common/test_main.cc',
+        'test_constants.h',
+        'eztime_test.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/client_porting/eztime/eztime.gyp:eztime',
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+        '<(DEPTH)/testing/gmock.gyp:gmock',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+    {
+      'target_name': 'eztime_test_deploy',
+      'type': 'none',
+      'dependencies': [
+        'eztime_test',
+      ],
+      'variables': {
+        'executable_name': 'eztime_test',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/starboard/client_porting/icu_init/icu_init.gyp b/src/starboard/client_porting/icu_init/icu_init.gyp
index e3e5768..3e0cfdc 100644
--- a/src/starboard/client_porting/icu_init/icu_init.gyp
+++ b/src/starboard/client_porting/icu_init/icu_init.gyp
@@ -22,7 +22,7 @@
         'icu_init.h',
       ],
       'dependencies': [
-        '<(DEPTH)/starboard/starboard.gyp:starboard',
+        '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
         '<(DEPTH)/third_party/icu/icu.gyp:icui18n',
         '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
       ],
diff --git a/src/starboard/client_porting/poem/poem.gyp b/src/starboard/client_porting/poem/poem.gyp
index 9779e63..193d9af 100644
--- a/src/starboard/client_porting/poem/poem.gyp
+++ b/src/starboard/client_porting/poem/poem.gyp
@@ -37,7 +37,7 @@
       'variables': {
         'executable_name': 'poem_unittests',
       },
-      'includes': [ '../../build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index fb8598a..6bd70f3 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -68,6 +68,9 @@
 //   //   exposes functionality for my new feature.
 //   #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// API version where SbMediaTime is deprecated (for SbTime).
+#define SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // Minimum API version where supporting player error messages is required.
 #define SB_PLAYER_ERROR_MESSAGE_API_VERSION SB_EXPERIMENTAL_API_VERSION
 
@@ -77,6 +80,14 @@
 // Minimum API version where supporting audioless video playback is required.
 #define SB_AUDIOLESS_VIDEO_API_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// Minimum API version where calling SbPlayerCreate mutiple times (without
+// calling SbPlayerDestroy in between) must not crash, and likewise calling
+// SbAudioSinkCreate multiple times (without calling SbAudioSinkDestroy in
+// between) must not crash. SbPlayerCreate may return kSbPlayerInvalid if
+// additional players are not supported. SbAudioSinkCreate may return
+// kSbAudionSinkInvalid if additional audio sinks are not supported.
+#define SB_MULTI_PLAYER_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // API version where DRM session closed callback is required.
 //   Add a callback to SbDrmCreateSystem that allows a DRM system to
 //   signal that a DRM session has closed from the Starboard layer.
@@ -99,6 +110,30 @@
 //   support int16 audio samples after this version.
 #define SB_DEPRECATE_INT16_AUDIO_SAMPLE_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// API version where SbSystemSupportsResume() is supported.
+//   Platforms doesn't need to resume after suspend can return false in
+//   SbSystemSupportsResume() to free up the resource used by resume after
+//   suspend.
+//   Please see the comment in system.h for more details.
+#define SB_ALLOW_DISABLE_RESUME_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Minimum API version for supporting the kSbKeyMicrophone keycode
+#define SB_MICROPHONE_KEY_CODE_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Add support for new decode target type,
+// kSbDecodeTargetFormat3Plane10BitYUVI420.
+//   Added kSbDecodeTargetFormat3Plane10BitYUVI420 to the SbDecodeTargetFormat
+//   enum in order to support 10-bit YUV textures.
+#define SB_10_BIT_YUV_I420_DECODE_TARGET_SUPPORT_API_VERSION \
+  SB_EXPERIMENTAL_API_VERSION
+
+// API version where SbAudioSinkConsumeFramesFunc() can optional take an
+//   absolute timestamp to indicate when the frames are consumed.
+//   Platforms that have the |frames_consumed| updated asynchronously can have
+//   more accurate audio time reporting with this extra parameter.
+//   Please see the comment in audio_sink.h for more details.
+#define SB_ASYNC_AUDIO_FRAMES_REPORTING_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // --- Release Candidate Feature Defines -------------------------------------
 
 // --- Common Detected Features ----------------------------------------------
@@ -625,6 +660,13 @@
 #endif  // !SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
 #endif  // SB_API_VERSION < SB_DEPRECATE_INT16_AUDIO_SAMPLE_VERSION
 
+#if SB_API_VERSION >= SB_ASYNC_AUDIO_FRAMES_REPORTING_API_VERSION
+#if !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
+#error Your platform must define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING in API \
+    version SB_ASYNC_AUDIO_FRAMES_REPORTING_API_VERSION or later.
+#endif  // !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_ASYNC_AUDIO_FRAMES_REPORTING_API_VERSION
+
 // --- Derived Configuration -------------------------------------------------
 
 // Whether the current platform is little endian.
diff --git a/src/starboard/contrib/creator/ci20x11/egl_workaround.cc b/src/starboard/contrib/creator/ci20x11/egl_workaround.cc
index e6f83e2..f812d71 100644
--- a/src/starboard/contrib/creator/ci20x11/egl_workaround.cc
+++ b/src/starboard/contrib/creator/ci20x11/egl_workaround.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <X11/Xlib.h>
 #include "cobalt/renderer/backend/egl/display.h"
 
 extern "C" EGLDisplay __real_eglGetDisplay(EGLNativeDisplayType native_display);
@@ -20,7 +21,7 @@
 extern "C" EGLBoolean __real_eglTerminate(EGLDisplay display);
 extern "C" EGLBoolean __wrap_eglTerminate(EGLDisplay display);
 
-NativeDisplayType native_display_;
+Display* native_display_;
 
 extern "C" EGLDisplay
     __wrap_eglGetDisplay(EGLNativeDisplayType native_display) {
@@ -30,6 +31,6 @@
 
 extern "C" EGLBoolean __wrap_eglTerminate(EGLDisplay display) {
   EGLBoolean result = __real_eglTerminate(display);
-  XCloseDisplay((NativeDisplayType) native_display_);
+  XCloseDisplay(native_display_);
   return result;
 }
diff --git a/src/starboard/contrib/creator/ci20x11/gcc/4.9/compiler_flags.gypi b/src/starboard/contrib/creator/ci20x11/gcc/4.9/compiler_flags.gypi
index b782944..864c7ec 100644
--- a/src/starboard/contrib/creator/ci20x11/gcc/4.9/compiler_flags.gypi
+++ b/src/starboard/contrib/creator/ci20x11/gcc/4.9/compiler_flags.gypi
@@ -92,6 +92,11 @@
   },
 
   'target_defaults': {
+    'defines': [
+      # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
+      # nasty macros (|Status|, for example) that conflict with Chromium base.
+      'MESA_EGL_NO_X11_HEADERS'
+    ],
     'cflags_c': [
       # Limit to C99. This allows Linux to be a canary build for any
       # C11 features that are not supported on some platforms' compilers.
diff --git a/src/starboard/contrib/creator/ci20x11/system_get_property.cc b/src/starboard/contrib/creator/ci20x11/system_get_property.cc
index cb834af..fd4222c 100644
--- a/src/starboard/contrib/creator/ci20x11/system_get_property.cc
+++ b/src/starboard/contrib/creator/ci20x11/system_get_property.cc
@@ -14,8 +14,8 @@
 
 #include "starboard/system.h"
 
-#include <linux/if.h>  // NOLINT(build/include_alpha)
 #include <netdb.h>
+#include <linux/if.h>  // NOLINT(build/include_alpha)
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 
diff --git a/src/starboard/contrib/creator/shared/compiler_flags.gypi b/src/starboard/contrib/creator/shared/compiler_flags.gypi
index 3cbdf7c..d3005f1 100644
--- a/src/starboard/contrib/creator/shared/compiler_flags.gypi
+++ b/src/starboard/contrib/creator/shared/compiler_flags.gypi
@@ -103,7 +103,7 @@
     'defines': [
       # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
       # nasty macros (|Status|, for example) that conflict with Chromium base.
-      #'MESA_EGL_NO_X11_HEADERS'
+      'MESA_EGL_NO_X11_HEADERS'
     ],
     'target_conditions': [
       ['clang==1', {
diff --git a/src/starboard/contrib/creator/shared/gyp_configuration.gypi b/src/starboard/contrib/creator/shared/gyp_configuration.gypi
index b853967..26a5869 100644
--- a/src/starboard/contrib/creator/shared/gyp_configuration.gypi
+++ b/src/starboard/contrib/creator/shared/gyp_configuration.gypi
@@ -24,7 +24,9 @@
 
     'platform_libraries': [
       '-lasound',
-      '-ldl',
+      '-lavcodec',
+      '-lavformat',
+      '-lavutil',
       '-lm',
       '-lpthread',
       '-lrt',
diff --git a/src/starboard/contrib/creator/shared/player_components_impl.cc b/src/starboard/contrib/creator/shared/player_components_impl.cc
index 3195d3a..95ae78c 100644
--- a/src/starboard/contrib/creator/shared/player_components_impl.cc
+++ b/src/starboard/contrib/creator/shared/player_components_impl.cc
@@ -27,7 +27,6 @@
 #include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
 #include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h"
 #include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
-#include "starboard/time.h"
 
 namespace starboard {
 namespace shared {
@@ -47,9 +46,9 @@
     SB_DCHECK(audio_decoder);
     SB_DCHECK(audio_renderer_sink);
 
-    scoped_ptr<AudioDecoderImpl> audio_decoder_impl(new AudioDecoderImpl(
+    scoped_ptr<AudioDecoderImpl> audio_decoder_impl(AudioDecoderImpl::Create(
         audio_parameters.audio_codec, audio_parameters.audio_header));
-    if (audio_decoder_impl->is_valid()) {
+    if (audio_decoder_impl && audio_decoder_impl->is_valid()) {
       audio_decoder->reset(audio_decoder_impl.release());
     } else {
       audio_decoder->reset();
@@ -73,10 +72,10 @@
     video_decoder->reset();
 
     scoped_ptr<FfmpegVideoDecoderImpl> ffmpeg_video_decoder(
-        new FfmpegVideoDecoderImpl(
+        FfmpegVideoDecoderImpl::Create(
             video_parameters.video_codec, video_parameters.output_mode,
             video_parameters.decode_target_graphics_context_provider));
-    if (ffmpeg_video_decoder->is_valid()) {
+    if (ffmpeg_video_decoder && ffmpeg_video_decoder->is_valid()) {
       video_decoder->reset(ffmpeg_video_decoder.release());
     }
 
diff --git a/src/starboard/contrib/tizen/shared/wayland/application_tizen.cc b/src/starboard/contrib/tizen/shared/wayland/application_tizen.cc
new file mode 100644
index 0000000..0dec35c
--- /dev/null
+++ b/src/starboard/contrib/tizen/shared/wayland/application_tizen.cc
@@ -0,0 +1,54 @@
+// Copyright 2018 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/contrib/tizen/shared/wayland/application_tizen.h"
+
+#include <string.h>
+
+#include "starboard/contrib/tizen/shared/wayland/window_internal_tizen.h"
+
+namespace starboard {
+namespace shared {
+namespace wayland {
+
+void ApplicationWaylandTizen::Initialize() {
+  elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+  ApplicationWayland::Initialize();
+}
+
+bool ApplicationWaylandTizen::OnGlobalObjectAvailable(
+    struct wl_registry* registry,
+    uint32_t name,
+    const char* interface,
+    uint32_t version) {
+  if (!strcmp(interface, "tizen_policy")) {
+    tz_policy_ = static_cast<tizen_policy*>(wl_registry_bind(
+        registry, name, &tizen_policy_interface, SB_TIZEN_POLICY_VERSION));
+    return true;
+  }
+
+  return ApplicationWayland::OnGlobalObjectAvailable(registry, name, interface,
+                                                     version);
+}
+
+SbWindow ApplicationWaylandTizen::CreateWindow(const SbWindowOptions* options) {
+  SbWindow window = new SbWindowPrivateTizen(
+      display_, tz_policy_, compositor_, shell_, options, video_pixel_ratio_);
+  dev_input_.SetSbWindow(window);
+  return window;
+}
+
+}  // wayland
+}  // shared
+}  // starboard
diff --git a/src/starboard/contrib/tizen/shared/wayland/application_tizen.h b/src/starboard/contrib/tizen/shared/wayland/application_tizen.h
new file mode 100644
index 0000000..e766a81
--- /dev/null
+++ b/src/starboard/contrib/tizen/shared/wayland/application_tizen.h
@@ -0,0 +1,50 @@
+// Copyright 2018 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_CONTRIB_TIZEN_SHARED_WAYLAND_APPLICATION_TIZEN_H_
+#define STARBOARD_CONTRIB_TIZEN_SHARED_WAYLAND_APPLICATION_TIZEN_H_
+
+#include <tizen-extension-client-protocol.h>
+
+#include "starboard/shared/wayland/application_wayland.h"
+
+namespace starboard {
+namespace shared {
+namespace wayland {
+
+class ApplicationWaylandTizen : public ApplicationWayland {
+ public:
+  ApplicationWaylandTizen() {}
+  ~ApplicationWaylandTizen() {}
+
+  // wl registry add listener
+  bool OnGlobalObjectAvailable(struct wl_registry* registry,
+                               uint32_t name,
+                               const char* interface,
+                               uint32_t version) override;
+
+  SbWindow CreateWindow(const SbWindowOptions* options) override;
+
+ protected:
+  void Initialize() override;
+
+ private:
+  tizen_policy* tz_policy_;
+};
+
+}  // wayland
+}  // shared
+}  // starboard
+
+#endif  // STARBOARD_CONTRIB_TIZEN_SHARED_WAYLAND_APPLICATION_TIZEN_H_
diff --git a/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.cc b/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.cc
new file mode 100644
index 0000000..723050c
--- /dev/null
+++ b/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.cc
@@ -0,0 +1,75 @@
+// Copyright 2018 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/contrib/tizen/shared/wayland/window_internal_tizen.h"
+
+#include "starboard/shared/wayland/application_wayland.h"
+
+static void WindowCbVisibilityChange(void* data,
+                                     struct tizen_visibility* tizen_visibility
+                                         EINA_UNUSED,
+                                     uint32_t visibility) {
+#if SB_HAS(LAZY_SUSPEND)
+  if (visibility == TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED)
+    ApplicationWayland::Get()->Pause(NULL, NULL);
+  else
+    ApplicationWayland::Get()->Unpause(NULL, NULL);
+#else
+  if (visibility == TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED)
+    starboard::shared::starboard::Application::Get()->Suspend(NULL, NULL);
+  else
+    starboard::shared::starboard::Application::Get()->Unpause(NULL, NULL);
+#endif
+}
+
+static const struct tizen_visibility_listener tizen_visibility_listener = {
+    WindowCbVisibilityChange
+};
+
+SbWindowPrivateTizen::SbWindowPrivateTizen(wl_display* display,
+                                           tizen_policy* policy,
+                                           wl_compositor* compositor,
+                                           wl_shell* shell,
+                                           const SbWindowOptions* options,
+                                           float pixel_ratio)
+    : SbWindowPrivate(compositor, shell, options, pixel_ratio) {
+#if SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
+  video_window = display;
+#else
+  video_window = elm_win_add(NULL, "Cobalt_Video", ELM_WIN_BASIC);
+  elm_win_title_set(video_window, "Cobalt_Video");
+  elm_win_autodel_set(video_window, EINA_TRUE);
+  evas_object_resize(video_window, width, height);
+  evas_object_hide(video_window);
+#endif
+  tz_policy = policy;
+  tz_visibility = tizen_policy_get_visibility(tz_policy, surface);
+  tizen_visibility_add_listener(tz_visibility, &tizen_visibility_listener,
+                                this);
+  tizen_policy_activate(tz_policy, surface);
+}
+
+SbWindowPrivateTizen::~SbWindowPrivateTizen() {
+#if !SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
+  evas_object_hide(video_window);
+#endif
+  video_window = NULL;
+  tizen_visibility_destroy(tz_visibility);
+}
+
+void SbWindowPrivateTizen::WindowRaise() {
+  SbWindowPrivate::WindowRaise();
+  if (tz_policy)
+    tizen_policy_raise(tz_policy, surface);
+}
diff --git a/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.h b/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.h
new file mode 100644
index 0000000..3375588
--- /dev/null
+++ b/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.h
@@ -0,0 +1,44 @@
+// Copyright 2018 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_CONTRIB_TIZEN_SHARED_WAYLAND_WINDOW_INTERNAL_TIZEN_H_
+#define STARBOARD_CONTRIB_TIZEN_SHARED_WAYLAND_WINDOW_INTERNAL_TIZEN_H_
+
+#include <Elementary.h>
+#include <tizen-extension-client-protocol.h>
+
+#include "starboard/shared/wayland/window_internal.h"
+
+struct SbWindowPrivateTizen : SbWindowPrivate {
+  explicit SbWindowPrivateTizen(wl_display* display,
+                                tizen_policy* policy,
+                                wl_compositor* compositor,
+                                wl_shell* shell,
+                                const SbWindowOptions* options,
+                                float pixel_ratio = 1.0);
+
+  ~SbWindowPrivateTizen();
+
+  void WindowRaise() override;
+
+  tizen_policy* tz_policy;
+  tizen_visibility* tz_visibility;
+#if SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
+  wl_display* video_window;
+#else
+  Evas_Object* video_window;
+#endif
+};
+
+#endif  // STARBOARD_CONTRIB_TIZEN_SHARED_WAYLAND_WINDOW_INTERNAL_TIZEN_H_
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index b04aa42..dfd9189 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -135,6 +135,12 @@
   // A decoder target format consisting of Y, U, and V planes, in that order.
   kSbDecodeTargetFormat3PlaneYUVI420,
 
+#if SB_API_VERSION >= SB_10_BIT_YUV_I420_DECODE_TARGET_SUPPORT_API_VERSION
+  // A decoder target format consisting of 10bit Y, U, and V planes, in that
+  // order.
+  kSbDecodeTargetFormat3Plane10BitYUVI420,
+#endif
+
 #if SB_API_VERSION >= 6
   // A decoder target format consisting of a single plane with pixels layed out
   // in the format UYVY.  Since there are two Y values per sample, but only one
@@ -334,6 +340,9 @@
       return 1;
     case kSbDecodeTargetFormat2PlaneYUVNV12:
       return 2;
+#if SB_API_VERSION >= SB_10_BIT_YUV_I420_DECODE_TARGET_SUPPORT_API_VERSION
+    case kSbDecodeTargetFormat3Plane10BitYUVI420:
+#endif
     case kSbDecodeTargetFormat3PlaneYUVI420:
       return 3;
     default:
diff --git a/src/starboard/doc/style.md b/src/starboard/doc/style.md
index 7345067..f14aa33 100644
--- a/src/starboard/doc/style.md
+++ b/src/starboard/doc/style.md
@@ -227,6 +227,11 @@
     comments with pipes around them.
   * All comments must be full grammatically-correct English sentences with
     proper punctuation.
+  * Comments in Starboard headers must be written as requirements for the
+    porter, for example: "must not return NULL" or "should not return
+    NULL" rather than "will not return NULL". The choice of "must" vs "should"
+    must follow the guidelines of IETF RFC,
+    https://www.ietf.org/rfc/rfc2119.txt .
 
 ### Implementations
 
diff --git a/src/starboard/examples/blitter/blitter.gyp b/src/starboard/examples/blitter/blitter.gyp
index 0b3483b..41872b1 100644
--- a/src/starboard/examples/blitter/blitter.gyp
+++ b/src/starboard/examples/blitter/blitter.gyp
@@ -36,6 +36,7 @@
       'variables': {
         'executable_name': 'starboard_blitter_example',
       },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/examples/glclear/glclear.gyp b/src/starboard/examples/glclear/glclear.gyp
index 46a020c..a9c3623 100644
--- a/src/starboard/examples/glclear/glclear.gyp
+++ b/src/starboard/examples/glclear/glclear.gyp
@@ -34,6 +34,7 @@
       'variables': {
         'executable_name': 'starboard_glclear_example',
       },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/examples/window/window.gyp b/src/starboard/examples/window/window.gyp
index 496d7cd..db9d23a 100644
--- a/src/starboard/examples/window/window.gyp
+++ b/src/starboard/examples/window/window.gyp
@@ -33,6 +33,7 @@
       'variables': {
         'executable_name': 'starboard_window_example',
       },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/key.h b/src/starboard/key.h
index 27e0431..c2331fa 100644
--- a/src/starboard/key.h
+++ b/src/starboard/key.h
@@ -245,6 +245,11 @@
   kSbKeyMediaAudioTrack = 0x3001,
 #endif  // SB_API_VERSION >= 6
 
+#if SB_API_VERSION >= SB_MICROPHONE_KEY_CODE_API_VERSION
+  // A button that will trigger voice input.
+  kSbKeyMicrophone = 0x3002,
+#endif  // SB_API_VERSION >= SB_MICROPHONE_KEY_CODE_API_VERSION
+
   // Mouse buttons, starting with the left mouse button.
   kSbKeyMouse1 = 0x7000,
   kSbKeyMouse2 = 0x7001,
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
index 79bdf4e..73ce74e 100644
--- a/src/starboard/linux/shared/BUILD.gn
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -537,6 +537,7 @@
     "//starboard/shared/starboard/system_request_stop.cc",
     "//starboard/shared/starboard/system_request_suspend.cc",
     "//starboard/shared/starboard/system_request_unpause.cc",
+    '//starboard/shared/starboard/system_supports_resume.cc',
     "//starboard/shared/starboard/window_set_default_options.cc",
     "//starboard/shared/stub/accessibility_get_display_settings.cc",
     "//starboard/shared/stub/accessibility_get_text_to_speech_settings.cc",
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index bbe079c..e0cbe8e 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -273,6 +273,13 @@
 // non-zero on platforms with webm/vp9 support.
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
 
+// Specifies whether this platform updates audio frames asynchronously.  In such
+// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
+// indicate the absolute time that the consumed audio frames are reported.
+// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
+// details.
+#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 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.
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi
index a98a1ba..13264f2 100644
--- a/src/starboard/linux/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -20,9 +20,6 @@
     'target_arch%': 'x64',
     'target_os': 'linux',
 
-    'javascript_engine%': 'v8',
-    'cobalt_enable_jit%': 1,
-
     'platform_libraries': [
       '-lasound',
       '-ldl',
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index d23d1e7..33bf5c7 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -40,8 +40,13 @@
     return 'ninja,qtcreator_ninja'
 
   def GetVariables(self, config_name):
-    return super(LinuxConfiguration, self).GetVariables(config_name,
-                                                        use_clang=1)
+    variables = super(LinuxConfiguration, self).GetVariables(
+        config_name, use_clang=1)
+    variables.update({
+        'javascript_engine': 'v8',
+        'cobalt_enable_jit': 1,
+    })
+    return variables
 
   def GetLauncherPath(self):
     """Gets the path to the launcher module for this platform."""
@@ -49,7 +54,9 @@
 
   def GetGeneratorVariables(self, config_name):
     del config_name
-    generator_variables = {'qtcreator_session_name_prefix': 'cobalt',}
+    generator_variables = {
+        'qtcreator_session_name_prefix': 'cobalt',
+    }
     return generator_variables
 
   def GetEnvironmentVariables(self):
@@ -66,8 +73,18 @@
 
   def GetTestFilters(self):
     filters = super(LinuxConfiguration, self).GetTestFilters()
-    filters.extend([
-        test_filter.TestFilter(
-            'starboard_platform_tests', test_filter.FILTER_ALL),
-    ])
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
     return filters
+
+  _FILTERED_TESTS = {
+      'starboard_platform_tests': [test_filter.FILTER_ALL],
+      'player_filter_tests': [
+          # These tests have memory leaks related to av_malloc.
+          'AudioDecoderTests/AudioDecoderTest.*',
+
+          # These tests fail on buildbot.
+          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/0',
+          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/1',
+      ]
+  }
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index c73b15c..057d1d8 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -270,10 +270,12 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
@@ -281,6 +283,7 @@
       '<(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/player_write_sample2.cc',
       '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
       '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
       '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc',
@@ -292,6 +295,7 @@
       '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_supports_resume.cc',
       '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
       '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
       '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
@@ -332,7 +336,7 @@
           '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
           '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
           '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
-          '<(DEPTH)/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc',
+          '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
         ],
       }, {
         'starboard_platform_sources': [
diff --git a/src/starboard/linux/shared/starboard_platform_tests.gypi b/src/starboard/linux/shared/starboard_platform_tests.gypi
new file mode 100644
index 0000000..4eaacd1
--- /dev/null
+++ b/src/starboard/linux/shared/starboard_platform_tests.gypi
@@ -0,0 +1,51 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '<(DEPTH)/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'starboard_platform_tests',
+      'type': '<(gtest_target_type)',
+      'includes': [
+        '<(DEPTH)/starboard/shared/starboard/media/media_tests.gypi',
+      ],
+      'sources': [
+        '<(DEPTH)/starboard/common/test_main.cc',
+        '<@(media_tests_sources)',
+      ],
+      'defines': [
+        # This allows the tests to include internal only header files.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+        '<(DEPTH)/testing/gmock.gyp:gmock',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+    {
+      'target_name': 'starboard_platform_tests_deploy',
+      'type': 'none',
+      'dependencies': [
+        '<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:starboard_platform_tests',
+      ],
+      'variables': {
+        'executable_name': 'starboard_platform_tests',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform_tests.gyp b/src/starboard/linux/x64directfb/future/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64directfb/future/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64directfb/starboard_platform_tests.gyp b/src/starboard/linux/x64directfb/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64directfb/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
index 82e3e7b..3b53f39 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -21,11 +21,11 @@
 from starboard.tools import build
 
 
-class PlatformConfig(shared_configuration.LinuxConfiguration):
+class LinuxX64X11Clang36Configuration(shared_configuration.LinuxConfiguration):
   """Starboard Linux X64 X11 Clang 3.6 platform configuration."""
 
   def __init__(self, platform, asan_enabled_by_default=True):
-    super(PlatformConfig, self).__init__(
+    super(LinuxX64X11Clang36Configuration, self).__init__(
         platform, asan_enabled_by_default, goma_supports_compiler=False)
 
     script_path = os.path.dirname(os.path.realpath(__file__))
@@ -37,7 +37,8 @@
                                       'Release+Asserts')
 
   def GetEnvironmentVariables(self):
-    env_variables = super(PlatformConfig, self).GetEnvironmentVariables()
+    env_variables = super(LinuxX64X11Clang36Configuration,
+                          self).GetEnvironmentVariables()
     toolchain_bin_dir = os.path.join(self.toolchain_dir, 'bin')
     env_variables.update({
         'CC': os.path.join(toolchain_bin_dir, 'clang'),
@@ -45,10 +46,21 @@
     })
     return env_variables
 
+  def GetVariables(self, config_name):
+    # A significant amount of code in V8 fails to compile on clang 3.6 using
+    # the debug config, due to an internal error in clang.
+    variables = super(LinuxX64X11Clang36Configuration,
+                      self).GetVariables(config_name)
+    variables.update({
+        'javascript_engine': 'mozjs-45',
+        'cobalt_enable_jit': 0,
+    })
+    return variables
+
 
 def CreatePlatformConfig():
   try:
-    return PlatformConfig('linux-x64x11-clang-3-6')
+    return LinuxX64X11Clang36Configuration('linux-x64x11-clang-3-6')
   except RuntimeError as e:
     logging.critical(e)
     return None
diff --git a/src/starboard/linux/x64x11/clang/3.6/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/clang/3.6/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/clang/3.6/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/clang/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/clang/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/clang/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/egl/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/egl/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/egl/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/future/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/future/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/future/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
index c663185..1e84ea1 100644
--- a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
@@ -16,16 +16,15 @@
 import os
 import subprocess
 
-
 from starboard.linux.shared import gyp_configuration as shared_configuration
 from starboard.tools import build
 
 
-class PlatformConfig(shared_configuration.LinuxConfiguration):
+class LinuxX64X11Gcc63Configuration(shared_configuration.LinuxConfiguration):
   """Starboard Linux platform configuration."""
 
   def __init__(self, platform, asan_enabled_by_default=False):
-    super(PlatformConfig, self).__init__(
+    super(LinuxX64X11Gcc63Configuration, self).__init__(
         platform, asan_enabled_by_default, goma_supports_compiler=False)
 
     # Run the script that ensures gcc 6.3.0 is installed.
@@ -36,14 +35,20 @@
                                       'x86_64-linux-gnu-gcc-6.3.0', 'gcc')
 
   def GetVariables(self, configuration):
-    variables = super(PlatformConfig, self).GetVariables(configuration)
-    variables.update({'clang': 0,})
+    variables = super(LinuxX64X11Gcc63Configuration,
+                      self).GetVariables(configuration)
+    variables.update({
+        'clang': 0,
+    })
     toolchain_lib_path = os.path.join(self.toolchain_dir, 'lib64')
-    variables.update({'toolchain_lib_path': toolchain_lib_path,})
+    variables.update({
+        'toolchain_lib_path': toolchain_lib_path,
+    })
     return variables
 
   def GetEnvironmentVariables(self):
-    env_variables = super(PlatformConfig, self).GetEnvironmentVariables()
+    env_variables = super(LinuxX64X11Gcc63Configuration,
+                          self).GetEnvironmentVariables()
     toolchain_bin_dir = os.path.join(self.toolchain_dir, 'bin')
     env_variables.update({
         'CC': os.path.join(toolchain_bin_dir, 'gcc'),
@@ -53,4 +58,4 @@
 
 
 def CreatePlatformConfig():
-  return PlatformConfig('linux-x64x11-gcc-6-3')
+  return LinuxX64X11Gcc63Configuration('linux-x64x11-gcc-6-3')
diff --git a/src/starboard/linux/x64x11/gcc/6.3/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/gcc/6.3/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/gcc/6.3/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/gcc/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/gcc/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/gcc/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/gczeal/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/gczeal/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/gczeal/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/gyp_configuration.py b/src/starboard/linux/x64x11/gyp_configuration.py
index ed69825..50059e5 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gyp_configuration.py
@@ -25,7 +25,8 @@
 class LinuxX64X11Configuration(shared_configuration.LinuxConfiguration):
   """Starboard Linux X64 X11 platform configuration."""
 
-  def __init__(self, platform_name='linux-x64x11',
+  def __init__(self,
+               platform_name='linux-x64x11',
                asan_enabled_by_default=True,
                goma_supports_compiler=True):
     super(LinuxX64X11Configuration, self).__init__(
diff --git a/src/starboard/linux/x64x11/mock/configuration_public.h b/src/starboard/linux/x64x11/mock/configuration_public.h
index c4a5f03..2c07d9c 100644
--- a/src/starboard/linux/x64x11/mock/configuration_public.h
+++ b/src/starboard/linux/x64x11/mock/configuration_public.h
@@ -371,6 +371,13 @@
 // non-zero on platforms with webm/vp9 support.
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
 
+// Specifies whether this platform updates audio frames asynchronously.  In such
+// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
+// indicate the absolute time that the consumed audio frames are reported.
+// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
+// details.
+#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 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.
diff --git a/src/starboard/linux/x64x11/mock/gyp_configuration.py b/src/starboard/linux/x64x11/mock/gyp_configuration.py
index f3d5ded..3fe2a5b 100644
--- a/src/starboard/linux/x64x11/mock/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/mock/gyp_configuration.py
@@ -19,17 +19,19 @@
 from starboard.tools.testing import test_filter
 
 
-class PlatformConfig(platform_configuration.PlatformConfiguration):
+class LinuxX64X11MockConfiguration(
+    platform_configuration.PlatformConfiguration):
   """Starboard mock platform configuration."""
 
   def __init__(self, platform):
-    super(PlatformConfig, self).__init__(platform)
+    super(LinuxX64X11MockConfiguration, self).__init__(platform)
 
   def GetBuildFormat(self):
     return 'ninja,qtcreator_ninja'
 
   def GetVariables(self, configuration):
-    return super(PlatformConfig, self).GetVariables(configuration, use_clang=1)
+    return super(LinuxX64X11MockConfiguration, self).GetVariables(
+        configuration, use_clang=1)
 
   def GetGeneratorVariables(self, configuration):
     del configuration
@@ -52,13 +54,13 @@
     return env_variables
 
   def GetTestFilters(self):
-    filters = super(PlatformConfig, self).GetTestFilters()
+    filters = super(LinuxX64X11MockConfiguration, self).GetTestFilters()
     filters.extend([
-        test_filter.TestFilter(
-            'starboard_platform_tests', test_filter.FILTER_ALL),
+        test_filter.TestFilter('starboard_platform_tests',
+                               test_filter.FILTER_ALL),
     ])
     return filters
 
 
 def CreatePlatformConfig():
-  return PlatformConfig('linux-x64x11-mock')
+  return LinuxX64X11MockConfiguration('linux-x64x11-mock')
diff --git a/src/starboard/linux/x64x11/mock/starboard_platform.gyp b/src/starboard/linux/x64x11/mock/starboard_platform.gyp
index dd5621a..2e8b84a 100644
--- a/src/starboard/linux/x64x11/mock/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/mock/starboard_platform.gyp
@@ -101,13 +101,16 @@
         '<(DEPTH)/starboard/shared/stub/player_destroy.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_current_frame.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_info.cc',
+        '<(DEPTH)/starboard/shared/stub/player_get_info2.cc',
         '<(DEPTH)/starboard/shared/stub/player_output_mode_supported.cc',
         '<(DEPTH)/starboard/shared/stub/player_seek.cc',
+        '<(DEPTH)/starboard/shared/stub/player_seek2.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_bounds.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_playback_rate.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_volume.cc',
         '<(DEPTH)/starboard/shared/stub/player_write_end_of_stream.cc',
         '<(DEPTH)/starboard/shared/stub/player_write_sample.cc',
+        '<(DEPTH)/starboard/shared/stub/player_write_sample2.cc',
         '<(DEPTH)/starboard/shared/stub/socket_accept.cc',
         '<(DEPTH)/starboard/shared/stub/socket_bind.cc',
         '<(DEPTH)/starboard/shared/stub/socket_clear_last_error.cc',
@@ -201,6 +204,7 @@
         '<(DEPTH)/starboard/shared/stub/system_request_suspend.cc',
         '<(DEPTH)/starboard/shared/stub/system_request_unpause.cc',
         '<(DEPTH)/starboard/shared/stub/system_sort.cc',
+        '<(DEPTH)/starboard/shared/stub/system_supports_resume.cc',
         '<(DEPTH)/starboard/shared/stub/system_symbolize.cc',
         '<(DEPTH)/starboard/shared/stub/thread_create.cc',
         '<(DEPTH)/starboard/shared/stub/thread_create_local_key.cc',
diff --git a/src/starboard/linux/x64x11/mock/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/mock/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/mock/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/mozjs/gyp_configuration.py b/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
index f1b50e9..cfba8a5 100644
--- a/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
@@ -11,10 +11,24 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-"""Starboard Linux X64 X11 future platform configuration."""
+"""Starboard Linux X64 X11 mozjs platform configuration."""
 
 from starboard.linux.x64x11 import gyp_configuration as linux_configuration
 
 
+class LinuxX64X11MozjsConfiguration(
+    linux_configuration.LinuxX64X11Configuration):
+  """Starboard Linux X64 X11 mozjs platform configuration."""
+
+  def GetVariables(self, config_name):
+    variables = super(LinuxX64X11MozjsConfiguration,
+                      self).GetVariables(config_name)
+    variables.update({
+        'javascript_engine': 'mozjs-45',
+        'cobalt_enable_jit': 0,
+    })
+    return variables
+
+
 def CreatePlatformConfig():
-  return linux_configuration.LinuxX64X11Configuration('linux-x64x11-mozjs')
+  return LinuxX64X11MozjsConfiguration('linux-x64x11-mozjs')
diff --git a/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/mozjs/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/skia/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/skia/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
index 1a4a493..896e890 100644
--- a/src/starboard/linux/x64x11/starboard_platform_tests.gyp
+++ b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
@@ -1,4 +1,4 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2018 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.
@@ -13,39 +13,6 @@
 # limitations under the License.
 {
   'includes': [
-    '<(DEPTH)/starboard/shared/starboard/player/filter/testing/filter_tests.gypi',
-  ],
-  'targets': [
-    {
-      'target_name': 'starboard_platform_tests',
-      'type': '<(gtest_target_type)',
-      'includes': [
-        '<(DEPTH)/starboard/shared/starboard/media/media_tests.gypi',
-      ],
-      'sources': [
-        '<(DEPTH)/starboard/common/test_main.cc',
-        '<@(media_tests_sources)',
-      ],
-      'defines': [
-        # This allows the tests to include internal only header files.
-        'STARBOARD_IMPLEMENTATION',
-      ],
-      'dependencies': [
-        '<(DEPTH)/starboard/starboard.gyp:starboard',
-        '<(DEPTH)/testing/gmock.gyp:gmock',
-        '<(DEPTH)/testing/gtest.gyp:gtest',
-      ],
-    },
-    {
-      'target_name': 'starboard_platform_tests_deploy',
-      'type': 'none',
-      'dependencies': [
-        '<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:starboard_platform_tests',
-      ],
-      'variables': {
-        'executable_name': 'starboard_platform_tests',
-      },
-      'includes': [ '../../build/deploy.gypi' ],
-    },
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
   ],
 }
diff --git a/src/starboard/linux/x64x11/v8/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/v8/starboard_platform_tests.gyp
new file mode 100644
index 0000000..896e890
--- /dev/null
+++ b/src/starboard/linux/x64x11/v8/starboard_platform_tests.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 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': [
+    '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 51f9c0d..0dd1259 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -31,11 +31,13 @@
 
 // --- Types -----------------------------------------------------------------
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 // Time represented in 90KHz ticks.
 typedef int64_t SbMediaTime;
 
 #define SB_MEDIA_TIME_TO_SB_TIME(media) (media * 100 / 9)
 #define SB_TIME_TO_SB_MEDIA_TIME(time) (time * 9 / 100)
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 
 // Types of media component streams.
 typedef enum SbMediaType {
@@ -475,11 +477,13 @@
 #endif  // SB_API_VERSION >= 6
 } SbMediaAudioHeader;
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 // --- Constants -------------------------------------------------------------
 
-// TODO: remove kSbMediaTimeSecond.
+// TODO: remove entirely.
 // One second in SbMediaTime (90KHz ticks).
 #define kSbMediaTimeSecond ((SbMediaTime)(90000))
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 
 // --- Functions -------------------------------------------------------------
 
diff --git a/src/starboard/nplb/audio_sink_create_test.cc b/src/starboard/nplb/audio_sink_create_test.cc
index 7377caf..0d082af 100644
--- a/src/starboard/nplb/audio_sink_create_test.cc
+++ b/src/starboard/nplb/audio_sink_create_test.cc
@@ -35,7 +35,12 @@
   *is_eos_reached = false;
 }
 
-void ConsumeFramesFuncStub(int frames_consumed, void* context) {}
+void ConsumeFramesFuncStub(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                           SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                           void* context) {
+}
 
 }  // namespace
 
@@ -55,6 +60,34 @@
   SbAudioSinkDestroy(audio_sink);
 }
 
+#if SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+TEST(SbAudioSinkCreateTest, MultiSink) {
+  ASSERT_GE(SbAudioSinkGetMaxChannels(), 1);
+
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+
+  const int kMaxSinks = 16;
+  std::vector<SbAudioSink> created_sinks;
+  for (int i = 0; i < kMaxSinks; ++i) {
+    created_sinks.push_back(SbAudioSinkCreate(
+        frame_buffers.channels(),
+        SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+        frame_buffers.sample_type(), frame_buffers.storage_type(),
+        frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+        UpdateSourceStatusFuncStub, ConsumeFramesFuncStub,
+        reinterpret_cast<void*>(1)));
+    if (!SbAudioSinkIsValid(created_sinks[i])) {
+      created_sinks.pop_back();
+      break;
+    }
+  }
+  SB_DLOG(INFO) << "Created " << created_sinks.size() << " valid audio sinks";
+  for (auto sink : created_sinks) {
+    SbAudioSinkDestroy(sink);
+  }
+}
+#endif  // SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+
 TEST(SbAudioSinkCreateTest, SunnyDayAllCombinations) {
   std::vector<SbMediaAudioSampleType> sample_types;
   if (SbAudioSinkIsAudioSampleTypeSupported(
diff --git a/src/starboard/nplb/audio_sink_helpers.cc b/src/starboard/nplb/audio_sink_helpers.cc
index ab8cb5b..a1ff53c 100644
--- a/src/starboard/nplb/audio_sink_helpers.cc
+++ b/src/starboard/nplb/audio_sink_helpers.cc
@@ -176,7 +176,15 @@
   condition_variable_.Signal();
 }
 
-void AudioSinkTestEnvironment::OnConsumeFrames(int frames_consumed) {
+void AudioSinkTestEnvironment::OnConsumeFrames(int frames_consumed
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                               ,
+                                               SbTime frames_consumed_at
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                               ) {
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  SB_DCHECK(frames_consumed_at <= SbTimeGetMonotonicNow());
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   ScopedLock lock(mutex_);
   frames_consumed_ += frames_consumed;
   condition_variable_.Signal();
@@ -196,10 +204,17 @@
 
 // static
 void AudioSinkTestEnvironment::ConsumeFramesFunc(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                                 SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                                  void* context) {
   AudioSinkTestEnvironment* environment =
       reinterpret_cast<AudioSinkTestEnvironment*>(context);
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  environment->OnConsumeFrames(frames_consumed, frames_consumed_at);
+#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   environment->OnConsumeFrames(frames_consumed);
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 }
 
 }  // namespace nplb
diff --git a/src/starboard/nplb/audio_sink_helpers.h b/src/starboard/nplb/audio_sink_helpers.h
index 1acaebe..2929525 100644
--- a/src/starboard/nplb/audio_sink_helpers.h
+++ b/src/starboard/nplb/audio_sink_helpers.h
@@ -91,14 +91,22 @@
                             int* offset_in_frames,
                             bool* is_playing,
                             bool* is_eos_reached);
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  void OnConsumeFrames(int frames_consumed, SbTime frames_consumed_at);
+#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   void OnConsumeFrames(int frames_consumed);
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
   static void UpdateSourceStatusFunc(int* frames_in_buffer,
                                      int* offset_in_frames,
                                      bool* is_playing,
                                      bool* is_eos_reached,
                                      void* context);
-  static void ConsumeFramesFunc(int frames_consumed, void* context);
+  static void ConsumeFramesFunc(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                void* context);
   SbAudioSink sink_;
 
   AudioSinkTestFrameBuffers frame_buffers_;
diff --git a/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp b/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp
index cb30701..f7d1f6d 100644
--- a/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp
+++ b/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp
@@ -53,7 +53,7 @@
       'variables': {
         'executable_name': 'nplb_blitter_pixel_tests',
       },
-      'includes': [ '../../build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 80c0e30..c951670 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -291,7 +291,7 @@
       'variables': {
         'executable_name': 'nplb',
       },
-      'includes': [ '../build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index a32327c..d9d09ae 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <vector>
+
 #include "starboard/blitter.h"
 #include "starboard/decode_target.h"
 #include "starboard/player.h"
@@ -60,8 +62,11 @@
 
     SbPlayer player = SbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-        kSbMediaAudioCodecAac, SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid,
-        &audio_header, NULL, NULL, NULL,
+        kSbMediaAudioCodecAac,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+        SB_PLAYER_NO_DURATION,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+        kSbDrmSystemInvalid, &audio_header, NULL, NULL, NULL,
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
         NULL,
 #endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
@@ -93,8 +98,11 @@
 
     SbPlayer player = SbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-        kSbMediaAudioCodecNone, SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid,
-        NULL, NULL, NULL, NULL,
+        kSbMediaAudioCodecNone,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+        SB_PLAYER_NO_DURATION,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+        kSbDrmSystemInvalid, NULL, NULL, NULL, NULL,
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
         NULL,
 #endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
@@ -111,8 +119,100 @@
 }
 #endif  // SB_HAS(AUDIOLESS_VIDEO)
 
-#endif  // SB_HAS(PLAYER_WITH_URL)
+#if SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+TEST_F(SbPlayerTest, MultiPlayer) {
+  SbMediaAudioHeader audio_header;
 
+  audio_header.format_tag = 0xff;
+  audio_header.number_of_channels = 2;
+  audio_header.samples_per_second = 22050;
+  audio_header.block_alignment = 4;
+  audio_header.bits_per_sample = 32;
+  audio_header.audio_specific_config_size = 0;
+  audio_header.average_bytes_per_second = audio_header.samples_per_second *
+                                          audio_header.number_of_channels *
+                                          audio_header.bits_per_sample / 8;
+
+  SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
+
+  constexpr SbPlayerOutputMode kOutputModes[] = {
+      kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut};
+
+  constexpr SbMediaAudioCodec kAudioCodecs[] = {
+      kSbMediaAudioCodecNone,
+
+      kSbMediaAudioCodecAac, kSbMediaAudioCodecOpus, kSbMediaAudioCodecVorbis,
+  };
+
+  // TODO: turn this into a macro.
+  // Perform a check to determine if new audio codecs have been added to the
+  // SbMediaAudioCodec enum, but not the array |audio_codecs|. If the compiler
+  // warns about a missing case here, the value must be added to |audio_codecs|.
+  SbMediaAudioCodec audio_codec = kAudioCodecs[0];
+  switch (audio_codec) {
+    case kAudioCodecs[0]:
+    case kAudioCodecs[1]:
+    case kAudioCodecs[2]:
+    case kAudioCodecs[3]:
+      break;
+  }
+
+  constexpr SbMediaVideoCodec kVideoCodecs[] = {
+      kSbMediaVideoCodecNone,
+
+      kSbMediaVideoCodecH264,   kSbMediaVideoCodecH265, kSbMediaVideoCodecMpeg2,
+      kSbMediaVideoCodecTheora, kSbMediaVideoCodecVc1,  kSbMediaVideoCodecVp10,
+      kSbMediaVideoCodecVp8,    kSbMediaVideoCodecVp9,
+  };
+
+  // TODO: turn this into a macro.
+  // Perform a check to determine if new video codecs have been added to the
+  // SbMediaVideoCodec enum, but not the array |video_codecs|. If the compiler
+  // warns about a missing case here, the value must be added to |video_codecs|.
+  SbMediaVideoCodec video_codec = kVideoCodecs[0];
+  switch (video_codec) {
+    case kVideoCodecs[0]:
+    case kVideoCodecs[1]:
+    case kVideoCodecs[2]:
+    case kVideoCodecs[3]:
+    case kVideoCodecs[4]:
+    case kVideoCodecs[5]:
+    case kVideoCodecs[6]:
+    case kVideoCodecs[7]:
+    case kVideoCodecs[8]:
+      break;
+  }
+
+  const int kMaxPlayersPerConfig = 16;
+  std::vector<SbPlayer> created_players;
+  int number_of_players = 0;
+  for (int i = 0; i < kMaxPlayersPerConfig; ++i) {
+    for (int j = 0; j < SB_ARRAY_SIZE_INT(kOutputModes); ++j) {
+      for (int k = 0; k < SB_ARRAY_SIZE_INT(kAudioCodecs); ++k) {
+        for (int l = 0; l < SB_ARRAY_SIZE_INT(kVideoCodecs); ++l) {
+          created_players.push_back(SbPlayerCreate(
+              fake_graphics_context_provider_.window(), kVideoCodecs[l],
+              kAudioCodecs[k], kSbDrmSystemInvalid, &audio_header, NULL, NULL,
+              NULL, NULL, NULL, kOutputModes[j],
+              fake_graphics_context_provider_.decoder_target_provider()));
+          if (!SbPlayerIsValid(created_players.back())) {
+            created_players.pop_back();
+          }
+        }
+      }
+    }
+    if (created_players.size() == number_of_players) {
+      break;
+    }
+    number_of_players = created_players.size();
+  }
+  SB_DLOG(INFO) << "Created " << number_of_players << " players in total.";
+  for (auto player : created_players) {
+    SbPlayerDestroy(player);
+  }
+}
+#endif  // SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+#endif  // SB_HAS(PLAYER_WITH_URL)
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/player_create_with_url_test.cc b/src/starboard/nplb/player_create_with_url_test.cc
index 18632ea..3de57d0 100644
--- a/src/starboard/nplb/player_create_with_url_test.cc
+++ b/src/starboard/nplb/player_create_with_url_test.cc
@@ -39,10 +39,11 @@
     if (!SbPlayerOutputModeSupportedWithUrl(output_mode)) {
       continue;
     }
-    // TODO: change this URL to something that will create a valid player.
     char url[] = "about:blank";
-    SB_DLOG(ERROR) << "Creating player";
-    SbPlayer player = SbPlayerCreateWithUrl(url, window, SB_PLAYER_NO_DURATION,
+    SbPlayer player = SbPlayerCreateWithUrl(url, window,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+                                            SB_PLAYER_NO_DURATION,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                                             NULL, NULL, NULL, NULL);
 
     EXPECT_TRUE(SbPlayerIsValid(player));
@@ -57,6 +58,43 @@
   SbWindowDestroy(window);
 }
 
+#if SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+TEST(SbPlayerUrlTest, MultiPlayer) {
+  SbWindowOptions window_options;
+  SbWindowSetDefaultOptions(&window_options);
+
+  SbWindow window = SbWindowCreate(&window_options);
+  EXPECT_TRUE(SbWindowIsValid(window));
+
+  SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
+                                       kSbPlayerOutputModePunchOut};
+
+  for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
+    SbPlayerOutputMode output_mode = output_modes[i];
+    if (!SbPlayerOutputModeSupportedWithUrl(output_mode)) {
+      continue;
+    }
+    const int kMaxPlayers = 16;
+    std::vector<SbPlayer> created_players;
+    char url[] = "about:blank";
+    for (int j = 0; j < kMaxPlayers; ++j) {
+      created_players.push_back(
+          SbPlayerCreateWithUrl(url, window, NULL, NULL, NULL, NULL));
+      if (!SbPlayerIsValid(created_players[j])) {
+        created_players.pop_back();
+        break;
+      }
+    }
+    SB_DLOG(INFO) << "Created " << created_players.size()
+                  << " valid players for output mode " << output_mode;
+    for (auto player : created_players) {
+      SbPlayerDestroy(player);
+    }
+  }
+  SbWindowDestroy(window);
+}
+#endif  // SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+
 #endif  // SB_HAS(PLAYER_WITH_URL)
 
 }  // namespace
diff --git a/src/starboard/nplb/socket_send_to_test.cc b/src/starboard/nplb/socket_send_to_test.cc
index e1181ed..e4fae02 100644
--- a/src/starboard/nplb/socket_send_to_test.cc
+++ b/src/starboard/nplb/socket_send_to_test.cc
@@ -127,6 +127,82 @@
   EXPECT_TRUE(SbSocketDestroy(trio.server_socket));
 }
 
+// Tests the expectation that writing to a socket that is never drained
+// will result in that socket becoming full and thus will return a
+// kSbSocketPending status, which indicates that it is blocked.
+TEST_P(PairSbSocketSendToTest, RainyDaySendToSocketUntilBlocking) {
+  static const int kChunkSize = 1024;
+  // 1GB limit for sending data.
+  static const uint64_t kMaxTransferLimit = 1024 * 1024 * 1024;
+
+  scoped_ptr<ConnectedTrioWrapped> trio =
+      CreateAndConnectWrapped(GetServerAddressType(), GetClientAddressType(),
+                              GetPortNumberForTests(), kSocketTimeout);
+  // Push data into socket until it dies.
+  uint64_t num_bytes = 0;
+  while (num_bytes < kMaxTransferLimit) {
+    char buff[kChunkSize] = {};
+    int result = trio->client_socket->SendTo(buff, sizeof(buff), NULL);
+
+    if (result < 0) {
+      SbSocketError err = SbSocketGetLastError(trio->client_socket->socket());
+      EXPECT_EQ(kSbSocketPending, err);
+      return;
+    }
+
+    if (result == 0) {  // Connection dropped unexpectedly.
+      EXPECT_TRUE(false) << "Connection unexpectedly dropped.";
+    }
+
+    num_bytes += static_cast<uint64_t>(result);
+  }
+  EXPECT_TRUE(false) << "Max transfer rate reached.";
+}
+
+// Tests the expectation that killing a connection will cause the other
+// connected socket to fail to write. For sockets without socket connection
+// support this will show up as a generic error. Otherwise this will show
+// up as a connection reset error.
+TEST_P(PairSbSocketSendToTest, RainyDaySendToSocketConnectionReset) {
+  static const int kChunkSize = 1024;
+
+  scoped_ptr<ConnectedTrioWrapped> trio =
+      CreateAndConnectWrapped(GetServerAddressType(), GetClientAddressType(),
+                              GetPortNumberForTests(), kSocketTimeout);
+
+  // Kills the server, the client socket will have it's connection reset during
+  // one of the subsequent writes.
+  trio->server_socket.reset();
+
+  // Expect that after some retries the client socket will return that the
+  // connection will reset.
+  int kNumRetries = 1000;
+  for (int i = 0; i < kNumRetries; ++i) {
+    char buff[kChunkSize] = {};
+    SbThreadSleep(1);
+    int result = trio->client_socket->SendTo(buff, sizeof(buff), NULL);
+
+    if (result < 0) {
+      SbSocketError err = SbSocketGetLastError(trio->client_socket->socket());
+
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \
+    SB_API_VERSION >= 9
+      EXPECT_EQ(kSbSocketErrorConnectionReset, err)
+          << "Expected connection drop.";
+#else
+      EXPECT_EQ(kSbSocketErrorFailed, err);
+#endif
+      return;
+    }
+
+    if (result == 0) {
+      return;  // Other way in which the connection was reset.
+    }
+  }
+  ASSERT_TRUE(false) << "Connection was not dropped after "
+                     << kNumRetries << " tries.";
+}
+
 #if SB_HAS(IPV6)
 INSTANTIATE_TEST_CASE_P(
     SbSocketAddressTypes,
diff --git a/src/starboard/nplb/socket_waiter_wait_test.cc b/src/starboard/nplb/socket_waiter_wait_test.cc
index d70734a..dfad316 100644
--- a/src/starboard/nplb/socket_waiter_wait_test.cc
+++ b/src/starboard/nplb/socket_waiter_wait_test.cc
@@ -104,6 +104,20 @@
   EXPECT_EQ(&values, values.context);
   EXPECT_EQ(kSbSocketWaiterInterestWrite, values.ready_interests);
 
+  // Try again to make sure writable sockets are still writable
+  values.count = 0;
+  EXPECT_TRUE(SbSocketWaiterAdd(
+      waiter, trio.client_socket, &values, &TestSocketWaiterCallback,
+      kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite, false));
+
+  WaitShouldNotBlock(waiter);
+
+  EXPECT_EQ(1, values.count);  // Check that the callback was called once.
+  EXPECT_EQ(waiter, values.waiter);
+  EXPECT_EQ(trio.client_socket, values.socket);
+  EXPECT_EQ(&values, values.context);
+  EXPECT_EQ(kSbSocketWaiterInterestWrite, values.ready_interests);
+
   // The client socket should become ready to read after we write some data to
   // it.
   values.count = 0;
diff --git a/src/starboard/player.h b/src/starboard/player.h
index e0d2bba..47241dd 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -111,6 +111,7 @@
   kSbPlayerOutputModeInvalid,
 } SbPlayerOutputMode;
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 // Information about the current media playback state.
 typedef struct SbPlayerInfo {
   // The position of the playback head, as precisely as possible, in 90KHz ticks
@@ -169,6 +170,63 @@
   SbMediaTime buffer_duration_pts;
 #endif  // SB_HAS(PLAYER_WITH_URL)
 } SbPlayerInfo;
+#else  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+// Information about the current media playback state.
+typedef struct SbPlayerInfo2 {
+  // The position of the playback head, as precisely as possible, in
+  // microseconds.
+  SbTime current_media_timestamp;
+
+  // The known duration of the currently playing media stream, in microseconds.
+  SbTime duration;
+
+  // The result of getStartDate for the currently playing media stream, in
+  // microseconds since the epoch of January 1, 1601 UTC.
+  SbTime start_date;
+
+  // The width of the currently displayed frame, in pixels, or 0 if not provided
+  // by this player.
+  int frame_width;
+
+  // The height of the currently displayed frame, in pixels, or 0 if not
+  // provided by this player.
+  int frame_height;
+
+  // Whether playback is currently paused.
+  bool is_paused;
+
+  // The current player volume in [0, 1].
+  double volume;
+
+  // The number of video frames sent to the player since the creation of the
+  // player.
+  int total_video_frames;
+
+  // The number of video frames decoded but not displayed since the creation of
+  // the player.
+  int dropped_video_frames;
+
+  // The number of video frames that failed to be decoded since the creation of
+  // the player.
+  int corrupted_video_frames;
+
+  // The rate of playback.  The video is played back in a speed that is
+  // proportional to this.  By default it is 1.0 which indicates that the
+  // playback is at normal speed.  When it is greater than one, the video is
+  // played in a faster than normal speed.  When it is less than one, the video
+  // is played in a slower than normal speed.  Negative speeds are not
+  // supported.
+  double playback_rate;
+
+#if SB_HAS(PLAYER_WITH_URL)
+  // The position of the buffer head, as precisely as possible, in microseconds.
+  SbTime buffer_start_timestamp;
+
+  // The known duration of the currently playing media buffer, in microseconds.
+  SbTime buffer_duration;
+#endif  // SB_HAS(PLAYER_WITH_URL)
+} SbPlayerInfo2;
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 
 // An opaque handle to an implementation-private structure representing a
 // player.
@@ -272,7 +330,9 @@
 SB_EXPORT SbPlayer
 SbPlayerCreateWithUrl(const char* url,
                       SbWindow window,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                       SbMediaTime duration_pts,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                       SbPlayerStatusFunc player_status_func,
                       SbPlayerEncryptedMediaInitDataEncounteredCB
                           encrypted_media_init_data_encountered_cb,
@@ -308,7 +368,8 @@
 //   there is a mutex guarding calls into each |SbPlayer| instance.
 // - If there is a platform limitation on how many players can coexist
 //   simultaneously, then calls made to this function that attempt to exceed
-//   that limit will return |kSbPlayerInvalid|.
+//   that limit must return |kSbPlayerInvalid|. Multiple calls to SbPlayerCreate
+//   must not cause a crash.
 //
 // |window|: The window that will display the player. |window| can be
 //   |kSbWindowInvalid| for platforms where video is only displayed on a
@@ -326,8 +387,10 @@
 //   audio track.  In such case |audio_header| must be NULL.
 #endif  // SB_HAS(AUDIOLESS_VIDEO)
 //
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 // |duration_pts|: The expected media duration in 90KHz ticks (PTS). It may be
 //   set to |SB_PLAYER_NO_DURATION| for live streams.
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 //
 // |drm_system|: If the media stream has encrypted portions, then this
 //   parameter provides an appropriate DRM system, created with
@@ -381,12 +444,14 @@
 //   use the provider to create SbDecodeTargets on the renderer thread. A
 //   provider may not always be needed by the player, but if it is needed, and
 //   the provider is not given, the player will fail by returning
-//   kSbPlayerInvalid.
+//   |kSbPlayerInvalid|.
 SB_EXPORT SbPlayer
 SbPlayerCreate(SbWindow window,
                SbMediaVideoCodec video_codec,
                SbMediaAudioCodec audio_codec,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                SbMediaTime duration_pts,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                SbDrmSystem drm_system,
                const SbMediaAudioHeader* audio_header,
                SbPlayerDeallocateSampleFunc sample_deallocate_func,
@@ -418,6 +483,7 @@
 // |player|: The player to be destroyed.
 SB_EXPORT void SbPlayerDestroy(SbPlayer player);
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 // Tells the player to freeze playback (if playback has already started),
 // reset or flush the decoder pipeline, and go back to the Prerolling state.
 // The player should restart playback once it can display the frame at
@@ -444,9 +510,45 @@
 //   when SbPlayerSeek was called. To be very specific, once SbPlayerSeek has
 //   been called with ticket X, a client should ignore all
 //   |SbPlayerDecoderStatusFunc| calls that do not pass in ticket X.
+
 SB_EXPORT void SbPlayerSeek(SbPlayer player,
                             SbMediaTime seek_to_pts,
                             int ticket);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+// SbPlayerSeek2 is like the deprecated SbPlayerSeek, but accepts SbTime
+// |seek_to_timestamp| instead of SbMediaTime |seek_to_pts|.
+
+// Tells the player to freeze playback (if playback has already started),
+// reset or flush the decoder pipeline, and go back to the Prerolling state.
+// The player should restart playback once it can display the frame at
+// |seek_to_timestamp|, or the closest it can get. (Some players can only seek
+// to I-Frames, for example.)
+//
+// - Seek must be called before samples are sent when starting playback for
+//   the first time, or the client never receives the
+//   |kSbPlayerDecoderStateNeedsData| signal.
+// - A call to seek may interrupt another seek.
+// - After this function is called, the client should not send any more audio
+//   or video samples until |SbPlayerDecoderStatusFunc| is called back with
+//   |kSbPlayerDecoderStateNeedsData| for each required media type.
+//   |SbPlayerDecoderStatusFunc| is the |decoder_status_func| callback function
+//   that was specified when the player was created (SbPlayerCreate).
+//
+// |player|: The SbPlayer in which the seek operation is being performed.
+// |seek_to_timestamp|: The frame at which playback should begin.
+// |ticket|: A user-supplied unique ID that is be passed to all subsequent
+//   |SbPlayerDecoderStatusFunc| calls. (That is the |decoder_status_func|
+//   callback function specified when calling SbPlayerCreate.)
+//
+//   The |ticket| value is used to filter calls that may have been in flight
+//   when SbPlayerSeek2 was called. To be very specific, once SbPlayerSeek2 has
+//   been called with ticket X, a client should ignore all
+//   |SbPlayerDecoderStatusFunc| calls that do not pass in ticket X.
+
+SB_EXPORT void SbPlayerSeek2(SbPlayer player,
+                             SbTime seek_to_timestamp,
+                             int ticket);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 
 // Writes a single sample of the given media type to |player|'s input stream.
 // Its data may be passed in via more than one buffers.  The lifetime of
@@ -482,6 +584,7 @@
 //   |NULL|.
 // |sample_drm_info|: The DRM system for the media sample. This value is
 //   required for encrypted samples. Otherwise, it must be |NULL|.
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 SB_EXPORT void SbPlayerWriteSample(
     SbPlayer player,
     SbMediaType sample_type,
@@ -496,6 +599,54 @@
     SbMediaTime sample_pts,
     const SbMediaVideoSampleInfo* video_sample_info,
     const SbDrmSampleInfo* sample_drm_info);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+// SbPlayerWriteSample2 is like the deprecated SbPlayerWriteSample, but accepts
+// SbTime |sample_timestamp| instead of SbMediaTime |sample_pts|.
+
+// Writes a single sample of the given media type to |player|'s input stream.
+// Its data may be passed in via more than one buffers.  The lifetime of
+// |sample_buffers|, |sample_buffer_sizes|, |video_sample_info|, and
+// |sample_drm_info| (as well as member |subsample_mapping| contained inside it)
+// are not guaranteed past the call to SbPlayerWriteSample. That means that
+// before returning, the implementation must synchronously copy any information
+// it wants to retain from those structures.
+//
+// |player|: The player to which the sample is written.
+// |sample_type|: The type of sample being written. See the |SbMediaType|
+//   enum in media.h.
+// |sample_buffers|: A pointer to an array of buffers with
+//   |number_of_sample_buffers| elements that hold the data for this sample. The
+//   buffers are expected to be a portion of a bytestream of the codec type that
+//   the player was created with. The buffers should contain a sequence of whole
+//   NAL Units for video, or a complete audio frame.  |sample_buffers| cannot be
+//   assumed to live past the call into SbPlayerWriteSample(), so it must be
+//   copied if its content will be used after SbPlayerWriteSample() returns.
+// |sample_buffer_sizes|: A pointer to an array of sizes with
+//   |number_of_sample_buffers| elements.  Each of them specify the number of
+//   bytes in the corresponding buffer contained in |sample_buffers|.  None of
+//   them can be 0.  |sample_buffer_sizes| cannot be assumed to live past the
+//   call into SbPlayerWriteSample(), so it must be copied if its content will
+//   be used after SbPlayerWriteSample() returns.
+// |number_of_sample_buffers|: Specify the number of elements contained inside
+//   |sample_buffers| and |sample_buffer_sizes|.  It has to be at least one, or
+//   the call will be ignored.
+// |sample_timestamp|: The timestamp of the sample in microseconds. Note that
+//   samples MAY be written "slightly" out of order.
+// |video_sample_info|: Information about a video sample. This value is
+//   required if |sample_type| is |kSbMediaTypeVideo|. Otherwise, it must be
+//   |NULL|.
+// |sample_drm_info|: The DRM system for the media sample. This value is
+//   required for encrypted samples. Otherwise, it must be |NULL|.
+SB_EXPORT void SbPlayerWriteSample2(
+    SbPlayer player,
+    SbMediaType sample_type,
+    const void* const* sample_buffers,
+    const int* sample_buffer_sizes,
+    int number_of_sample_buffers,
+    SbTime sample_timestamp,
+    const SbMediaVideoSampleInfo* video_sample_info,
+    const SbDrmSampleInfo* sample_drm_info);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 
 // Writes a marker to |player|'s input stream of |stream_type| indicating that
 // there are no more samples for that media type for the remainder of this
@@ -554,6 +705,7 @@
 //   value of |1.0| means that it should be played at full volume.
 SB_EXPORT void SbPlayerSetVolume(SbPlayer player, double volume);
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 // Gets a snapshot of the current player state and writes it to
 // |out_player_info|. This function may be called very frequently and is
 // expected to be inexpensive.
@@ -561,6 +713,19 @@
 // |player|: The player about which information is being retrieved.
 // |out_player_info|: The information retrieved for the player.
 SB_EXPORT void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+// SbPlayerGetInfo2 is like the deprecated SbPlayerGetInfo, but accepts
+// SbPlayerInfo2* |out_player_info2| instead of SbPlayerInfo |out_player_info|.
+
+// Gets a snapshot of the current player state and writes it to
+// |out_player_info|. This function may be called very frequently and is
+// expected to be inexpensive.
+//
+// |player|: The player about which information is being retrieved.
+// |out_player_info|: The information retrieved for the player.
+SB_EXPORT void SbPlayerGetInfo2(SbPlayer player,
+                                SbPlayerInfo2* out_player_info2);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 
 // Given a player created with the kSbPlayerOutputModeDecodeToTexture
 // output mode, it will return a SbDecodeTarget representing the current frame
diff --git a/src/starboard/queue.h b/src/starboard/queue.h
index e333ef3..3e2b648 100644
--- a/src/starboard/queue.h
+++ b/src/starboard/queue.h
@@ -130,6 +130,16 @@
                  queue_.end());
   }
 
+  void Clear() {
+    ScopedLock lock(mutex_);
+    queue_.clear();
+  }
+
+  size_t Size() {
+    ScopedLock lock(mutex_);
+    return queue_.size();
+  }
+
  private:
   Mutex mutex_;
   ConditionVariable condition_;
diff --git a/src/starboard/raspi/2/__init__.py b/src/starboard/raspi/2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/starboard/raspi/2/__init__.py
diff --git a/src/starboard/raspi/2/gyp_configuration.py b/src/starboard/raspi/2/gyp_configuration.py
index 403e139..b816faa 100644
--- a/src/starboard/raspi/2/gyp_configuration.py
+++ b/src/starboard/raspi/2/gyp_configuration.py
@@ -16,5 +16,19 @@
 from starboard.raspi.shared import gyp_configuration as shared_configuration
 
 
+class Raspi2PlatformConfig(shared_configuration.RaspiPlatformConfig):
+
+  def __init__(self, platform):
+    super(Raspi2PlatformConfig, self).__init__(platform)
+
+  def GetVariables(self, config_name):
+    variables = super(Raspi2PlatformConfig, self).GetVariables(config_name)
+    variables.update({
+        'javascript_engine': 'v8',
+        'cobalt_enable_jit': 1,
+    })
+    return variables
+
+
 def CreatePlatformConfig():
-  return shared_configuration.RaspiPlatformConfig('raspi-2')
+  return Raspi2PlatformConfig('raspi-2')
diff --git a/src/starboard/raspi/2/mozjs/atomic_public.h b/src/starboard/raspi/2/mozjs/atomic_public.h
new file mode 100644
index 0000000..e9e2061
--- /dev/null
+++ b/src/starboard/raspi/2/mozjs/atomic_public.h
@@ -0,0 +1,20 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_RASPI_2_MOZJS_ATOMIC_PUBLIC_H_
+#define STARBOARD_RASPI_2_MOZJS_ATOMIC_PUBLIC_H_
+
+#include "starboard/raspi/2/atomic_public.h"
+
+#endif  // STARBOARD_RASPI_2_MOZJS_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/raspi/2/mozjs/configuration_public.h b/src/starboard/raspi/2/mozjs/configuration_public.h
new file mode 100644
index 0000000..59e2a1f
--- /dev/null
+++ b/src/starboard/raspi/2/mozjs/configuration_public.h
@@ -0,0 +1,25 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Raspberry PI 2 Raspbian.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_RASPI_2_MOZJS_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_RASPI_2_MOZJS_CONFIGURATION_PUBLIC_H_
+
+#include "starboard/raspi/2/configuration_public.h"
+
+#endif  // STARBOARD_RASPI_2_MOZJS_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/raspi/2/mozjs/gyp_configuration.gypi b/src/starboard/raspi/2/mozjs/gyp_configuration.gypi
new file mode 100644
index 0000000..a55faf6
--- /dev/null
+++ b/src/starboard/raspi/2/mozjs/gyp_configuration.gypi
@@ -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.
+
+{
+  'target_defaults': {
+    'default_configuration': 'raspi-2-mozjs_debug',
+    'configurations': {
+      'raspi-2-mozjs_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'raspi-2-mozjs_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'raspi-2-mozjs_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'raspi-2-mozjs_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '../architecture.gypi',
+    '../../shared/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/raspi/2/mozjs/gyp_configuration.py b/src/starboard/raspi/2/mozjs/gyp_configuration.py
new file mode 100644
index 0000000..2885e53
--- /dev/null
+++ b/src/starboard/raspi/2/mozjs/gyp_configuration.py
@@ -0,0 +1,38 @@
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 platform configuration for gyp_cobalt."""
+
+import importlib
+
+# pylint: disable=invalid-name
+Raspi2PlatformConfig = importlib.import_module(
+    'starboard.raspi.2.gyp_configuration').Raspi2PlatformConfig
+
+
+class Raspi2MozjsPlatformConfig(Raspi2PlatformConfig):
+
+  def __init__(self, platform):
+    super(Raspi2MozjsPlatformConfig, self).__init__(platform)
+
+  def GetVariables(self, config_name):
+    variables = super(Raspi2MozjsPlatformConfig, self).GetVariables(config_name)
+    variables.update({
+        'javascript_engine': 'mozjs-45',
+        'cobalt_enable_jit': 0,
+    })
+    return variables
+
+
+def CreatePlatformConfig():
+  return Raspi2MozjsPlatformConfig('raspi-2-mozjs')
diff --git a/src/starboard/raspi/2/mozjs/starboard_platform.gyp b/src/starboard/raspi/2/mozjs/starboard_platform.gyp
new file mode 100644
index 0000000..1eff12d
--- /dev/null
+++ b/src/starboard/raspi/2/mozjs/starboard_platform.gyp
@@ -0,0 +1,18 @@
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../../shared/starboard_platform.gypi',
+  ],
+}
diff --git a/src/starboard/raspi/2/mozjs/thread_types_public.h b/src/starboard/raspi/2/mozjs/thread_types_public.h
new file mode 100644
index 0000000..d3f0154
--- /dev/null
+++ b/src/starboard/raspi/2/mozjs/thread_types_public.h
@@ -0,0 +1,20 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/raspi/2/thread_types_public.h"
+
+#endif  // STARBOARD_RASPI_2_MOZJS_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/raspi/2/skia/gyp_configuration.py b/src/starboard/raspi/2/skia/gyp_configuration.py
index 6b4926b..0e2036b 100644
--- a/src/starboard/raspi/2/skia/gyp_configuration.py
+++ b/src/starboard/raspi/2/skia/gyp_configuration.py
@@ -13,21 +13,8 @@
 # limitations under the License.
 """Starboard Raspberry Pi 2 platform configuration for gyp_cobalt."""
 
-import logging
-import os
-import sys
-
-_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
-sys.path.insert(0, os.path.join(_SCRIPT_DIR, '../..'))
-
-# pylint: disable=g-import-not-at-top
-from shared.gyp_configuration import RaspiPlatformConfig
+from starboard.raspi.shared.gyp_configuration import RaspiPlatformConfig
 
 
 def CreatePlatformConfig():
-  try:
-    return RaspiPlatformConfig('raspi-2-skia')
-  except RuntimeError as e:
-    logging.critical(e)
-    return None
-
+  return RaspiPlatformConfig('raspi-2-skia')
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index ae146d4..4f07543 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -269,6 +269,13 @@
 // non-zero on platforms with webm/vp9 support.
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
 
+// Specifies whether this platform updates audio frames asynchronously.  In such
+// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
+// indicate the absolute time that the consumed audio frames are reported.
+// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
+// details.
+#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 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.
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index 78ed966..e7f27dc 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -90,21 +90,23 @@
 
   def GetTestFilters(self):
     filters = super(RaspiPlatformConfig, self).GetTestFilters()
-    filters.extend([
-        # The RasPi test devices don't have access to an IPV6 network, so
-        # disable the related tests.
-        test_filter.TestFilter(
-            'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.'
-            'SunnyDayDestination/1'),
-        test_filter.TestFilter(
-            'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.'
-            'SunnyDaySourceForDestination/1'),
-        test_filter.TestFilter(
-            'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.'
-            'SunnyDaySourceNotLoopback/1'),
-        test_filter.TestFilter('starboard_platform_tests',
-                               test_filter.FILTER_ALL),
-        test_filter.TestFilter('nplb_blitter_pixel_tests',
-                               test_filter.FILTER_ALL),
-    ])
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
     return filters
+
+  _FILTERED_TESTS = {
+      # The RasPi test devices don't have access to an IPV6 network, so
+      # disable the related tests.
+      'nplb': [
+          'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest'
+          '.SunnyDayDestination/1',
+          'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest'
+          '.SunnyDaySourceForDestination/1',
+          'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest'
+          '.SunnyDaySourceNotLoopback/1',
+      ],
+      'nplb_blitter_pixel_tests': [test_filter.FILTER_ALL],
+      # TODO: enable player_filter_tests.
+      'player_filter_tests': [test_filter.FILTER_ALL],
+      'starboard_platform_tests': [test_filter.FILTER_ALL],
+  }
diff --git a/src/starboard/raspi/shared/launcher.py b/src/starboard/raspi/shared/launcher.py
index ebf20ed..a1fea1d 100644
--- a/src/starboard/raspi/shared/launcher.py
+++ b/src/starboard/raspi/shared/launcher.py
@@ -141,9 +141,17 @@
         command, timeout=Launcher._PEXPECT_TIMEOUT)
     retry_count = 0
     while True:
+      expected_prompts = [
+          r'.*Are\syou\ssure.*',  # Fingerprint verification
+          r'\S+ password:',  # Password prompt
+      ]
       try:
-        self.pexpect_process.expect(r'\S+ password:')
-        break
+        i = self.pexpect_process.expect(expected_prompts)
+        if i == 0:
+          self.pexpect_process.sendline('yes')
+        else:
+          self.pexpect_process.sendline(Launcher._RASPI_PASSWORD)
+          break
       except pexpect.TIMEOUT:
         if self.shutdown_initiated.is_set():
           return
@@ -153,7 +161,6 @@
         if retry_count > Launcher._PEXPECT_PASSWORD_TIMEOUT_MAX_RETRIES:
           exc_info = sys.exc_info()
           raise exc_info[0], exc_info[1], exc_info[2]
-    self.pexpect_process.sendline(Launcher._RASPI_PASSWORD)
 
   def _PexpectReadLines(self):
     """Reads all lines from the pexpect process."""
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index 74040e6..d172430 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -91,7 +91,6 @@
         '<(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/dlmalloc/system_get_used_cpu_memory.cc',
         '<(DEPTH)/starboard/shared/gcc/atomic_gcc.h',
         '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc',
         '<(DEPTH)/starboard/shared/iso/character_is_digit.cc',
@@ -144,6 +143,7 @@
         '<(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',
@@ -318,10 +318,12 @@
         '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.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_seek2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
@@ -329,6 +331,7 @@
         '<(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/player_write_sample2.cc',
         '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
         '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
         '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc',
@@ -340,6 +343,7 @@
         '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
         '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
         '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_supports_resume.cc',
         '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
diff --git a/src/starboard/shared/posix/memory_flush.cc b/src/starboard/shared/posix/memory_flush.cc
index 61dadd9..202b081 100644
--- a/src/starboard/shared/posix/memory_flush.cc
+++ b/src/starboard/shared/posix/memory_flush.cc
@@ -18,6 +18,10 @@
 
 #include <iomanip>
 
+#if SB_IS(ARCH_MIPS)
+#include <sys/cachectl.h>
+#endif
+
 #include "starboard/log.h"
 
 #if !SB_CAN(MAP_EXECUTABLE_MEMORY)
@@ -33,6 +37,11 @@
                          << std::dec << result << "d)";
 #endif
 
+#if SB_IS(ARCH_MIPS)
+  _flush_cache(reinterpret_cast<char*>(memory), (size_t)size_bytes, BCACHE);
+  return;
+#endif
+
 #if !defined(__has_builtin)
 #define __has_builtin(a) (0)
 #endif
diff --git a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
index 59b4389..97db681 100644
--- a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
+++ b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
@@ -118,7 +118,11 @@
           std::min(kMaxFramesToConsumePerRequest, frames_in_buffer);
 
       SbThreadSleep(frames_to_consume * kSbTimeSecond / sampling_frequency_hz_);
-      consume_frame_func_(frames_to_consume, context_);
+      consume_frame_func_(frames_to_consume,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                          SbTimeGetMonotonicNow(),
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                          context_);
     } else {
       // Wait for five millisecond if we are paused.
       SbThreadSleep(kSbTimeMillisecond * 5);
diff --git a/src/starboard/shared/starboard/link_receiver.cc b/src/starboard/shared/starboard/link_receiver.cc
index 45b9d3f..a046f8b 100644
--- a/src/starboard/shared/starboard/link_receiver.cc
+++ b/src/starboard/shared/starboard/link_receiver.cc
@@ -316,8 +316,6 @@
     return;
   }
 
-  SB_LOG(INFO) << "LinkReceiver port: " << actual_port_;
-
   char port_string[32] = {0};
   SbStringFormatF(port_string, SB_ARRAY_SIZE(port_string), "%d", actual_port_);
   CreateTemporaryFile("link_receiver_port", port_string,
diff --git a/src/starboard/shared/starboard/media/media_support_internal.h b/src/starboard/shared/starboard/media/media_support_internal.h
index 94d78e8..01a07ca 100644
--- a/src/starboard/shared/starboard/media/media_support_internal.h
+++ b/src/starboard/shared/starboard/media/media_support_internal.h
@@ -41,6 +41,9 @@
                                        int64_t bitrate,
                                        int fps);
 
+SB_EXPORT bool IsVp9HwDecoderSupported();
+SB_EXPORT bool IsVp9GPUDecoderSupported();
+
 // Indicates whether this platform supports |audio_codec| at |bitrate|.
 // If |audio_codec| is not supported under any condition, this function
 // returns |false|.
diff --git a/src/starboard/shared/starboard/net_log.cc b/src/starboard/shared/starboard/net_log.cc
index dd74ceb..a176fe6 100644
--- a/src/starboard/shared/starboard/net_log.cc
+++ b/src/starboard/shared/starboard/net_log.cc
@@ -102,6 +102,26 @@
   JoinFunction join_function_;
 };
 
+std::string ToString(SbSocketError error) {
+  switch (error) {
+    case kSbSocketOk: { return "kSbSocketOk"; }
+    case kSbSocketPending: { return "kSbSocketErrorConnectionReset"; }
+    case kSbSocketErrorFailed: { return "kSbSocketErrorFailed"; }
+
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \
+  SB_API_VERSION >= 9
+    case kSbSocketErrorConnectionReset: {
+      return "kSbSocketErrorConnectionReset";
+    }
+#endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) ||
+      // SB_API_VERSION >= 9
+  }
+  SB_NOTREACHED() << "Unexpected case " << error;
+  std::stringstream ss;
+  ss << "Unknown-" << error;
+  return ss.str();
+}
+
 scoped_ptr<Socket> CreateListenSocket() {
   scoped_ptr<Socket> socket(
       new Socket(NET_LOG_IP_VERSION, kSbSocketProtocolTcp));
@@ -128,6 +148,131 @@
   return socket.Pass();
 }
 
+class BufferedSocketWriter {
+ public:
+  BufferedSocketWriter(int in_memory_buffer_size, int chunk_size)
+      : max_memory_buffer_size_(in_memory_buffer_size),
+        chunk_size_(chunk_size) {}
+
+  void Append(const char* data, size_t data_n) {
+    bool overflow = false;
+    log_mutex_.Acquire();
+    for (const char* curr = data; curr != data + data_n; ++curr) {
+      log_.push_back(*curr);
+      if (log_.size() > max_memory_buffer_size_) {
+        overflow = true;
+        log_.pop_front();
+      }
+    }
+    log_mutex_.Release();
+
+    SB_LOG_IF(ERROR, overflow) << "Net log dropped buffer data.";
+  }
+
+  void WaitUntilWritableOrConnectionReset(SbSocket sock) {
+    SbSocketWaiter waiter = SbSocketWaiterCreate();
+
+    struct F {
+      static void WakeUp(SbSocketWaiter waiter, SbSocket, void*, int) {
+        SbSocketWaiterWakeUp(waiter);
+      }
+    };
+
+    SbSocketWaiterAdd(waiter,
+                      sock,
+                      NULL,
+                      &F::WakeUp,
+                      kSbSocketWaiterInterestWrite,
+                      false);  // false means one shot.
+
+    SbSocketWaiterWait(waiter);
+    SbSocketWaiterRemove(waiter, sock);
+    SbSocketWaiterDestroy(waiter);
+  }
+
+  bool IsConnectionReset(SbSocketError err) {
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \
+    SB_API_VERSION >= 9
+    return err == kSbSocketErrorConnectionReset;
+#else
+    return false;
+#endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) ||
+        // SB_API_VERSION >= 9
+  }
+
+  // Will flush data through to the dest_socket. Returns |true| if
+  // flushed, else connection was dropped or an error occured.
+  bool Flush(SbSocket dest_socket) {
+    std::string curr_write_block;
+    while (TransferData(chunk_size_, &curr_write_block)) {
+      while (!curr_write_block.empty()) {
+        int bytes_to_write = static_cast<int>(curr_write_block.size());
+        int result = SbSocketSendTo(dest_socket, curr_write_block.c_str(),
+                                    bytes_to_write, NULL);
+
+        if (result < 0) {
+          SbSocketError err = SbSocketGetLastError(dest_socket);
+          SbSocketClearLastError(dest_socket);
+          if (err == kSbSocketPending) {
+            blocked_counts_.increment();
+            WaitUntilWritableOrConnectionReset(dest_socket);
+            continue;
+          } else if (IsConnectionReset(err)) {
+            return false;
+          } else {
+            SB_LOG(ERROR) << "An error happened while writing to socket: "
+                          << ToString(err);
+            return false;
+          }
+          break;
+        } else if (result == 0) {
+          // Socket has closed.
+          return false;
+        } else {
+          // Expected condition. Partial or full write was successful.
+          size_t bytes_written = static_cast<size_t>(result);
+          SB_DCHECK(bytes_written <= bytes_to_write);
+          curr_write_block.erase(0, bytes_written);
+        }
+      }
+    }
+    return true;
+  }
+
+  int32_t blocked_counts() const { return blocked_counts_.load(); }
+
+ private:
+  bool TransferData(size_t max_size, std::string* destination) {
+    ScopedLock lock_log(log_mutex_);
+    size_t log_size = log_.size();
+    if (log_size == 0) {
+      return false;
+    }
+
+    size_t size = std::min<size_t>(max_size, log_size);
+    std::deque<char>::iterator begin_it = log_.begin();
+    std::deque<char>::iterator end_it = begin_it;
+    std::advance(end_it, size);
+
+    destination->assign(begin_it, end_it);
+    log_.erase(begin_it, end_it);
+    return true;
+  }
+
+  void PrependData(const std::string& curr_write_block) {
+    ScopedLock lock_log(log_mutex_);
+    log_.insert(log_.begin(),
+                curr_write_block.begin(),
+                curr_write_block.end());
+  }
+
+  int max_memory_buffer_size_;
+  int chunk_size_;
+  Mutex log_mutex_;
+  std::deque<char> log_;
+  atomic_int32_t blocked_counts_;
+};
+
 // This class will listen to the provided socket for a client
 // connection. When a client connection is established, a
 // callback will be invoked.
@@ -168,7 +313,8 @@
 class NetLogServer {
  public:
   static NetLogServer* Instance();
-  NetLogServer() {
+  NetLogServer() : buffered_socket_writer_(NET_LOG_MAX_IN_MEMORY_BUFFER,
+                                           NET_LOG_SOCKET_SEND_SIZE) {
     ScopedLock lock(socket_mutex_);
     listen_socket_ = CreateListenSocket();
     ListenForClient();
@@ -206,7 +352,7 @@
   }
 
   void OnLog(const char* msg) {
-    AppendToLog(msg);
+    buffered_socket_writer_.Append(msg, SbStringGetLength(msg));
     writer_thread_sema_.Put();
   }
 
@@ -215,13 +361,24 @@
     writer_thread_.reset(nullptr);
     socket_listener_.reset();
 
-    InternalFlush();  // One last flush to the socket.
+    Flush();  // One last flush to the socket.
     ScopedLock lock(socket_mutex_);
     client_socket_.reset();
     listen_socket_.reset();
   }
 
-  bool Flush() { return InternalFlush(); }
+  // Return |true| if the data was written out to a connected socket,
+  // else |false| if
+  // 1. There was no connected client.
+  // 2. The connection was dropped.
+  // 3. Some other connection error happened.
+  bool Flush() {
+    ScopedLock lock(socket_mutex_);
+    if (!client_socket_) {
+      return false;
+    }
+    return buffered_socket_writer_.Flush(client_socket_->socket());
+  }
 
  private:
   void OnWriterTaskJoined() {
@@ -229,7 +386,6 @@
   }
 
   void WriterTask(Semaphore* /*joined_sema*/, atomic_bool* is_joined) {
-    std::string curr_log_data;
     while (true) {
       writer_thread_sema_.Take();
 
@@ -244,113 +400,15 @@
     }
   }
 
-  std::string ToString(SbSocketError error) {
-    switch (error) {
-      case kSbSocketOk: { return "kSbSocketOk"; }
-      case kSbSocketPending: { return "kSbSocketErrorConnectionReset"; }
-      case kSbSocketErrorFailed: { return "kSbSocketErrorFailed"; }
-
-#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \
-    SB_API_VERSION >= 9
-      case kSbSocketErrorConnectionReset: {
-        return "kSbSocketErrorConnectionReset";
-      }
-#endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) ||
-        // SB_API_VERSION >= 9
-    }
-    SB_NOTREACHED() << "Unexpected case " << error;
-    std::stringstream ss;
-    ss << "Unknown-" << error;
-    return ss.str();
-  }
-
-  // Return false if socket disconnected.
-  bool InternalFlush() {
-    ScopedLock lock(socket_mutex_);
-    if (!client_socket_) {
-      return false;
-    }
-    // Dummy read from client socket. This is required by the socket api.
-    char buff[2048];
-    client_socket_->ReceiveFrom(buff, sizeof(buff), NULL);
-    std::string curr_write_block;
-    while (TransferLog(NET_LOG_SOCKET_SEND_SIZE, &curr_write_block)) {
-      while (!curr_write_block.empty()) {
-        int bytes_to_write = static_cast<int>(curr_write_block.size());
-        int result = client_socket_->SendTo(curr_write_block.c_str(),
-                                            bytes_to_write,
-                                            NULL);
-        if (result < 0) {
-          SbSocketError err = client_socket_->GetLastError();
-          client_socket_->ClearLastError();
-          if (err == kSbSocketPending) {
-            // Buffer was full.
-            SbThreadSleep(kSbTimeMillisecond);
-            continue;
-          } else {
-            // Unexpected error. Clear the current write block to prevent
-            // endless loop.
-            SB_LOG(ERROR) << "An error happened while writing to socket: "
-                          << ToString(err);
-            curr_write_block.clear();
-          }
-          break;
-        } else if (result == 0) {
-          // Socket has closed.
-          return false;
-        } else {
-          // Expected condition. Partial or full write was successful.
-          size_t bytes_written = static_cast<size_t>(result);
-          SB_DCHECK(bytes_written <= bytes_to_write);
-          curr_write_block.erase(0, bytes_written);
-        }
-      }
-    }
-    return true;
-  }
-
-  bool TransferLog(size_t max_size, std::string* destination) {
-    ScopedLock lock_log(log_mutex_);
-    size_t log_size = log_.size();
-    if (log_size == 0) {
-      return false;
-    }
-
-    size_t size = std::min<size_t>(max_size, log_size);
-    std::deque<char>::iterator begin_it = log_.begin();
-    std::deque<char>::iterator end_it = begin_it;
-    std::advance(end_it, size);
-
-    destination->assign(begin_it, end_it);
-    log_.erase(begin_it, end_it);
-    return true;
-  }
-
-  void AppendToLog(const char* msg) {
-    size_t str_len = SbStringGetLength(msg);
-    bool overflow = false;
-    log_mutex_.Acquire();
-    for (const char* curr = msg; curr != msg + str_len; ++curr) {
-      log_.push_back(*curr);
-      if (log_.size() > NET_LOG_MAX_IN_MEMORY_BUFFER) {
-        overflow = true;
-        log_.pop_front();
-      }
-    }
-    log_mutex_.Release();
-
-    SB_LOG_IF(ERROR, overflow) << "Net log dropped buffer data.";
-  }
-
   scoped_ptr<Socket> listen_socket_;
   scoped_ptr<Socket> client_socket_;
   Mutex socket_mutex_;
-  std::deque<char> log_;
-  Mutex log_mutex_;
 
   scoped_ptr<SocketListener> socket_listener_;
   scoped_ptr<Thread> writer_thread_;
   Semaphore writer_thread_sema_;
+
+  BufferedSocketWriter buffered_socket_writer_;
 };
 
 class ThreadLocalBoolean {
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
index b2855ad..5294d10 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -81,7 +81,7 @@
       consume_frames_called_(false),
       seeking_(false),
       seeking_to_time_(0),
-      last_time_(0),
+      last_media_time_(0),
       frame_buffer_(max_cached_frames_ * bytes_per_frame_),
       frames_sent_to_sink_(0),
       pending_decoder_outputs_(0),
@@ -236,7 +236,7 @@
     ScopedLock scoped_lock(mutex_);
     eos_state_ = kEOSNotReceived;
     seeking_to_time_ = std::max<SbTime>(seek_to_time, 0);
-    last_time_ = seek_to_time;
+    last_media_time_ = seek_to_time;
     seeking_ = true;
   }
 
@@ -280,7 +280,7 @@
   SB_DCHECK(is_playing);
   SB_DCHECK(is_eos_played);
 
-  SbTime media_sb_time = 0;
+  SbTime media_time = 0;
   SbTimeMonotonic now = -1;
   SbTimeMonotonic elasped_since_last_set = 0;
   int64_t frames_played = 0;
@@ -299,6 +299,10 @@
     if (frames_consumed_by_sink_since_last_get_current_time_ > 0) {
       audio_frame_tracker_.RecordPlayedFrames(
           frames_consumed_by_sink_since_last_get_current_time_);
+#if SB_LOG_MEDIA_TIME_STATS
+      total_frames_consumed_ +=
+          frames_consumed_by_sink_since_last_get_current_time_;
+#endif  // SB_LOG_MEDIA_TIME_STATS
       frames_consumed_by_sink_since_last_get_current_time_ = 0;
     }
 
@@ -315,32 +319,35 @@
     frames_played =
         audio_frame_tracker_.GetFutureFramesPlayedAdjustedToPlaybackRate(
             elapsed_frames);
-    media_sb_time =
+    media_time =
         seeking_to_time_ + frames_played * kSbTimeSecond / samples_per_second;
-    if (media_sb_time < last_time_) {
-      SB_DLOG(WARNING) << "Audio time runs backwards from " << last_time_
-                       << " to " << media_sb_time;
-      media_sb_time = last_time_;
+    if (media_time < last_media_time_) {
+#if SB_LOG_MEDIA_TIME_STATS
+      SB_LOG(WARNING) << "Audio time runs backwards from " << last_media_time_
+                      << " to " << media_time;
+#endif  // SB_LOG_MEDIA_TIME_STATS
+      media_time = last_media_time_;
     }
-    last_time_ = media_sb_time;
+    last_media_time_ = media_time;
   }
 
 #if SB_LOG_MEDIA_TIME_STATS
   if (system_and_media_time_offset_ < 0 && frames_played > 0) {
-    system_and_media_time_offset_ = now - media_sb_time;
+    system_and_media_time_offset_ = now - media_time;
   }
   if (system_and_media_time_offset_ > 0) {
-    SbTime offset = now - media_sb_time;
+    SbTime offset = now - media_time;
     SbTime diff = std::abs(offset - system_and_media_time_offset_);
     max_offset_difference_ = std::max(diff, max_offset_difference_);
     SB_LOG(ERROR) << "Media time stats: (" << now << "-"
                   << frames_consumed_set_at_ << "=" << elasped_since_last_set
-                  << ") => " << frames_played << " => " << media_sb_time
-                  << "  diff: " << diff << "/" << max_offset_difference_;
+                  << ") + " << total_frames_consumed_ << " => " << frames_played
+                  << " => " << media_time << "  diff: " << diff << "/"
+                  << max_offset_difference_;
   }
 #endif  // SB_LOG_MEDIA_TIME_STATS
 
-  return media_sb_time;
+  return media_time;
 }
 
 void AudioRenderer::GetSourceStatus(int* frames_in_buffer,
@@ -369,27 +376,34 @@
   }
 }
 
-void AudioRenderer::ConsumeFrames(int frames_consumed) {
-  // Note that occasionally thread context switch may cause that the time
-  // recorded here is several milliseconds later than the time |frames_consumed|
-  // is recorded.  This causes the audio time to drift as much as the difference
-  // between the two times.
-  // This is usually not a huge issue as:
-  // 1. It happens rarely.
-  // 2. It doesn't accumulate.
-  // 3. It doesn't affect frame presenting even with a 60fps video.
-  // However, if this ever becomes a problem, we can smooth it out over multiple
-  // ConsumeFrames() calls.
-  auto system_time = SbTimeGetMonotonicNow();
+void AudioRenderer::ConsumeFrames(int frames_consumed
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                  ,
+                                  SbTime frames_consumed_at
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                  ) {
+// Note that occasionally thread context switch may cause that the time
+// recorded here is several milliseconds later than the time |frames_consumed|
+// is recorded.  This causes the audio time to drift as much as the difference
+// between the two times.
+// This is usually not a huge issue as:
+// 1. It happens rarely.
+// 2. It doesn't accumulate.
+// 3. It doesn't affect frame presenting even with a 60fps video.
+// However, if this ever becomes a problem, we can smooth it out over multiple
+// ConsumeFrames() calls.
+#if !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  SbTime frames_consumed_at = SbTimeGetMonotonicNow();
+#endif  // !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
   ScopedTryLock lock(mutex_);
   if (lock.is_locked()) {
     frames_consumed_on_sink_thread_ += frames_consumed;
 
-    UpdateVariablesOnSinkThread_Locked(system_time);
+    UpdateVariablesOnSinkThread_Locked(frames_consumed_at);
   } else {
     frames_consumed_on_sink_thread_ += frames_consumed;
-    frames_consumed_set_at_on_sink_thread_ = system_time;
+    frames_consumed_set_at_on_sink_thread_ = frames_consumed_at;
   }
 }
 
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index 7149d98..13a92e9 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -101,7 +101,7 @@
   bool consume_frames_called_;
   bool seeking_;
   SbTime seeking_to_time_;
-  SbTime last_time_;
+  SbTime last_media_time_;
   AudioFrameTracker audio_frame_tracker_;
 
   int64_t frames_sent_to_sink_;
@@ -118,7 +118,11 @@
                        int* offset_in_frames,
                        bool* is_playing,
                        bool* is_eos_reached) override;
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  void ConsumeFrames(int frames_consumed, SbTime frames_consumed_at) override;
+#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   void ConsumeFrames(int frames_consumed) override;
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
   void UpdateVariablesOnSinkThread_Locked(SbTime system_time_on_consume_frames);
 
@@ -169,6 +173,7 @@
 #if SB_LOG_MEDIA_TIME_STATS
   SbTime system_and_media_time_offset_ = -1;
   SbTime max_offset_difference_ = 0;
+  int64_t total_frames_consumed_ = 0;
 #endif  // SB_LOG_MEDIA_TIME_STATS
 };
 
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h b/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h
index 5f53e28..f4d2718 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h
@@ -16,6 +16,7 @@
 #define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_SINK_H_
 
 #include "starboard/audio_sink.h"
+#include "starboard/time.h"
 
 namespace starboard {
 namespace shared {
@@ -32,7 +33,12 @@
                                  int* offset_in_frames,
                                  bool* is_playing,
                                  bool* is_eos_reached) = 0;
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    virtual void ConsumeFrames(int frames_consumed,
+                               SbTime frames_consumed_at) = 0;
+#else   //  SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
     virtual void ConsumeFrames(int frames_consumed) = 0;
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
    protected:
     ~RenderCallback() {}
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc
index accfb4f..bd6d06c 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc
@@ -133,13 +133,21 @@
 
 // static
 void AudioRendererSinkImpl::ConsumeFramesFunc(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                              SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                               void* context) {
   AudioRendererSinkImpl* audio_renderer_sink =
       static_cast<AudioRendererSinkImpl*>(context);
   SB_DCHECK(audio_renderer_sink);
   SB_DCHECK(audio_renderer_sink->render_callback_);
 
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  audio_renderer_sink->render_callback_->ConsumeFrames(frames_consumed,
+                                                       frames_consumed_at);
+#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   audio_renderer_sink->render_callback_->ConsumeFrames(frames_consumed);
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 }
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h
index b6d67fb..0e18907 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h
@@ -59,7 +59,11 @@
                                      bool* is_playing,
                                      bool* is_eos_reached,
                                      void* context);
-  static void ConsumeFramesFunc(int frames_consumed, void* context);
+  static void ConsumeFramesFunc(int frames_consumed,
+#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                SbTime frames_consumed_at,
+#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+                                void* context);
 
   ThreadChecker thread_checker_;
   SbAudioSinkPrivate* audio_sink_;
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 36719a9..d03167c 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -384,7 +384,8 @@
   if ((*player_worker_.*get_player_state_cb_)() == kSbPlayerStatePrerolling) {
     bool audio_seek_in_progress =
         audio_renderer_ && audio_renderer_->IsSeekingInProgress();
-    if (!audio_seek_in_progress && !video_renderer_->IsSeekingInProgress()) {
+    if (!audio_seek_in_progress &&
+        !video_renderer_->UpdateAndRetrieveIsSeekingInProgress()) {
       (*player_worker_.*update_player_state_cb_)(kSbPlayerStatePresenting);
       if (!paused_) {
         GetMediaTimeProvider()->Play();
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
index b7067ed..215dad1 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -321,6 +321,7 @@
   EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
 }
 
+#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
 TEST_F(AudioRendererTest, SunnyDayWithDoublePlaybackRateAndInt16Samples) {
   const int kPlaybackRate = 2;
 
@@ -393,6 +394,7 @@
 
   EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
 }
+#endif  // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
 
 TEST_F(AudioRendererTest, StartPlayBeforePreroll) {
   {
diff --git a/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi b/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi
similarity index 87%
rename from src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi
rename to src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi
index e87511b..af39e56 100644
--- a/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi
+++ b/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi
@@ -15,7 +15,7 @@
 {
   'targets': [
     {
-      'target_name': 'filter_tests',
+      'target_name': 'player_filter_tests',
       'type': '<(gtest_target_type)',
       'sources': [
         '<(DEPTH)/starboard/common/test_main.cc',
@@ -39,11 +39,11 @@
         '<(DEPTH)/starboard/starboard.gyp:starboard',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
-        'filter_tests_copy_test_data',
+        'player_filter_tests_copy_test_data',
       ],
     },
     {
-      'target_name': 'filter_tests_copy_test_data',
+      'target_name': 'player_filter_tests_copy_test_data',
       'type': 'none',
       'variables': {
         'content_test_input_files': [
@@ -54,15 +54,15 @@
       'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'],
     },
     {
-      'target_name': 'filter_tests_deploy',
+      'target_name': 'player_filter_tests_deploy',
       'type': 'none',
       'dependencies': [
-        'filter_tests',
+        'player_filter_tests',
       ],
       'variables': {
-        'executable_name': 'filter_tests',
+        'executable_name': 'player_filter_tests',
       },
-      'includes': [ '../../../../../build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
index e434a4b..17ce97a 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
@@ -216,6 +216,7 @@
     return !event_queue_.empty();
   }
 
+#if SB_HAS(GLES2)
   void AssertValidDecodeTarget() {
     if (GetParam().output_mode == kSbPlayerOutputModeDecodeToTexture) {
       SbDecodeTarget decode_target = video_decoder_->GetCurrentDecodeTarget();
@@ -223,6 +224,7 @@
       fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target);
     }
   }
+#endif  // SB_HAS(GLES2)
 
   // This has to be called when the decoder is just initialized/reseted or when
   // status is |kNeedMoreInput|.
@@ -386,6 +388,7 @@
   }
 }
 
+#if SB_HAS(GLES2)
 TEST_P(VideoDecoderTest, GetCurrentDecodeTargetBeforeWriteInputBuffer) {
   if (GetParam().output_mode == kSbPlayerOutputModeDecodeToTexture) {
     SbDecodeTarget decode_target = video_decoder_->GetCurrentDecodeTarget();
@@ -393,6 +396,7 @@
     fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target);
   }
 }
+#endif  // SB_HAS(GLES2)
 
 TEST_P(VideoDecoderTest, ThreeMoreDecoders) {
   // Create three more decoders for each supported combinations.
@@ -439,18 +443,21 @@
               std::bind(&VideoDecoderTest::OnDecoderStatusUpdate, this, _1, _2),
               std::bind(&VideoDecoderTest::OnError, this));
 
+#if SB_HAS(GLES2)
           if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
             SbDecodeTarget decode_target =
                 video_decoders[i]->GetCurrentDecodeTarget();
             EXPECT_FALSE(SbDecodeTargetIsValid(decode_target));
             fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target);
           }
+#endif  // SB_HAS(GLES2)
         }
       }
     }
   }
 }
 
+#if SB_HAS(GLES2)
 TEST_P(VideoDecoderTest, SingleInput) {
   WriteSingleInput(0);
   WriteEndOfStream();
@@ -489,6 +496,7 @@
     AssertValidDecodeTarget();
   }
 }
+#endif  // SB_HAS(GLES2)
 
 TEST_P(VideoDecoderTest, EndOfStreamWithoutAnyInput) {
   WriteEndOfStream();
@@ -574,6 +582,7 @@
   ASSERT_FALSE(error_occurred);
 }
 
+#if SB_HAS(GLES2)
 TEST_P(VideoDecoderTest, DecodeFullGOP) {
   int gop_size = 1;
   while (gop_size < dmp_reader_.number_of_video_buffers()) {
@@ -608,6 +617,7 @@
       }));
   ASSERT_FALSE(error_occurred);
 }
+#endif  // SB_HAS(GLES2)
 
 std::vector<TestParam> GetSupportedTests() {
   SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture,
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
index 4aae342..dfdf8d6 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
@@ -141,8 +141,13 @@
          !end_of_stream_written_ && need_more_input_;
 }
 
-bool VideoRenderer::IsSeekingInProgress() const {
+bool VideoRenderer::UpdateAndRetrieveIsSeekingInProgress() {
   SB_DCHECK(thread_checker_.CalledOnValidThread());
+  ScopedLock lock(mutex_);
+  if (seeking_ && !frames_.empty()) {
+    auto elapsed = SbTimeGetMonotonicNow() - absolute_time_of_first_input_;
+    seeking_ = elapsed < decoder_->GetPrerollTimeout();
+  }
   return seeking_;
 }
 
@@ -170,13 +175,8 @@
       frames_.push_back(frame);
     }
 
-    if (seeking_ && !frames_.empty()) {
-      if (frames_.size() >= decoder_->GetPrerollFrameCount()) {
-        seeking_ = false;
-      } else {
-        auto elapsed = SbTimeGetMonotonicNow() - absolute_time_of_first_input_;
-        seeking_ = elapsed < decoder_->GetPrerollTimeout();
-      }
+    if (seeking_ && frames_.size() >= decoder_->GetPrerollFrameCount()) {
+      seeking_ = false;
     }
   }
 
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
index 61c55a7..e982ab5 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
@@ -63,7 +63,7 @@
   bool IsEndOfStreamWritten() const { return end_of_stream_written_; }
   bool IsEndOfStreamPlayed() const;
   bool CanAcceptMoreData() const;
-  bool IsSeekingInProgress() const;
+  bool UpdateAndRetrieveIsSeekingInProgress();
 
   SbDecodeTarget GetCurrentDecodeTarget();
 
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index 1d75d2d..c6cedd9 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -40,7 +40,9 @@
 SbPlayer SbPlayerCreate(SbWindow window,
                         SbMediaVideoCodec video_codec,
                         SbMediaAudioCodec audio_codec,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                         SbMediaTime duration_pts,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                         SbDrmSystem drm_system,
                         const SbMediaAudioHeader* audio_header,
                         SbPlayerDeallocateSampleFunc sample_deallocate_func,
@@ -53,6 +55,9 @@
                         SbPlayerOutputMode output_mode,
                         SbDecodeTargetGraphicsContextProvider* provider) {
   SB_UNREFERENCED_PARAMETER(window);
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  SB_UNREFERENCED_PARAMETER(duration_pts);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 
   const int64_t kDefaultBitRate = 0;
   if (audio_codec != kSbMediaAudioCodecNone &&
@@ -88,13 +93,12 @@
       new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
                                          audio_header, output_mode, provider));
 
-  SbPlayer player = new SbPlayerPrivate(
-      audio_codec, SB_MEDIA_TIME_TO_SB_TIME(duration_pts),
-      sample_deallocate_func, decoder_status_func, player_status_func,
+  SbPlayer player = new SbPlayerPrivate(audio_codec, sample_deallocate_func,
+                                        decoder_status_func, player_status_func,
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
-      player_error_func,
+                                        player_error_func,
 #endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
-      context, handler.Pass());
+                                        context, handler.Pass());
 
 #if SB_PLAYER_ENABLE_VIDEO_DUMPER
   using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
diff --git a/src/starboard/shared/starboard/player/player_get_info.cc b/src/starboard/shared/starboard/player/player_get_info.cc
index ebf7b2f..165c591 100644
--- a/src/starboard/shared/starboard/player/player_get_info.cc
+++ b/src/starboard/shared/starboard/player/player_get_info.cc
@@ -17,6 +17,7 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info) {
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
@@ -30,3 +31,4 @@
 
   player->GetInfo(out_player_info);
 }
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/starboard/player/player_get_info2.cc b/src/starboard/shared/starboard/player/player_get_info2.cc
new file mode 100644
index 0000000..c6c428c
--- /dev/null
+++ b/src/starboard/shared/starboard/player/player_get_info2.cc
@@ -0,0 +1,34 @@
+// Copyright 2018 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/player.h"
+
+#include "starboard/log.h"
+#include "starboard/shared/starboard/player/player_internal.h"
+
+#if SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+void SbPlayerGetInfo2(SbPlayer player, SbPlayerInfo2* out_player_info) {
+  if (!SbPlayerIsValid(player)) {
+    SB_DLOG(WARNING) << "player is invalid.";
+    return;
+  }
+
+  if (out_player_info == NULL) {
+    SB_DLOG(WARNING) << "out_player_info is NULL.";
+    return;
+  }
+
+  player->GetInfo(out_player_info);
+}
+#endif  // SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index ec5246e..a1d778a 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -29,7 +29,6 @@
 
 SbPlayerPrivate::SbPlayerPrivate(
     SbMediaAudioCodec audio_codec,
-    SbTime duration,
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
@@ -41,7 +40,6 @@
     : sample_deallocate_func_(sample_deallocate_func),
       context_(context),
       ticket_(SB_PLAYER_INITIAL_TICKET),
-      duration_(duration),
       media_time_(0),
       media_time_update_time_(SbTimeGetMonotonicNow()),
       frame_width_(0),
@@ -107,18 +105,32 @@
   // TODO: Wait until a frame is rendered with the updated bounds.
 }
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 void SbPlayerPrivate::GetInfo(SbPlayerInfo* out_player_info) {
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+void SbPlayerPrivate::GetInfo(SbPlayerInfo2* out_player_info) {
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   SB_DCHECK(out_player_info != NULL);
 
   starboard::ScopedLock lock(mutex_);
-  // TODO: change out_player_info to have duration (in SbTime).
-  out_player_info->duration_pts = SB_TIME_TO_SB_MEDIA_TIME(duration_);
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  out_player_info->duration_pts = SB_PLAYER_NO_DURATION;
   if (is_paused_) {
     out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME(media_time_);
   } else {
     out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME(
         GetMediaTime(media_time_, media_time_update_time_));
   }
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  out_player_info->duration = SB_PLAYER_NO_DURATION;
+  if (is_paused_) {
+    out_player_info->current_media_timestamp = media_time_;
+  } else {
+    out_player_info->current_media_timestamp =
+        GetMediaTime(media_time_, media_time_update_time_);
+  }
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+
   out_player_info->frame_width = frame_width_;
   out_player_info->frame_height = frame_height_;
   out_player_info->is_paused = is_paused_;
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h
index 9a85dd4..c26ced5 100644
--- a/src/starboard/shared/starboard/player/player_internal.h
+++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -31,7 +31,6 @@
 
   SbPlayerPrivate(
       SbMediaAudioCodec audio_codec,
-      SbTime duration,
       SbPlayerDeallocateSampleFunc sample_deallocate_func,
       SbPlayerDecoderStatusFunc decoder_status_func,
       SbPlayerStatusFunc player_status_func,
@@ -52,7 +51,11 @@
   void WriteEndOfStream(SbMediaType stream_type);
   void SetBounds(int z_index, int x, int y, int width, int height);
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   void GetInfo(SbPlayerInfo* out_player_info);
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+  void GetInfo(SbPlayerInfo2* out_player_info);
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
   void SetPause(bool pause);
   void SetPlaybackRate(double playback_rate);
   void SetVolume(double volume);
@@ -69,7 +72,6 @@
 
   starboard::Mutex mutex_;
   int ticket_;
-  SbTime duration_;
   SbTime media_time_;
   SbTimeMonotonic media_time_update_time_;
   int frame_width_;
diff --git a/src/starboard/shared/starboard/player/player_seek.cc b/src/starboard/shared/starboard/player/player_seek.cc
index 868a845..deb9b85 100644
--- a/src/starboard/shared/starboard/player/player_seek.cc
+++ b/src/starboard/shared/starboard/player/player_seek.cc
@@ -17,6 +17,7 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 void SbPlayerSeek(SbPlayer player, SbMediaTime seek_to_pts, int ticket) {
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
@@ -25,3 +26,4 @@
 
   player->Seek(SB_MEDIA_TIME_TO_SB_TIME(seek_to_pts), ticket);
 }
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/starboard/player/player_seek2.cc b/src/starboard/shared/starboard/player/player_seek2.cc
new file mode 100644
index 0000000..8b2b008
--- /dev/null
+++ b/src/starboard/shared/starboard/player/player_seek2.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 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/player.h"
+
+#include "starboard/log.h"
+#include "starboard/shared/starboard/player/player_internal.h"
+
+#if SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+void SbPlayerSeek2(SbPlayer player, SbTime seek_to_timestamp, int ticket) {
+  if (!SbPlayerIsValid(player)) {
+    SB_DLOG(WARNING) << "player is invalid.";
+    return;
+  }
+
+  player->Seek(seek_to_timestamp, ticket);
+}
+#endif  // SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/starboard/player/player_write_sample.cc b/src/starboard/shared/starboard/player/player_write_sample.cc
index 4cfa242..3fa2129 100644
--- a/src/starboard/shared/starboard/player/player_write_sample.cc
+++ b/src/starboard/shared/starboard/player/player_write_sample.cc
@@ -20,6 +20,7 @@
 #include "starboard/shared/starboard/player/video_dmp_writer.h"
 #endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 void SbPlayerWriteSample(SbPlayer player,
                          SbMediaType sample_type,
 #if SB_API_VERSION >= 6
@@ -68,3 +69,4 @@
                       number_of_sample_buffers, sample_timestamp,
                       video_sample_info, sample_drm_info);
 }
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/starboard/player/player_write_sample2.cc b/src/starboard/shared/starboard/player/player_write_sample2.cc
new file mode 100644
index 0000000..17dab34
--- /dev/null
+++ b/src/starboard/shared/starboard/player/player_write_sample2.cc
@@ -0,0 +1,65 @@
+// Copyright 2018 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/player.h"
+
+#include "starboard/log.h"
+#include "starboard/shared/starboard/player/player_internal.h"
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#include "starboard/shared/starboard/player/video_dmp_writer.h"
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
+
+#if SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+void SbPlayerWriteSample2(SbPlayer player,
+                          SbMediaType sample_type,
+                          const void* const* sample_buffers,
+                          const int* sample_buffer_sizes,
+                          int number_of_sample_buffers,
+                          SbTime sample_timestamp,
+                          const SbMediaVideoSampleInfo* video_sample_info,
+                          const SbDrmSampleInfo* sample_drm_info) {
+  if (!SbPlayerIsValid(player)) {
+    SB_DLOG(WARNING) << "player is invalid.";
+    return;
+  }
+
+  if (number_of_sample_buffers < 1) {
+    SB_DLOG(WARNING) << "SbPlayerWriteSample() doesn't support"
+                     << " |number_of_sample_buffers| less than one.";
+    return;
+  }
+
+  if (sample_buffers == NULL) {
+    SB_DLOG(WARNING) << "|sample_buffers| cannot be NULL";
+    return;
+  }
+
+  if (sample_buffer_sizes == NULL) {
+    SB_DLOG(WARNING) << "|sample_buffer_sizes| cannot be NULL";
+    return;
+  }
+
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+  using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
+  VideoDmpWriter::OnPlayerWriteSample(
+      player, sample_type, sample_buffers, sample_buffer_sizes,
+      number_of_sample_buffers, sample_timestamp, video_sample_info,
+      sample_drm_info);
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
+
+  player->WriteSample(sample_type, sample_buffers, sample_buffer_sizes,
+                      number_of_sample_buffers, sample_timestamp,
+                      video_sample_info, sample_drm_info);
+}
+#endif  // SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/starboard/system_supports_resume.cc b/src/starboard/shared/starboard/system_supports_resume.cc
new file mode 100644
index 0000000..c9d4755
--- /dev/null
+++ b/src/starboard/shared/starboard/system_supports_resume.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 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/system.h"
+
+#if SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
+
+bool SbSystemSupportsResume() {
+  return true;
+}
+
+#endif  // SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc
index e61f14e..ea6f6de 100644
--- a/src/starboard/shared/stub/player_create.cc
+++ b/src/starboard/shared/stub/player_create.cc
@@ -17,7 +17,9 @@
 SbPlayer SbPlayerCreate(SbWindow /*window*/,
                         SbMediaVideoCodec /*video_codec*/,
                         SbMediaAudioCodec /*audio_codec*/,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                         SbMediaTime /*duration_pts*/,
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
                         SbDrmSystem /*drm_system*/,
                         const SbMediaAudioHeader* /*audio_header*/,
                         SbPlayerDeallocateSampleFunc /*sample_deallocate_func*/,
diff --git a/src/starboard/shared/stub/player_get_info.cc b/src/starboard/shared/stub/player_get_info.cc
index 0e9ed89..7ff6f43 100644
--- a/src/starboard/shared/stub/player_get_info.cc
+++ b/src/starboard/shared/stub/player_get_info.cc
@@ -14,4 +14,6 @@
 
 #include "starboard/player.h"
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 void SbPlayerGetInfo(SbPlayer /*player*/, SbPlayerInfo* /*out_player_info*/) {}
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/stub/player_get_info2.cc b/src/starboard/shared/stub/player_get_info2.cc
new file mode 100644
index 0000000..d877e9c
--- /dev/null
+++ b/src/starboard/shared/stub/player_get_info2.cc
@@ -0,0 +1,20 @@
+// Copyright 2018 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/player.h"
+
+#if SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+void SbPlayerGetInfo2(SbPlayer /*player*/, SbPlayerInfo2* /*out_player_info*/) {
+}
+#endif  // SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/stub/player_seek.cc b/src/starboard/shared/stub/player_seek.cc
index 3d4f704..d027690 100644
--- a/src/starboard/shared/stub/player_seek.cc
+++ b/src/starboard/shared/stub/player_seek.cc
@@ -14,6 +14,8 @@
 
 #include "starboard/player.h"
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 void SbPlayerSeek(SbPlayer /*player*/,
                   SbMediaTime /*seek_to_pts*/,
                   int /*ticket*/) {}
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/stub/player_seek2.cc b/src/starboard/shared/stub/player_seek2.cc
new file mode 100644
index 0000000..50a3122
--- /dev/null
+++ b/src/starboard/shared/stub/player_seek2.cc
@@ -0,0 +1,21 @@
+// Copyright 2018 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/player.h"
+
+#if SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+void SbPlayerSeek2(SbPlayer /*player*/,
+                   SbTime /*seek_to_timestamp*/,
+                   int /*ticket*/) {}
+#endif  // SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/stub/player_write_sample.cc b/src/starboard/shared/stub/player_write_sample.cc
index d1fecd8..7f65a00 100644
--- a/src/starboard/shared/stub/player_write_sample.cc
+++ b/src/starboard/shared/stub/player_write_sample.cc
@@ -14,6 +14,7 @@
 
 #include "starboard/player.h"
 
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
 void SbPlayerWriteSample(SbPlayer /*player*/,
                          SbMediaType /*sample_type*/,
 #if SB_API_VERSION >= 6
@@ -28,3 +29,4 @@
                          const SbMediaVideoSampleInfo* /*video_sample_info*/,
                          const SbDrmSampleInfo* /*sample_drm_info*/) {
 }
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/stub/player_write_sample2.cc b/src/starboard/shared/stub/player_write_sample2.cc
new file mode 100644
index 0000000..3cca469
--- /dev/null
+++ b/src/starboard/shared/stub/player_write_sample2.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 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/player.h"
+
+#if SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+void SbPlayerWriteSample2(SbPlayer /*player*/,
+                          SbMediaType /*sample_type*/,
+                          const void* const* /*sample_buffers*/,
+                          const int* /*sample_buffer_sizes*/,
+                          int /*number_of_sample_buffers*/,
+                          SbTime /*sample_timestamp*/,
+                          const SbMediaVideoSampleInfo* /*video_sample_info*/,
+                          const SbDrmSampleInfo* /*sample_drm_info*/) {}
+#endif  // SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
diff --git a/src/starboard/shared/stub/system_supports_resume.cc b/src/starboard/shared/stub/system_supports_resume.cc
new file mode 100644
index 0000000..c9d4755
--- /dev/null
+++ b/src/starboard/shared/stub/system_supports_resume.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 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/system.h"
+
+#if SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
+
+bool SbSystemSupportsResume() {
+  return true;
+}
+
+#endif  // SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc
index a488e6a..76da8a2 100644
--- a/src/starboard/shared/uwp/application_uwp.cc
+++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -337,6 +337,12 @@
   void OnActivated(
       CoreApplicationView^ application_view, IActivatedEventArgs^ args) {
     SB_LOG(INFO) << "OnActivated";
+    ApplicationUwp* application_uwp = ApplicationUwp::Get();
+    Microsoft::WRL::ComPtr<ID3D12Device> device;
+    HRESULT hr =
+        D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
+    SB_DCHECK(SUCCEEDED(hr));
+    application_uwp->SetD3D12Device(device);
     std::string start_url = entry_point_;
     bool command_line_set = false;
 
@@ -356,7 +362,6 @@
       // The starboard: scheme provides commandline arguments, but that's
       // only allowed during a process's first activation.
       std::string scheme = sbwin32::platformStringToString(uri->SchemeName);
-
       if (!previously_activated_ && ends_with(scheme, "-starboard")) {
         std::string uri_string = wchar_tToUTF8(uri->RawUri->Data());
         // args_ is a vector of std::string, but argv_ is a vector of
@@ -445,7 +450,6 @@
             static_cast<int>(argv_.size()), argv_.data());
       }
 
-      ApplicationUwp* application_uwp = ApplicationUwp::Get();
       const CommandLine* command_line =
           ::starboard::shared::uwp::GetCommandLinePointer(application_uwp);
 
diff --git a/src/starboard/shared/uwp/application_uwp.h b/src/starboard/shared/uwp/application_uwp.h
index 8727d5c..5e5de59 100644
--- a/src/starboard/shared/uwp/application_uwp.h
+++ b/src/starboard/shared/uwp/application_uwp.h
@@ -15,7 +15,9 @@
 #ifndef STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
 #define STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
 
+#include <D3D12.h>
 #include <agile.h>
+
 #include <string>
 #include <unordered_map>
 
@@ -112,6 +114,12 @@
   // Returns true on success.
   bool TurnOffHdcp();
 
+  Microsoft::WRL::ComPtr<ID3D12Device> GetD3D12Device() { return d3d12device_; }
+
+  void SetD3D12Device(const Microsoft::WRL::ComPtr<ID3D12Device>& device) {
+    d3d12device_ = device;
+  }
+
  private:
   // --- Application overrides ---
   bool IsStartImmediate() override { return false; }
@@ -147,6 +155,7 @@
   std::unordered_map<SbEventId, Windows::System::Threading::ThreadPoolTimer^>
       timer_event_map_;
 
+  Microsoft::WRL::ComPtr<ID3D12Device> d3d12device_;
   int device_id_;
 
   // |hdcp_session_| is locked by |hdcp_session_mutex_|.
diff --git a/src/starboard/shared/uwp/decode_target_internal.cc b/src/starboard/shared/uwp/decode_target_internal.cc
new file mode 100644
index 0000000..3ed4863
--- /dev/null
+++ b/src/starboard/shared/uwp/decode_target_internal.cc
@@ -0,0 +1,383 @@
+// Copyright 2018 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/shared/uwp/decode_target_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "third_party/angle/include/EGL/egl.h"
+#include "third_party/angle/include/EGL/eglext.h"
+#include "third_party/angle/include/GLES2/gl2.h"
+#include "third_party/angle/include/GLES2/gl2ext.h"
+
+namespace {
+
+using Microsoft::WRL::ComPtr;
+using starboard::shared::win32::CheckResult;
+
+// {3C3A43AB-C69B-46C9-AA8D-B0CFFCD4596D}
+const GUID kCobaltNv12BindChroma = {
+    0x3c3a43ab,
+    0xc69b,
+    0x46c9,
+    {0xaa, 0x8d, 0xb0, 0xcf, 0xfc, 0xd4, 0x59, 0x6d}};
+
+// {C62BF18D-B5EE-46B1-9C31-F61BD8AE3B0D}
+const GUID kCobaltDxgiBuffer = {
+    0Xc62bf18d,
+    0Xb5ee,
+    0X46b1,
+    {0X9c, 0X31, 0Xf6, 0X1b, 0Xd8, 0Xae, 0X3b, 0X0d}};
+
+ComPtr<ID3D11Texture2D> CreateEmptyTexture(
+    const ComPtr<ID3D11Device>& d3d_device,
+    int width,
+    int height) {
+  ComPtr<ID3D11Texture2D> texture;
+  D3D11_TEXTURE2D_DESC texture_desc = {};
+  texture_desc.Width = width;
+  texture_desc.Height = height;
+  texture_desc.MipLevels = 1;
+  texture_desc.ArraySize = 1;
+  texture_desc.Format = DXGI_FORMAT_NV12;
+  texture_desc.SampleDesc.Count = 1;
+  texture_desc.SampleDesc.Quality = 0;
+  texture_desc.Usage = D3D11_USAGE_DEFAULT;
+  texture_desc.BindFlags =
+      D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+  CheckResult(d3d_device->CreateTexture2D(&texture_desc, nullptr,
+                                          texture.GetAddressOf()));
+  return texture;
+}
+
+void UpdateTexture(
+    const ComPtr<ID3D11Texture2D>& texture,
+    const ComPtr<ID3D11VideoDevice1>& video_device,
+    const ComPtr<ID3D11VideoContext>& video_context,
+    const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
+    const ComPtr<ID3D11VideoProcessor>& video_processor,
+    const ComPtr<IMFSample>& video_sample,
+    const RECT& video_area) {
+  ComPtr<IMFMediaBuffer> media_buffer;
+  CheckResult(video_sample->GetBufferByIndex(0, media_buffer.GetAddressOf()));
+
+  ComPtr<IMFDXGIBuffer> dxgi_buffer;
+  CheckResult(media_buffer.As(&dxgi_buffer));
+
+  ComPtr<ID3D11Texture2D> input_texture;
+  CheckResult(dxgi_buffer->GetResource(IID_PPV_ARGS(&input_texture)));
+
+  // The VideoProcessor needs to know what subset of the decoded
+  // frame contains active pixels that should be displayed to the user.
+  video_context->VideoProcessorSetStreamSourceRect(video_processor.Get(), 0,
+                                                   TRUE, &video_area);
+
+  D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_desc = {};
+  input_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
+  input_desc.Texture2D.MipSlice = 0;
+  dxgi_buffer->GetSubresourceIndex(&input_desc.Texture2D.ArraySlice);
+
+  ComPtr<ID3D11VideoProcessorInputView> input_view;
+  CheckResult(video_device->CreateVideoProcessorInputView(
+      input_texture.Get(), video_enumerator.Get(), &input_desc,
+      input_view.GetAddressOf()));
+
+  D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC output_desc = {};
+  output_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
+  output_desc.Texture2D.MipSlice = 0;
+
+  ComPtr<ID3D11VideoProcessorOutputView> output_view;
+  CheckResult(video_device->CreateVideoProcessorOutputView(
+      texture.Get(), video_enumerator.Get(), &output_desc,
+      output_view.GetAddressOf()));
+
+  // We have a single video stream, which is enabled for display.
+  D3D11_VIDEO_PROCESSOR_STREAM stream_info = {};
+  stream_info.Enable = TRUE;
+  stream_info.pInputSurface = input_view.Get();
+  CheckResult(video_context->VideoProcessorBlt(
+      video_processor.Get(), output_view.Get(), 0, 1, &stream_info));
+}
+
+ComPtr<ID3D11Texture2D> CreateVPXTexture(const ComPtr<ID3D11Device>& d3d_device,
+                                         const vpx_image_t* img,
+                                         BYTE* frame_buf_nv12) {
+  ComPtr<ID3D11Texture2D> texture;
+  D3D11_TEXTURE2D_DESC texture_desc = {};
+  texture_desc.Width = img->w;
+  texture_desc.Height = img->h;
+  texture_desc.MipLevels = 1;
+  texture_desc.ArraySize = 1;
+  texture_desc.Format = DXGI_FORMAT_NV12;
+  texture_desc.SampleDesc.Count = 1;
+  texture_desc.SampleDesc.Quality = 0;
+  texture_desc.Usage = D3D11_USAGE_DEFAULT;
+  texture_desc.BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE;
+
+  BYTE* pData = frame_buf_nv12;
+  SbMemoryCopy(pData, img->planes[VPX_PLANE_Y], img->w * img->h);
+  pData += texture_desc.Width * texture_desc.Height;
+
+  unsigned int j = 0;
+  for (unsigned int i = 0; i < (img->w * img->h / 4); i++) {
+    pData[j++] = img->planes[VPX_PLANE_U][i];
+    pData[j++] = img->planes[VPX_PLANE_V][i];
+  }
+
+  D3D11_SUBRESOURCE_DATA tSData;
+  tSData.pSysMem = frame_buf_nv12;
+  tSData.SysMemPitch = img->w;
+  tSData.SysMemSlicePitch = img->w * img->h + (img->w * img->h / 2);
+  CheckResult(d3d_device->CreateTexture2D(&texture_desc, &tSData,
+                                          texture.GetAddressOf()));
+  return texture;
+}
+
+}  // namespace
+void SbDecodeTargetPrivate::CreateGLSurfacesNV12(
+    const ComPtr<ID3D11Texture2D>& texture,
+    int display_width,
+    int display_height) {
+  SbDecodeTargetInfoPlane* planeY = &(info.planes[kSbDecodeTargetPlaneY]);
+  SbDecodeTargetInfoPlane* planeUV = &(info.planes[kSbDecodeTargetPlaneUV]);
+
+  planeY->width = info.width;
+  planeY->height = info.height;
+  planeY->content_region.left = 0;
+  planeY->content_region.top = display_height;
+  planeY->content_region.right = display_width;
+  planeY->content_region.bottom = 0;
+
+  planeUV->width = info.width / 2;
+  planeUV->height = info.height / 2;
+  planeUV->content_region.left = planeY->content_region.left / 2;
+  planeUV->content_region.top = planeY->content_region.top / 2;
+  planeUV->content_region.right = planeY->content_region.right / 2;
+  planeUV->content_region.bottom = planeY->content_region.bottom / 2;
+
+  EGLint luma_texture_attributes[] = {EGL_WIDTH,
+                                      static_cast<EGLint>(info.width),
+                                      EGL_HEIGHT,
+                                      static_cast<EGLint>(info.height),
+                                      EGL_TEXTURE_TARGET,
+                                      EGL_TEXTURE_2D,
+                                      EGL_TEXTURE_FORMAT,
+                                      EGL_TEXTURE_RGBA,
+                                      EGL_NONE};
+
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+  EGLConfig config;
+  EGLint attribute_list[] = {EGL_SURFACE_TYPE,  // this must be first
+                             EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+                             EGL_RED_SIZE,
+                             8,
+                             EGL_GREEN_SIZE,
+                             8,
+                             EGL_BLUE_SIZE,
+                             8,
+                             EGL_ALPHA_SIZE,
+                             8,
+                             EGL_BIND_TO_TEXTURE_RGBA,
+                             EGL_TRUE,
+                             EGL_RENDERABLE_TYPE,
+                             EGL_OPENGL_ES2_BIT,
+                             EGL_NONE};
+
+  EGLint num_configs;
+  bool ok = eglChooseConfig(display, attribute_list, &config, 1, &num_configs);
+  SB_DCHECK(ok);
+  SB_DCHECK(num_configs == 1);
+
+  GLuint gl_textures[2] = {0};
+  glGenTextures(2, gl_textures);
+  SB_DCHECK(glGetError() == GL_NO_ERROR);
+
+  // This tells ANGLE that the texture it creates should draw
+  // the luma channel on R8.
+  HRESULT hr = texture->SetPrivateData(kCobaltNv12BindChroma, 0, nullptr);
+  SB_DCHECK(SUCCEEDED(hr));
+
+  surface[0] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
+                                                texture.Get(), config,
+                                                luma_texture_attributes);
+
+  SB_DCHECK(surface[0] != EGL_NO_SURFACE);
+
+  glBindTexture(GL_TEXTURE_2D, gl_textures[0]);
+  SB_DCHECK(glGetError() == GL_NO_ERROR);
+
+  ok = eglBindTexImage(display, surface[0], EGL_BACK_BUFFER);
+  SB_DCHECK(ok);
+
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+  planeY->texture = gl_textures[0];
+  planeY->gl_texture_target = GL_TEXTURE_2D;
+  planeY->gl_texture_format = GL_RED_EXT;
+
+  // This tells ANGLE that the texture it creates should draw
+  // the chroma channel on R8G8.
+  bool bind_chroma = true;
+  hr = texture->SetPrivateData(kCobaltNv12BindChroma, 1, &bind_chroma);
+  SB_DCHECK(SUCCEEDED(hr));
+
+  EGLint chroma_texture_attributes[] = {EGL_WIDTH,
+                                        static_cast<EGLint>(info.width) / 2,
+                                        EGL_HEIGHT,
+                                        static_cast<EGLint>(info.height) / 2,
+                                        EGL_TEXTURE_TARGET,
+                                        EGL_TEXTURE_2D,
+                                        EGL_TEXTURE_FORMAT,
+                                        EGL_TEXTURE_RGBA,
+                                        EGL_NONE};
+  surface[1] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
+                                                texture.Get(), config,
+                                                chroma_texture_attributes);
+
+  SB_DCHECK(surface[1] != EGL_NO_SURFACE);
+
+  glBindTexture(GL_TEXTURE_2D, gl_textures[1]);
+  SB_DCHECK(glGetError() == GL_NO_ERROR);
+
+  ok = eglBindTexImage(display, surface[1], EGL_BACK_BUFFER);
+  SB_DCHECK(ok);
+
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+  planeUV->texture = gl_textures[1];
+  planeUV->gl_texture_target = GL_TEXTURE_2D;
+  planeUV->gl_texture_format = GL_RG_EXT;
+
+  hr = texture->SetPrivateData(kCobaltDxgiBuffer, 0, nullptr);
+  SB_DCHECK(SUCCEEDED(hr));
+}
+
+SbDecodeTargetPrivate::SbDecodeTargetPrivate(
+    const ComPtr<ID3D11Device>& d3d_device,
+    const vpx_image_t* img,
+    BYTE* frame_buf_nv12)
+    : refcount(1), d3d_device_(d3d_device) {
+  SbMemorySet(&info, 0, sizeof(info));
+  info.format = kSbDecodeTargetFormat2PlaneYUVNV12;
+  info.is_opaque = true;
+  info.width = img->w;
+  info.height = img->h;
+
+  d3d_texture = CreateVPXTexture(d3d_device, img, frame_buf_nv12);
+  CreateGLSurfacesNV12(d3d_texture, img->d_w, img->d_h);
+}
+
+SbDecodeTargetPrivate::SbDecodeTargetPrivate(
+    const ComPtr<ID3D11Device>& d3d_device,
+    const ComPtr<ID3D11VideoDevice1>& video_device,
+    const ComPtr<ID3D11VideoContext>& video_context,
+    const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
+    const ComPtr<ID3D11VideoProcessor>& video_processor,
+    const ComPtr<IMFSample>& video_sample,
+    const RECT& video_area)
+    : refcount(1) {
+  SbMemorySet(&info, 0, sizeof(info));
+  info.format = kSbDecodeTargetFormat2PlaneYUVNV12;
+  info.is_opaque = true;
+  info.width = video_area.right;
+  info.height = video_area.bottom;
+
+  d3d_texture = CreateEmptyTexture(d3d_device, info.width, info.height);
+  if (video_sample) {
+    UpdateTexture(d3d_texture, video_device, video_context, video_enumerator,
+                  video_processor, video_sample, video_area);
+  }
+  CreateGLSurfacesNV12(d3d_texture, info.width, info.height);
+}
+
+SbDecodeTargetPrivate::~SbDecodeTargetPrivate() {
+  glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneY].texture));
+  glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneUV].texture));
+
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+  eglReleaseTexImage(display, surface[0], EGL_BACK_BUFFER);
+  eglDestroySurface(display, surface[0]);
+
+  eglReleaseTexImage(display, surface[1], EGL_BACK_BUFFER);
+  eglDestroySurface(display, surface[1]);
+}
+
+bool SbDecodeTargetPrivate::Update(
+    const ComPtr<ID3D11Device>& d3d_device,
+    const ComPtr<ID3D11VideoDevice1>& video_device,
+    const ComPtr<ID3D11VideoContext>& video_context,
+    const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
+    const ComPtr<ID3D11VideoProcessor>& video_processor,
+    const ComPtr<IMFSample>& video_sample,
+    const RECT& video_area) {
+  // Only allow updating if this is the only reference. Otherwise the update
+  // may change something that's currently being used.
+  if (SbAtomicNoBarrier_Load(&refcount) > 1) {
+    return false;
+  }
+
+  // The decode target info must be compatible. The resolution should match
+  // exactly, otherwise the shader may sample invalid texels along the
+  // texture border.
+  if (info.format != kSbDecodeTargetFormat2PlaneYUVNV12 ||
+      info.is_opaque != true || info.width != video_area.right ||
+      info.height != video_area.bottom) {
+    return false;
+  }
+
+  SB_UNREFERENCED_PARAMETER(d3d_device);
+  UpdateTexture(d3d_texture, video_device, video_context, video_enumerator,
+                video_processor, video_sample, video_area);
+  return true;
+}
+
+void SbDecodeTargetPrivate::AddRef() {
+  SbAtomicBarrier_Increment(&refcount, 1);
+}
+
+void SbDecodeTargetPrivate::Release() {
+  int new_count = SbAtomicBarrier_Increment(&refcount, -1);
+  SB_DCHECK(new_count >= 0);
+  if (new_count == 0) {
+    delete this;
+  }
+}
+
+void SbDecodeTargetRelease(SbDecodeTarget decode_target) {
+  if (SbDecodeTargetIsValid(decode_target)) {
+    decode_target->Release();
+  }
+}
+
+SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget decode_target) {
+  // Note that kSbDecodeTargetFormat2PlaneYUVNV12 represents DXGI_FORMAT_NV12.
+  SB_DCHECK(kSbDecodeTargetFormat2PlaneYUVNV12 == decode_target->info.format);
+  return decode_target->info.format;
+}
+
+bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
+                           SbDecodeTargetInfo* out_info) {
+  if (!out_info || !SbMemoryIsZero(out_info, sizeof(*out_info))) {
+    SB_DCHECK(false) << "out_info must be zeroed out.";
+    return false;
+  }
+  SbMemoryCopy(out_info, &decode_target->info, sizeof(*out_info));
+  return true;
+}
\ No newline at end of file
diff --git a/src/starboard/shared/uwp/decode_target_internal.h b/src/starboard/shared/uwp/decode_target_internal.h
new file mode 100644
index 0000000..e3725b2
--- /dev/null
+++ b/src/starboard/shared/uwp/decode_target_internal.h
@@ -0,0 +1,79 @@
+// Copyright 2018 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_SHARED_UWP_DECODE_TARGET_INTERNAL_H_
+#define STARBOARD_SHARED_UWP_DECODE_TARGET_INTERNAL_H_
+
+#include <D3d11_1.h>
+#include <mfidl.h>
+#include <wrl/client.h>
+
+#include "starboard/atomic.h"
+#include "starboard/decode_target.h"
+#include "starboard/shared/libvpx_xb1/vpx_xb1_video_decoder.h"
+
+struct SbDecodeTargetPrivate {
+  template <typename T>
+  using ComPtr = Microsoft::WRL::ComPtr<T>;
+
+  SbAtomic32 refcount;
+
+  // Publicly accessible information about the decode target.
+  SbDecodeTargetInfo info;
+
+  ComPtr<ID3D11Texture2D> d3d_texture;
+
+  // EGLSurface is defined as void* in "third_party/angle/include/EGL/egl.h".
+  // Use void* directly here to avoid `egl.h` being included broadly.
+  void* surface[2];
+
+  ComPtr<ID3D11Texture2D> d3d_textures_[3];
+  void* surface_[3] = {};
+  ComPtr<ID3D11Device> d3d_device_ = nullptr;
+
+  SbDecodeTargetPrivate(
+      const ComPtr<ID3D11Device>& d3d_device,
+      const ComPtr<ID3D11VideoDevice1>& video_device,
+      const ComPtr<ID3D11VideoContext>& video_context,
+      const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
+      const ComPtr<ID3D11VideoProcessor>& video_processor,
+      const ComPtr<IMFSample>& video_sample,
+      const RECT& video_area);
+
+  // This constructor works properly for Xbox One platform only
+  SbDecodeTargetPrivate(const ComPtr<ID3D11Device>& d3d_device,
+                        const vpx_image_t* img,
+                        BYTE* frame_buf_nv12);
+
+  ~SbDecodeTargetPrivate();
+
+  // Update the existing texture with the given video_sample's data.
+  // If the current object is not compatible with the new video_sample, then
+  // this will return false, and the caller should just create a new
+  // decode target for the sample.
+  bool Update(const ComPtr<ID3D11Device>& d3d_device,
+              const ComPtr<ID3D11VideoDevice1>& video_device,
+              const ComPtr<ID3D11VideoContext>& video_context,
+              const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
+              const ComPtr<ID3D11VideoProcessor>& video_processor,
+              const ComPtr<IMFSample>& video_sample,
+              const RECT& video_area);
+  void CreateGLSurfacesNV12(const ComPtr<ID3D11Texture2D>& texture,
+                            int display_width,
+                            int display_height);
+  void AddRef();
+  void Release();
+};
+
+#endif  // STARBOARD_SHARED_UWP_DECODE_TARGET_INTERNAL_H_
diff --git a/src/starboard/shared/uwp/media_is_video_supported.cc b/src/starboard/shared/uwp/media_is_video_supported.cc
new file mode 100644
index 0000000..3be4eab
--- /dev/null
+++ b/src/starboard/shared/uwp/media_is_video_supported.cc
@@ -0,0 +1,177 @@
+// Copyright 2018 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/shared/starboard/media/media_support_internal.h"
+
+#include <d3d11.h>
+#include <d3d12.h>
+#include <mfapi.h>
+#include <mfidl.h>
+#include <wrl/client.h>
+
+#include "starboard/shared/uwp/application_uwp.h"
+#include "third_party/libvpx_xb1/libvpx/vpx/vp8dx.h"
+#include "third_party/libvpx_xb1/libvpx/vpx/vpx_decoder.h"
+
+namespace {
+
+using ::starboard::shared::uwp::ApplicationUwp;
+
+const int kMaxVpxGpuDecodeTargetWidth = 3840;
+const int kMaxVpxGpuDecodeTargetHeight = 2160;
+const int kMaxVpxGpuDecoderCpuCoreUse = 4;
+
+}  // namespace
+
+#if SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
+
+namespace {
+// Cache the VP9 support status since the check may be expensive.
+enum Vp9Support { kVp9SupportUnknown, kVp9SupportYes, kVp9SupportNo };
+Vp9Support s_vp9_hw_support = kVp9SupportUnknown;
+Vp9Support s_vp9_gpu_support = kVp9SupportUnknown;
+}  // namespace
+
+// Check for VP9 support. Since this is used by a starboard function, it
+// cannot depend on other modules (e.g. ANGLE).
+SB_EXPORT bool IsVp9HwDecoderSupported() {
+  if (s_vp9_hw_support == kVp9SupportUnknown) {
+    // Try initializing the VP9 decoder to determine if it is supported.
+    HRESULT hr;
+
+    Microsoft::WRL::ComPtr<ID3D11Device> d3d_device;
+    hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0,
+                           nullptr, 0, D3D11_SDK_VERSION,
+                           d3d_device.GetAddressOf(), nullptr, nullptr);
+
+    UINT reset_token = 0;
+    Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> device_manager;
+    if (SUCCEEDED(hr)) {
+      hr = MFCreateDXGIDeviceManager(&reset_token,
+                                     device_manager.GetAddressOf());
+    }
+    if (SUCCEEDED(hr)) {
+      hr = device_manager->ResetDevice(d3d_device.Get(), reset_token);
+    }
+
+    Microsoft::WRL::ComPtr<IMFTransform> transform;
+    if (SUCCEEDED(hr)) {
+      hr = CoCreateInstance(CLSID_MSVPxDecoder, nullptr, CLSCTX_INPROC_SERVER,
+                            IID_PPV_ARGS(transform.GetAddressOf()));
+    }
+
+    if (SUCCEEDED(hr)) {
+      hr = transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
+                                     ULONG_PTR(device_manager.Get()));
+    }
+
+    s_vp9_hw_support = SUCCEEDED(hr) ? kVp9SupportYes : kVp9SupportNo;
+  }
+  return s_vp9_hw_support == kVp9SupportYes;
+}
+
+SB_EXPORT bool IsVp9GPUDecoderSupported() {
+  if (s_vp9_gpu_support == kVp9SupportUnknown) {
+    D3D12_COMMAND_QUEUE_DESC desc = {};
+    desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+    desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+    Microsoft::WRL::ComPtr<ID3D12CommandQueue> d3d12queue;
+    ApplicationUwp* application_uwp = ApplicationUwp::Get();
+    SB_CHECK(application_uwp->GetD3D12Device());
+    HRESULT hr = application_uwp->GetD3D12Device()->CreateCommandQueue(
+        &desc, IID_PPV_ARGS(&d3d12queue));
+    if (FAILED(hr)) {
+      SB_LOG(WARNING)
+          << "Could not create DX12 command queue: cannot use GPU VP9";
+      return false;
+    }
+    vpx_codec_ctx vpx_context = {};
+    vpx_codec_dec_cfg_t vpx_config = {};
+    vpx_config.w = kMaxVpxGpuDecodeTargetWidth;
+    vpx_config.h = kMaxVpxGpuDecodeTargetHeight;
+    vpx_config.threads = kMaxVpxGpuDecoderCpuCoreUse;
+
+    vpx_config.hw_device = application_uwp->GetD3D12Device().Get();
+    vpx_config.hw_command_queue = d3d12queue.Get();
+    vpx_codec_err_t status =
+        vpx_codec_dec_init(&vpx_context, vpx_codec_vp9_dx(), &vpx_config,
+                           VPX_CODEC_USE_FRAME_THREADING);
+    s_vp9_gpu_support =
+        (status == VPX_CODEC_OK) ? kVp9SupportYes : kVp9SupportNo;
+    vpx_codec_destroy(&vpx_context);
+  }
+  return s_vp9_gpu_support == kVp9SupportYes;
+}
+
+#else  // SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
+
+bool IsVp9HwDecoderSupported() {
+  return false;
+}
+
+bool IsVp9GPUDecoderSupported() {
+  return false;
+}
+
+#endif
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+                                       int frame_width,
+                                       int frame_height,
+                                       int64_t bitrate,
+                                       int fps) {
+  int max_width = 1920;
+  int max_height = 1080;
+
+  if (video_codec == kSbMediaVideoCodecVp9) {
+// Vp9 supports 8k only in whitelisted platforms, up to 4k in the others.
+#ifdef ENABLE_VP9_8K_SUPPORT
+    max_width = 7680;
+    max_height = 4320;
+#else   // ENABLE_VP9_8K_SUPPORT
+    max_width = 3840;
+    max_height = 2160;
+#endif  // ENABLE_VP9_8K_SUPPORT
+  } else if (video_codec == kSbMediaVideoCodecH264) {
+// Not all devices can support 4k H264; some (e.g. xb1) may crash in
+// the decoder if provided too high of a resolution. Therefore
+// platforms must explicitly opt-in to support 4k H264.
+#ifdef ENABLE_H264_4K_SUPPORT
+    max_width = 3840;
+    max_height = 2160;
+#endif  // ENABLE_H264_4K_SUPPORT
+  }
+
+  if (frame_width > max_width || frame_height > max_height) {
+    return false;
+  }
+
+  // Is bitrate in range?
+  if (bitrate > SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND) {
+    return false;
+  }
+  if (fps > 60) {
+    return false;
+  }
+  if (video_codec == kSbMediaVideoCodecH264) {
+    return true;
+  }
+  if (video_codec == kSbMediaVideoCodecVp9) {
+    if (IsVp9HwDecoderSupported())
+      return true;
+    if (IsVp9GPUDecoderSupported())
+      return true;
+  }
+  return false;
+}
diff --git a/src/starboard/shared/uwp/player_components_impl.cc b/src/starboard/shared/uwp/player_components_impl.cc
new file mode 100644
index 0000000..4359801
--- /dev/null
+++ b/src/starboard/shared/uwp/player_components_impl.cc
@@ -0,0 +1,110 @@
+// Copyright 2018 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/shared/starboard/player/filter/player_components.h"
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/shared/libvpx_xb1/vpx_xb1_video_decoder.h"
+#include "starboard/shared/starboard/media/media_support_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
+#include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
+#include "starboard/shared/win32/audio_decoder.h"
+#include "starboard/shared/win32/video_decoder.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+namespace {
+
+class PlayerComponentsImpl : public PlayerComponents {
+  void CreateAudioComponents(
+      const AudioParameters& audio_parameters,
+      scoped_ptr<AudioDecoder>* audio_decoder,
+      scoped_ptr<AudioRendererSink>* audio_renderer_sink) override {
+    using AudioDecoderImpl = ::starboard::shared::win32::AudioDecoder;
+
+    SB_DCHECK(audio_decoder);
+    SB_DCHECK(audio_renderer_sink);
+
+    audio_decoder->reset(new AudioDecoderImpl(audio_parameters.audio_codec,
+                                              audio_parameters.audio_header,
+                                              audio_parameters.drm_system));
+    audio_renderer_sink->reset(new AudioRendererSinkImpl);
+  }
+
+  void CreateVideoComponents(
+      const VideoParameters& video_parameters,
+      scoped_ptr<VideoDecoder>* video_decoder,
+      scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
+      scoped_refptr<VideoRendererSink>* video_renderer_sink) override {
+    using VideoDecoderImpl = ::starboard::shared::win32::VideoDecoder;
+    using VideoDecoderVpx = ::starboard::shared::vpx::VideoDecoder;
+
+    SB_DCHECK(video_decoder);
+    SB_DCHECK(video_render_algorithm);
+    SB_DCHECK(video_renderer_sink);
+
+    const bool use_software_vp9 =
+        (video_parameters.video_codec == kSbMediaVideoCodecVp9) &&
+        !IsVp9HwDecoderSupported();
+
+    if (use_software_vp9) {
+      // Software vp9 is the special case.
+      scoped_ptr<VideoDecoderVpx> video_decoder_impl(new VideoDecoderVpx(
+          video_parameters.video_codec, video_parameters.output_mode,
+          video_parameters.decode_target_graphics_context_provider));
+      *video_renderer_sink = video_decoder_impl->GetSink();
+      video_decoder->reset(video_decoder_impl.release());
+      video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
+    } else {
+      scoped_ptr<VideoDecoderImpl> video_decoder_impl(new VideoDecoderImpl(
+        video_parameters.video_codec, video_parameters.output_mode,
+        video_parameters.decode_target_graphics_context_provider,
+        video_parameters.drm_system));
+      *video_renderer_sink = video_decoder_impl->GetSink();
+      video_decoder->reset(video_decoder_impl.release());
+      video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
+    }
+  }
+
+  void GetAudioRendererParams(int* max_cached_frames,
+                              int* max_frames_per_append) const override {
+    SB_DCHECK(max_cached_frames);
+    SB_DCHECK(max_frames_per_append);
+
+    *max_cached_frames = 256 * 1024;
+    *max_frames_per_append = 16384;
+  }
+};
+
+}  // namespace
+
+// static
+scoped_ptr<PlayerComponents> PlayerComponents::Create() {
+  return make_scoped_ptr<PlayerComponents>(new PlayerComponentsImpl);
+}
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi
index 8f369cd..612944d 100644
--- a/src/starboard/shared/uwp/starboard_platform.gypi
+++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -84,6 +84,7 @@
       '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_supports_resume.cc',
     ]
   }
 }
diff --git a/src/starboard/shared/uwp/system_get_property.cc b/src/starboard/shared/uwp/system_get_property.cc
index 1fdc621..cc81814 100644
--- a/src/starboard/shared/uwp/system_get_property.cc
+++ b/src/starboard/shared/uwp/system_get_property.cc
@@ -202,10 +202,12 @@
       return CopyStringAndTestIfSuccess(out_value, value_length,
                                         os_name_and_version.c_str());
     }
+#if SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION
     case kSbSystemPropertyPlatformUuid: {
       SB_NOTIMPLEMENTED();
       return CopyStringAndTestIfSuccess(out_value, value_length, "N/A");
     }
+#endif  // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION
     default:
       SB_DLOG(WARNING) << __FUNCTION__
                        << ": Unrecognized property: " << property_id;
diff --git a/src/starboard/shared/wayland/application_wayland.cc b/src/starboard/shared/wayland/application_wayland.cc
index 06b2a28..51b93e2 100644
--- a/src/starboard/shared/wayland/application_wayland.cc
+++ b/src/starboard/shared/wayland/application_wayland.cc
@@ -17,75 +17,74 @@
 #include <EGL/egl.h>
 #include <poll.h>
 #include <sys/eventfd.h>
+#include <string.h>
+#include <unistd.h>
 
 #include "starboard/log.h"
 #include "starboard/memory.h"
-#include "starboard/shared/wayland/dev_input.h"
+#include "starboard/shared/starboard/audio_sink/audio_sink_internal.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.
+namespace {
 
+// registry_listener
+void GlobalObjectAvailable(void* data,
+                           struct wl_registry* registry,
+                           uint32_t name,
+                           const char* interface,
+                           uint32_t version) {
+  ApplicationWayland* app_wl = reinterpret_cast<ApplicationWayland*>(data);
+  SB_DCHECK(app_wl);
+  app_wl->OnGlobalObjectAvailable(registry, name, interface, version);
+}
+
+void GlobalObjectRemove(void*, struct wl_registry*, uint32_t) {}
+
+static struct wl_registry_listener registry_listener = {
+    &GlobalObjectAvailable,
+    &GlobalObjectRemove
+};
+
+}
+
+// Tizen application engine using the generic queue and a tizen implementation.
 ApplicationWayland::ApplicationWayland(float video_pixel_ratio)
-    : video_pixel_ratio_(video_pixel_ratio),
-      seat_(NULL),
-      keyboard_(NULL),
-      key_repeat_event_id_(kSbEventIdInvalid),
-      key_repeat_interval_(kKeyHoldTime),
-      key_modifiers_(0) {}
+    : video_pixel_ratio_(video_pixel_ratio) {
+  SbAudioSinkPrivate::Initialize();
+}
+
+void ApplicationWayland::InjectInputEvent(SbInputData* data) {
+  Inject(new Event(kSbEventTypeInput, data,
+                   &Application::DeleteDestructor<SbInputData>));
+}
+
+bool ApplicationWayland::OnGlobalObjectAvailable(struct wl_registry* registry,
+                                                 uint32_t name,
+                                                 const char* interface,
+                                                 uint32_t version) {
+  if (strcmp(interface, "wl_compositor") == 0) {
+    compositor_ = static_cast<wl_compositor*>(
+        wl_registry_bind(registry, name, &wl_compositor_interface, 1));
+    return true;
+  }
+  if (strcmp(interface, "wl_shell") == 0) {
+    shell_ = static_cast<wl_shell*>(
+        wl_registry_bind(registry, name, &wl_shell_interface, 1));
+    return true;
+  }
+  return dev_input_.OnGlobalObjectAvailable(registry, name, interface, version);
+}
 
 SbWindow ApplicationWayland::CreateWindow(const SbWindowOptions* options) {
   SB_DLOG(INFO) << "CreateWindow";
-  SbWindow window = new SbWindowPrivate(options, video_pixel_ratio_);
-  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);
-
+  SbWindow window =
+      new SbWindowPrivate(compositor_, shell_, options, video_pixel_ratio_);
+  dev_input_.SetSbWindow(window);
   return window;
 }
 
@@ -95,26 +94,13 @@
     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);
-
+  dev_input_.SetSbWindow(kSbWindowInvalid);
+  delete window;
   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);
@@ -133,13 +119,14 @@
 
 void ApplicationWayland::Teardown() {
   SB_DLOG(INFO) << "Teardown";
-  DeleteRepeatKey();
+  dev_input_.DeleteRepeatKey();
 
   TerminateEgl();
 
   wl_display_flush(display_);
   wl_display_disconnect(display_);
 
+  SbAudioSinkPrivate::TearDown();
   // Close wakeup event
   close(wakeup_fd_);
 }
@@ -234,60 +221,6 @@
   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;
-  data->device_id = 1;                           // kKeyboardDeviceId;
-  data->key = KeyCodeToSbKey(key);
-  data->key_location = KeyCodeToSbKeyLocation(key);
-  data->key_modifiers = key_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(void* context, EventHandledCallback callback) {
   Application::Pause(context, callback);
 
@@ -323,7 +256,7 @@
   const size_t payload_size = strlen(payload) + 1;
   char* copied_payload = new char[payload_size];
   snprintf(copied_payload, payload_size, "%s", payload);
-  Inject(new Event(kSbEventTypeLink, copiedPayload,
+  Inject(new Event(kSbEventTypeLink, copied_payload,
                    [](void* data) { delete[] reinterpret_cast<char*>(data); }));
 }
 
diff --git a/src/starboard/shared/wayland/application_wayland.h b/src/starboard/shared/wayland/application_wayland.h
index 12e60ff..5bfb605 100644
--- a/src/starboard/shared/wayland/application_wayland.h
+++ b/src/starboard/shared/wayland/application_wayland.h
@@ -15,18 +15,19 @@
 #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/event.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/shared/wayland/dev_input.h"
 #include "starboard/types.h"
 #include "starboard/window.h"
 
@@ -36,7 +37,7 @@
 
 class ApplicationWayland : public shared::starboard::QueueApplication {
  public:
-  explicit ApplicationWayland(float video_pixel_ratio);
+  explicit ApplicationWayland(float video_pixel_ratio = 1.0f);
   ~ApplicationWayland() override{};
 
   static ApplicationWayland* Get() {
@@ -44,35 +45,22 @@
         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();
+  // wl registry add listener
+  virtual bool OnGlobalObjectAvailable(struct wl_registry* registry,
+                                       uint32_t name,
+                                       const char* interface,
+                                       uint32_t version);
+
+  // display
   wl_display* GetWLDisplay() { return display_; }
 
-  // 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 UpdateKeyModifiers(unsigned int modifiers) {
-    key_modifiers_ = modifiers;
-  }
-  void CreateRepeatKey();
-  void DeleteRepeatKey();
-  void CreateKey(int key, int state, bool is_repeat);
+  // window
+  virtual SbWindow CreateWindow(const SbWindowOptions* options);
+  bool DestroyWindow(SbWindow window);
 
   // state change
-  void Pause(void* context, EventHandledCallback callback) override;
-  void Unpause(void* context, EventHandledCallback callback) override;
+  void Pause(void* context, EventHandledCallback callback);
+  void Unpause(void* context, EventHandledCallback callback);
 
   // state change observer
   class StateObserver {
@@ -88,6 +76,8 @@
   // deeplink
   void Deeplink(char* payload);
 
+  void InjectInputEvent(SbInputData* data);
+
  protected:
   // --- Application overrides ---
   void Initialize() override;
@@ -101,26 +91,16 @@
   Event* WaitForSystemEventWithTimeout(SbTime time) override;
   void WakeSystemEventWait() override;
 
- private:
-  void InitializeEgl();
-  void TerminateEgl();
-
-  // window
-  SbWindow window_;
-  float video_pixel_ratio_;
+ protected:
   wl_display* display_;
   wl_compositor* compositor_;
   wl_shell* shell_;
-  tizen_policy* tz_policy_;
+  DevInput dev_input_;
+  float video_pixel_ratio_;
 
-  // 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_;
-  unsigned int key_modifiers_;
+ private:
+  void InitializeEgl();
+  void TerminateEgl();
 
   // wakeup event
   int wakeup_fd_;
diff --git a/src/starboard/shared/wayland/dev_input.cc b/src/starboard/shared/wayland/dev_input.cc
new file mode 100644
index 0000000..9df9f7c
--- /dev/null
+++ b/src/starboard/shared/wayland/dev_input.cc
@@ -0,0 +1,563 @@
+// Copyright 2018 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/dev_input.h"
+
+#include <linux/input.h>
+#include <string.h>
+
+#include "starboard/input.h"
+#include "starboard/key.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/wayland/application_wayland.h"
+
+namespace starboard {
+namespace shared {
+namespace wayland {
+
+namespace {
+
+// 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
+const SbTime kKeyHoldTime = 500 * kSbTimeMillisecond;
+const SbTime kKeyRepeatTime = 50 * kSbTimeMillisecond;
+
+void SeatHandleCapabilities(void* data,
+                            struct wl_seat* seat,
+                            unsigned int caps) {
+  DevInput* dev_input = reinterpret_cast<DevInput*>(data);
+  SB_DCHECK(dev_input);
+  dev_input->OnSeatCapabilitiesChanged(seat, caps);
+}
+
+const struct wl_seat_listener seat_listener = {
+    &SeatHandleCapabilities,
+};
+
+#define KEY_INFO_BUTTON 0xbc
+
+// Converts an input_event code into an SbKey.
+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.
+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;
+}
+
+void KeyboardHandleKeyMap(void* data,
+                          struct wl_keyboard* keyboard,
+                          uint32_t format,
+                          int fd,
+                          uint32_t size) {
+  DevInput* dev_input = reinterpret_cast<DevInput*>(data);
+  SB_DCHECK(dev_input);
+  dev_input->OnKeyboardHandleKeyMap(keyboard, format, fd, size);
+}
+
+void KeyboardHandleEnter(void* data,
+                         struct wl_keyboard* keyboard,
+                         uint32_t serial,
+                         struct wl_surface* surface,
+                         struct wl_array* keys) {
+  DevInput* dev_input = reinterpret_cast<DevInput*>(data);
+  SB_DCHECK(dev_input);
+  dev_input->OnKeyboardHandleEnter(keyboard, serial, surface, keys);
+}
+
+void KeyboardHandleLeave(void* data,
+                         struct wl_keyboard* keyboard,
+                         uint32_t serial,
+                         struct wl_surface* surface) {
+  DevInput* dev_input = reinterpret_cast<DevInput*>(data);
+  SB_DCHECK(dev_input);
+  dev_input->OnKeyboardHandleLeave(keyboard, serial, surface);
+}
+
+void KeyboardHandleKey(void* data,
+                       struct wl_keyboard* keyboard,
+                       uint32_t serial,
+                       uint32_t time,
+                       uint32_t key,
+                       uint32_t state) {
+  DevInput* dev_input = reinterpret_cast<DevInput*>(data);
+  SB_DCHECK(dev_input);
+  dev_input->OnKeyboardHandleKey(keyboard, serial, time, key, state);
+}
+
+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) {
+  DevInput* dev_input = reinterpret_cast<DevInput*>(data);
+  SB_DCHECK(dev_input);
+  dev_input->OnKeyboardHandleModifiers(keyboard, serial, mods_depressed,
+                                       mods_latched, mods_locked, group);
+}
+
+const struct wl_keyboard_listener keyboard_listener = {
+    &KeyboardHandleKeyMap,
+    &KeyboardHandleEnter,
+    &KeyboardHandleLeave,
+    &KeyboardHandleKey,
+    &KeyboardHandleModifiers,
+};
+
+}
+
+DevInput::DevInput()
+    : wl_seat_(NULL),
+      wl_keyboard_(NULL),
+      key_repeat_event_id_(kSbEventIdInvalid),
+      key_repeat_interval_(kKeyHoldTime),
+      key_modifiers_(0),
+      window_(kSbWindowInvalid) {}
+
+bool DevInput::OnGlobalObjectAvailable(struct wl_registry* registry,
+                                       uint32_t name,
+                                       const char* interface,
+                                       uint32_t version) {
+  if (strcmp(interface, "wl_seat") == 0) {
+    wl_seat_ = static_cast<wl_seat*>(
+        wl_registry_bind(registry, name, &wl_seat_interface, 1));
+    SB_DCHECK(wl_seat_);
+    wl_seat_add_listener(wl_seat_, &seat_listener, this);
+    return true;
+  }
+  return false;
+}
+
+void DevInput::OnSeatCapabilitiesChanged(struct wl_seat* seat,
+                                         unsigned int caps) {
+  SB_DLOG(INFO) << "[Key] seat_handle_capabilities caps: " << caps;
+  if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
+    wl_keyboard_ = static_cast<wl_keyboard*>(wl_seat_get_keyboard(seat));
+    if (wl_keyboard_)
+      wl_keyboard_add_listener(wl_keyboard_, &keyboard_listener, this);
+  } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
+    if (wl_keyboard_)
+      wl_keyboard_destroy(wl_keyboard_);
+    wl_keyboard_ = NULL;
+  }
+}
+
+void DevInput::OnKeyboardHandleKeyMap(struct wl_keyboard* keyboard,
+                                      uint32_t format,
+                                      int fd,
+                                      uint32_t size) {
+  SB_DLOG(INFO) << "[Key] Keyboard keymap";
+}
+
+void DevInput::OnKeyboardHandleEnter(struct wl_keyboard* keyboard,
+                                     uint32_t serial,
+                                     struct wl_surface* surface,
+                                     struct wl_array* keys) {
+  SB_DLOG(INFO) << "[Key] Keyboard gained focus";
+}
+
+void DevInput::OnKeyboardHandleLeave(struct wl_keyboard* keyboard,
+                                     uint32_t serial,
+                                     struct wl_surface* surface) {
+  SB_DLOG(INFO) << "[Key] Keyboard lost focus";
+  DeleteRepeatKey();
+}
+
+void DevInput::OnKeyboardHandleModifiers(struct wl_keyboard* keyboard,
+                                         uint32_t serial,
+                                         uint32_t mods_depressed,
+                                         uint32_t mods_latched,
+                                         uint32_t mods_locked,
+                                         uint32_t group) {
+  // Convert to SbKeyModifiers.
+  unsigned int modifiers = kSbKeyModifiersNone;
+
+  if (mods_depressed & 1)
+    modifiers |= kSbKeyModifiersShift;
+  if (mods_depressed & 4)
+    modifiers |= kSbKeyModifiersCtrl;
+  if (mods_depressed & 8)
+    modifiers |= kSbKeyModifiersAlt;
+
+  SB_DLOG(INFO) << "[Key] Modifiers depressed " << mods_depressed
+                << ", latched " << mods_latched << ", locked " << mods_locked
+                << ", group " << group << ", key modifiers " << modifiers;
+
+  key_modifiers_ = modifiers;
+}
+
+void DevInput::OnKeyboardHandleKey(struct wl_keyboard* keyboard,
+                                   uint32_t serial,
+                                   uint32_t time,
+                                   uint32_t key,
+                                   uint32_t state) {
+  bool repeatable = (key == KEY_LEFT || key == KEY_RIGHT || key == KEY_UP || key == KEY_DOWN);
+  SB_DLOG(INFO) << "[Key] Key :" << key << ", state:" << state << " repeatable " << repeatable
+                << " key_repeat_key_ " << key_repeat_key_ << " key_repeat_state_ " << key_repeat_state_;
+  if (state && repeatable && key == key_repeat_key_ && key_repeat_state_)
+    return;
+
+  if (repeatable) {
+    CreateKey(key, state, true);
+  } else {
+    CreateKey(key, state, false);
+  }
+}
+
+void DevInput::CreateRepeatKey() {
+  if (!key_repeat_state_) {
+    return;
+  }
+
+  if (key_repeat_interval_) {
+    key_repeat_interval_ = kKeyRepeatTime;
+  }
+
+  CreateKey(key_repeat_key_, key_repeat_state_, true);
+}
+
+void DevInput::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;
+  data->device_id = 1;  // kKeyboardDeviceId;
+  data->key = KeyCodeToSbKey(key);
+  data->key_location = KeyCodeToSbKeyLocation(key);
+  data->key_modifiers = key_modifiers_;
+  ApplicationWayland::Get()->InjectInputEvent(data);
+
+  DeleteRepeatKey();
+
+  if (is_repeat && state) {
+    key_repeat_key_ = key;
+    key_repeat_state_ = state;
+    key_repeat_event_id_ = SbEventSchedule(
+        [](void* data) {
+          DevInput* dev_input = reinterpret_cast<DevInput*>(data);
+          dev_input->CreateRepeatKey();
+        },
+        this, key_repeat_interval_);
+  } else {
+    key_repeat_interval_ = kKeyHoldTime;
+  }
+}
+
+void DevInput::DeleteRepeatKey() {
+  key_repeat_state_ = 0;
+  if (key_repeat_event_id_ != kSbEventIdInvalid) {
+    SbEventCancel(key_repeat_event_id_);
+    key_repeat_event_id_ = kSbEventIdInvalid;
+  }
+}
+
+}  // namespace wayland
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/wayland/dev_input.h b/src/starboard/shared/wayland/dev_input.h
index fbcbf11..a8538cf 100644
--- a/src/starboard/shared/wayland/dev_input.h
+++ b/src/starboard/shared/wayland/dev_input.h
@@ -15,491 +15,72 @@
 #ifndef STARBOARD_SHARED_WAYLAND_DEV_INPUT_H_
 #define STARBOARD_SHARED_WAYLAND_DEV_INPUT_H_
 
-#include <EGL/egl.h>
-#include <linux/input.h>
+#include <wayland-client.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"
+#include "starboard/event.h"
+#include "starboard/time.h"
+#include "starboard/window.h"
 
 namespace starboard {
 namespace shared {
 namespace wayland {
 
-#define KEY_INFO_BUTTON 0xbc
+class DevInput {
+ public:
+  DevInput();
+  ~DevInput() {}
 
-// 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;
+  // wl registry add listener
+  bool OnGlobalObjectAvailable(struct wl_registry* registry,
+                               uint32_t name,
+                               const char* interface,
+                               uint32_t version);
 
-    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;
+  // wl seat add listener
+  void OnSeatCapabilitiesChanged(struct wl_seat* seat, unsigned int caps);
 
-    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));
+  // wl keyboard add listener
+  void OnKeyboardHandleKeyMap(struct wl_keyboard* keyboard,
+                              uint32_t format,
+                              int fd,
+                              uint32_t size);
+  void OnKeyboardHandleEnter(struct wl_keyboard* keyboard,
+                             uint32_t serial,
+                             struct wl_surface* surface,
+                             struct wl_array* keys);
+  void OnKeyboardHandleLeave(struct wl_keyboard* keyboard,
+                             uint32_t serial,
+                             struct wl_surface* surface);
+  void OnKeyboardHandleKey(struct wl_keyboard* keyboard,
+                           uint32_t serial,
+                           uint32_t time,
+                           uint32_t key,
+                           uint32_t state);
+  void OnKeyboardHandleModifiers(struct wl_keyboard* keyboard,
+                                 uint32_t serial,
+                                 uint32_t mods_depressed,
+                                 uint32_t mods_latched,
+                                 uint32_t mods_locked,
+                                 uint32_t group);
 
-    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;
+  // window
+  void SetSbWindow(SbWindow window) { window_ = window; }
 
-    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));
+  void DeleteRepeatKey();
+  void CreateKey(int key, int state, bool is_repeat);
+  void CreateRepeatKey();
 
-    // 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) {
-  ApplicationWayland* wayland_window_ =
-      reinterpret_cast<ApplicationWayland*>(data);
-  // Convert to SbKeyModifiers.
-  unsigned int modifiers = kSbKeyModifiersNone;
-
-  if (mods_depressed & 1)
-    modifiers |= kSbKeyModifiersShift;
-  if (mods_depressed & 4)
-    modifiers |= kSbKeyModifiersCtrl;
-  if (mods_depressed & 8)
-    modifiers |= kSbKeyModifiersAlt;
-
-  SB_DLOG(INFO) << "[Key] Modifiers depressed " << mods_depressed
-                << ", latched " << mods_latched << ", locked " << mods_locked
-                << ", group " << group << ", key modifiers " << modifiers;
-
-  wayland_window_->UpdateKeyModifiers(modifiers);
-}
-
-static const struct wl_keyboard_listener keyboard_listener_ = {
-    &KeyboardHandleKeyMap, &KeyboardHandleEnter,     &KeyboardHandleLeave,
-    &KeyboardHandleKey,    &KeyboardHandleModifiers,
+ private:
+  wl_seat* wl_seat_;
+  wl_keyboard* wl_keyboard_;
+  int key_repeat_key_;
+  int key_repeat_state_;
+  SbEventId key_repeat_event_id_;
+  SbTime key_repeat_interval_;
+  unsigned int key_modifiers_;
+  SbWindow window_;
 };
 
-// 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 SB_HAS(LAZY_SUSPEND)
-  if (visibility == TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED)
-    ApplicationWayland::Get()->Pause(NULL, NULL);
-  else
-    ApplicationWayland::Get()->Unpause(NULL, NULL);
-#else
-  if (visibility == TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED)
-    shared::starboard::Application::Get()->Suspend(NULL, NULL);
-  else
-    shared::starboard::Application::Get()->Unpause(NULL, NULL);
-#endif
-}
-
-static const struct tizen_visibility_listener tizen_visibility_listener = {
-    WindowCbVisibilityChange};
-
 }  // namespace wayland
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/wayland/window_internal.cc b/src/starboard/shared/wayland/window_internal.cc
index 1eb325a..bce7bc7 100644
--- a/src/starboard/shared/wayland/window_internal.cc
+++ b/src/starboard/shared/wayland/window_internal.cc
@@ -14,18 +14,81 @@
 
 #include "starboard/shared/wayland/window_internal.h"
 
+#include "starboard/log.h"
+
 namespace {
+
 const int kWindowWidth = 1920;
 const int kWindowHeight = 1080;
+
+// shell_surface_listener
+void ShellSurfacePing(void*,
+                      struct wl_shell_surface* shell_surface,
+                      uint32_t serial) {
+  wl_shell_surface_pong(shell_surface, serial);
 }
 
-SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* options,
+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";
+  }
+}
+
+void ShellSurfacePopupDone(void*, struct wl_shell_surface*) {}
+
+struct wl_shell_surface_listener shell_surface_listener = {
+    &ShellSurfacePing,
+    &ShellSurfaceConfigure,
+    &ShellSurfacePopupDone
+};
+
+}
+
+SbWindowPrivate::SbWindowPrivate(wl_compositor* compositor,
+                                 wl_shell* shell,
+                                 const SbWindowOptions* options,
                                  float pixel_ratio) {
   width = kWindowWidth;
   height = kWindowHeight;
   video_pixel_ratio = pixel_ratio;
+
   if (options && options->size.width > 0 && options->size.height > 0) {
     width = options->size.width;
     height = options->size.height;
   }
+
+  surface = wl_compositor_create_surface(compositor);
+  shell_surface = wl_shell_get_shell_surface(shell, surface);
+  wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, this);
+  wl_shell_surface_set_title(shell_surface, "cobalt");
+
+  struct wl_region* region;
+  region = wl_compositor_create_region(compositor);
+  wl_region_add(region, 0, 0, width, height);
+  wl_surface_set_opaque_region(surface, region);
+  wl_region_destroy(region);
+
+  egl_window = wl_egl_window_create(surface, width, height);
+
+  WindowRaise();
+}
+
+SbWindowPrivate::~SbWindowPrivate() {
+  wl_egl_window_destroy(egl_window);
+  wl_shell_surface_destroy(shell_surface);
+  wl_surface_destroy(surface);
+}
+
+void SbWindowPrivate::WindowRaise() {
+  if (shell_surface)
+    wl_shell_surface_set_toplevel(shell_surface);
 }
diff --git a/src/starboard/shared/wayland/window_internal.h b/src/starboard/shared/wayland/window_internal.h
index 7d2c616..2677f9e 100644
--- a/src/starboard/shared/wayland/window_internal.h
+++ b/src/starboard/shared/wayland/window_internal.h
@@ -15,29 +15,23 @@
 #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,
+  explicit SbWindowPrivate(wl_compositor* compositor,
+                           wl_shell* shell,
+                           const SbWindowOptions* options,
                            float pixel_ratio = 1.0);
-  ~SbWindowPrivate() {}
+  virtual ~SbWindowPrivate();
+
+  virtual void WindowRaise();
 
   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, pixel ratio of this window.
   int width;
diff --git a/src/starboard/shared/win32/audio_sink.cc b/src/starboard/shared/win32/audio_sink.cc
index 5a8affd..70aafb3 100644
--- a/src/starboard/shared/win32/audio_sink.cc
+++ b/src/starboard/shared/win32/audio_sink.cc
@@ -45,8 +45,10 @@
 
 WORD SampleTypeToFormatTag(SbMediaAudioSampleType type) {
   switch (type) {
+#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
     case kSbMediaAudioSampleTypeInt16:
       return WAVE_FORMAT_PCM;
+#endif  // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
     case kSbMediaAudioSampleTypeFloat32:
       return WAVE_FORMAT_IEEE_FLOAT;
     default:
@@ -57,8 +59,10 @@
 
 WORD SampleTypeToBitsPerSample(SbMediaAudioSampleType type) {
   switch (type) {
+#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
     case kSbMediaAudioSampleTypeInt16:
       return 16;
+#endif  // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
     case kSbMediaAudioSampleTypeFloat32:
       return 32;
     default:
diff --git a/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc b/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc
index 07bfe28..6f3c417 100644
--- a/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc
+++ b/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc
@@ -19,8 +19,10 @@
 bool SbAudioSinkIsAudioSampleTypeSupported(
     SbMediaAudioSampleType audio_sample_type) {
   switch (audio_sample_type) {
+#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
     case kSbMediaAudioSampleTypeInt16:
       return true;
+#endif  // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
     case kSbMediaAudioSampleTypeFloat32:
       return true;
     default:
diff --git a/src/starboard/shared/win32/dx_context_video_decoder.cc b/src/starboard/shared/win32/dx_context_video_decoder.cc
index 677a133..49d34c2 100644
--- a/src/starboard/shared/win32/dx_context_video_decoder.cc
+++ b/src/starboard/shared/win32/dx_context_video_decoder.cc
@@ -39,7 +39,7 @@
   PFNEGLQUERYDEVICEATTRIBEXTPROC query_device;
   query_device = reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
       eglGetProcAddress("eglQueryDeviceAttribEXT"));
-  SB_DCHECK(query_display != nullptr);
+  SB_DCHECK(query_device != nullptr);
 
   intptr_t egl_device = 0;
   query_display(display, EGL_DEVICE_EXT, &egl_device);
diff --git a/src/starboard/shared/win32/gyp_configuration.py b/src/starboard/shared/win32/gyp_configuration.py
index 43253e1..eb9ec4f 100644
--- a/src/starboard/shared/win32/gyp_configuration.py
+++ b/src/starboard/shared/win32/gyp_configuration.py
@@ -13,40 +13,43 @@
 # limitations under the License.
 """Starboard win32 shared platform configuration for gyp_cobalt."""
 
-import config.base
 import logging
 import os
 import re
 import subprocess
 import sys
 
+import config.base
 import starboard.shared.win32.sdk_configuration as sdk_configuration
 from starboard.tools.paths import STARBOARD_ROOT
 from starboard.tools.testing import test_filter
 
+
 def GetWindowsVersion():
-  out = subprocess.check_output('ver', universal_newlines = True, shell=True)
-  lines = [l for l in out.split('\n') if len(l) > 0]
+  out = subprocess.check_output('ver', universal_newlines=True, shell=True)
+  lines = [l for l in out.split('\n') if l]
   for l in lines:
-    m = re.search(r"Version\s([0-9\.]+)", out)
+    m = re.search(r'Version\s([0-9\.]+)', out)
     if m and m.group(1):
       major, minor, build = m.group(1).split('.')
       return (int(major), int(minor), int(build))
-  raise IOError("Could not retrieve windows version")
+  raise IOError('Could not retrieve windows version')
+
 
 def _QuotePath(path):
   return '"' + path + '"'
 
-class PlatformConfig(config.base.PlatformConfigBase):
+
+class Win32Configuration(config.base.PlatformConfigBase):
   """Starboard Microsoft Windows platform configuration."""
 
   def __init__(self, platform):
-    super(PlatformConfig, self).__init__(platform)
+    super(Win32Configuration, self).__init__(platform)
     self.sdk = sdk_configuration.SdkConfiguration()
 
   def GetVariables(self, configuration):
     sdk = self.sdk
-    variables = super(PlatformConfig, self).GetVariables(configuration)
+    variables = super(Win32Configuration, self).GetVariables(configuration)
     variables.update({
         'visual_studio_install_path': sdk.vs_install_dir_with_version,
         'windows_sdk_path': sdk.windows_sdk_path,
@@ -96,21 +99,19 @@
     return generator_variables
 
   def GetToolchain(self):
-    sys.path.append(
-        os.path.join(STARBOARD_ROOT, 'shared', 'msvc', 'uwp'))
+    sys.path.append(os.path.join(STARBOARD_ROOT, 'shared', 'msvc', 'uwp'))
     from msvc_toolchain import MSVCUWPToolchain  # pylint: disable=g-import-not-at-top,g-bad-import-order
     return MSVCUWPToolchain()
 
   def IsWin10orHigher(self):
     try:
       # Both Win10 and Win2016-Server will return 10.0+
-      major, minor, build = GetWindowsVersion()
+      major, _, _ = GetWindowsVersion()
       return major >= 10
     except Exception as e:
-        print("Error while getting version for windows: " + str(e))
+      print 'Error while getting version for windows: ' + str(e)
     return False
 
-
   def GetTestFilters(self):
     """Gets all tests to be excluded from a unit test run.
 
@@ -119,34 +120,35 @@
     """
 
     if not self.IsWin10orHigher():
-        logging.error("Tests can only be executed on Win10 and higher.")
-        return [ test_filter.DISABLE_TESTING ]
+      logging.error('Tests can only be executed on Win10 and higher.')
+      return [test_filter.DISABLE_TESTING]
     else:
-      return [
-        # Fails on JSC.
-        test_filter.TestFilter(
-            'bindings_test', ('EvaluateScriptTest.ThreeArguments')),
-        test_filter.TestFilter(
-            'bindings_test', ('GarbageCollectionTest.*')),
+      filters = super(Win32Configuration, self).GetTestFilters()
+      for target, tests in self._FILTERED_TESTS.iteritems():
+        filters.extend(test_filter.TestFilter(target, test) for test in tests)
+      return filters
 
-        test_filter.TestFilter('nplb', test_filter.FILTER_ALL),
-        test_filter.TestFilter('poem_unittests', test_filter.FILTER_ALL),
+  _FILTERED_TESTS = {
+      'bindings_test': [
+          'EvaluateScriptTest.ThreeArguments',
+          'GarbageCollectionTest.*',
+      ],
+      'nplb': [test_filter.FILTER_ALL],
+      'nplb_blitter_pixel_tests': [test_filter.FILTER_ALL],
+      'poem_unittests': [test_filter.FILTER_ALL],
+      'starboard_platform_tests': [test_filter.FILTER_ALL],
+      'webdriver_test': [test_filter.FILTER_ALL],
 
-        # The Windows platform uses D3D9 which doesn't let you create a D3D
-        # device without a display, causing these unit tests to erroneously
-        # fail on the buildbots, so they are disabled for Windows only.
-        test_filter.TestFilter('layout_tests', test_filter.FILTER_ALL),
-        test_filter.TestFilter('renderer_test', test_filter.FILTER_ALL),
+      # The Windows platform uses D3D9 which doesn't let you create a D3D
+      # device without a display, causing these unit tests to erroneously
+      # fail on the buildbots, so they are disabled for Windows only.
+      'layout_tests': [test_filter.FILTER_ALL],
+      'renderer_test': [test_filter.FILTER_ALL],
 
-        # No network on Windows, yet.
-        test_filter.TestFilter('web_platform_tests', test_filter.FILTER_ALL),
-        test_filter.TestFilter('net_unittests', test_filter.FILTER_ALL),
+      # TODO: enable player filter tests.
+      'player_filter_tests': [test_filter.FILTER_ALL],
 
-        test_filter.TestFilter('starboard_platform_tests',
-                               test_filter.FILTER_ALL),
-        test_filter.TestFilter('nplb_blitter_pixel_tests',
-                               test_filter.FILTER_ALL),
-        test_filter.TestFilter('webdriver_test',
-                               test_filter.FILTER_ALL)
-
-      ]
+      # No network on Windows, yet.
+      'web_platform_tests': [test_filter.FILTER_ALL],
+      'net_unittests': [test_filter.FILTER_ALL],
+  }
diff --git a/src/starboard/shared/win32/socket_internal.cc b/src/starboard/shared/win32/socket_internal.cc
index b5fe460..9ae95fd 100644
--- a/src/starboard/shared/win32/socket_internal.cc
+++ b/src/starboard/shared/win32/socket_internal.cc
@@ -44,6 +44,7 @@
     SB_API_VERSION >= 9
     case WSAECONNRESET:
     case WSAENETRESET:
+    case WSAECONNABORTED:
       return kSbSocketErrorConnectionReset;
 
     // Microsoft System Error codes:
diff --git a/src/starboard/shared/win32/socket_internal.h b/src/starboard/shared/win32/socket_internal.h
index fa29128..e4a2b0a 100644
--- a/src/starboard/shared/win32/socket_internal.h
+++ b/src/starboard/shared/win32/socket_internal.h
@@ -18,6 +18,7 @@
 #include <winsock2.h>
 #include <WS2tcpip.h>
 
+#include "starboard/atomic.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/win32/auto_event_handle.h"
 #include "starboard/socket.h"
@@ -42,6 +43,7 @@
         protocol(protocol),
         socket_handle(handle),
         socket_event(WSA_INVALID_EVENT),
+        writable(0),
         error(kSbSocketOk),
         waiter(kSbSocketWaiterInvalid),
         bound_to(bound_to) {}
@@ -59,6 +61,16 @@
   // The event related to the socket_handle.  Used for SbSocketWaiter.
   sbwin32::AutoEventHandle socket_event;
 
+  // Set to true between when socket is shown as writable via WSAEventSelect/
+  // WSAWaitForMultipleEvents and when writing to the socket returns
+  // fails with WSAEWOULDBLOCK.
+  //
+  // Used to work around the fact that WSAEventSelect for FD_WRITE is
+  // edge-triggered, unlike other events.
+  //
+  // See MSDN documentato n for WSAEventSelect FD_WRITE for more info.
+  starboard::atomic_bool writable;
+
   // The last error that occurred on this socket, or kSbSocketOk.
   SbSocketError error;
 
diff --git a/src/starboard/shared/win32/socket_send_to.cc b/src/starboard/shared/win32/socket_send_to.cc
index 36cdd8b..156c2c3 100644
--- a/src/starboard/shared/win32/socket_send_to.cc
+++ b/src/starboard/shared/win32/socket_send_to.cc
@@ -46,7 +46,10 @@
     }
 
     int last_error = WSAGetLastError();
-    if (last_error != EWOULDBLOCK) {
+
+    if ((last_error == WSAEWOULDBLOCK) || (last_error == WSAECONNABORTED)) {
+      socket->writable.store(false);
+    } else {
       SB_DLOG(ERROR) << "send failed, last_error = " << last_error;
     }
     socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
diff --git a/src/starboard/shared/win32/socket_waiter_internal.cc b/src/starboard/shared/win32/socket_waiter_internal.cc
index a5b289c..a5794f1 100644
--- a/src/starboard/shared/win32/socket_waiter_internal.cc
+++ b/src/starboard/shared/win32/socket_waiter_internal.cc
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 
+#include "starboard/common/optional.h"
 #include "starboard/log.h"
 #include "starboard/shared/win32/error_utils.h"
 #include "starboard/shared/win32/socket_internal.h"
@@ -110,6 +111,13 @@
   collection.resize(new_size);
 }
 
+SbSocketWaiterInterest CombineInterests(
+    SbSocketWaiterInterest a, SbSocketWaiterInterest b) {
+  int a_int = static_cast<int>(a);
+  int b_int = static_cast<int>(b);
+  return static_cast<SbSocketWaiterInterest>(a_int | b_int);
+}
+
 }  // namespace
 
 SbSocketWaiterPrivate::SbSocketWaiterPrivate()
@@ -173,7 +181,7 @@
     return false;
   }
 
-  long network_event_interests = 0;
+  int network_event_interests = 0;
   if (interests & kSbSocketWaiterInterestRead) {
     network_event_interests |= FD_READ | FD_ACCEPT | FD_CLOSE;
   }
@@ -260,10 +268,6 @@
   }
 
   if (socket->waiter != this) {
-    SB_DLOG(ERROR) << __FUNCTION__ << ": Socket (" << socket << ") "
-                   << "is watched by Waiter (" << socket->waiter << "), "
-                   << "not this Waiter (" << this << ").";
-    SB_DSTACK(ERROR);
     return false;
   }
 
@@ -305,13 +309,34 @@
     // There should always be a wakeup event.
     SB_DCHECK(number_events > 0);
 
-    DWORD return_value = WSAWaitForMultipleEvents(
-        number_events, waitees_.GetHandleArray(), false, millis, false);
+    SbSocket maybe_writable_socket = kSbSocketInvalid;
+    for (auto& it : waitees_.GetWaitees()) {
+      if (!it) {
+        continue;
+      }
+      if ((it->interests & kSbSocketWaiterInterestWrite) == 0) {
+        continue;
+      }
+      if (it->socket->writable.load()) {
+        maybe_writable_socket = it->socket;
+        break;
+      }
+    }
 
-    if ((return_value >= WSA_WAIT_EVENT_0) &&
-        (return_value < (WSA_WAIT_EVENT_0 + number_events))) {
-      int64_t socket_index = static_cast<int64_t>(return_value) -
-                             static_cast<int64_t>(WSA_WAIT_EVENT_0);
+    bool has_writable = (maybe_writable_socket != kSbSocketInvalid);
+    DWORD return_value = WSAWaitForMultipleEvents(
+        number_events, waitees_.GetHandleArray(),
+        false, has_writable ? 0 : millis, false);
+
+    if (has_writable || ((return_value >= WSA_WAIT_EVENT_0) &&
+        (return_value < (WSA_WAIT_EVENT_0 + number_events)))) {
+      int64_t socket_index;
+      if (has_writable) {
+        socket_index = waitees_.GetIndex(maybe_writable_socket).value();
+      } else {
+        socket_index = static_cast<int64_t>(return_value) -
+                       static_cast<int64_t>(WSA_WAIT_EVENT_0);
+      }
       SB_DCHECK(socket_index >= 0);
       if (socket_index < 0) {
         SB_NOTREACHED() << "Bad socket_index. " << socket_index;
@@ -341,9 +366,16 @@
         void* context = waitee->context;
 
         // Note: this should also go before Remove().
-        const SbSocketWaiterInterest interests =
+        SbSocketWaiterInterest interests =
             DiscoverNetworkEventInterests(socket->socket_handle);
 
+        if ((waitee->interests & kSbSocketWaiterInterestWrite) &&
+              socket->writable.load()) {
+          interests = CombineInterests(interests, kSbSocketWaiterInterestWrite);
+        } else if (interests & kSbSocketWaiterInterestWrite) {
+          socket->writable.store(true);
+        }
+
         if (!waitee->persistent) {
           Remove(waitee->socket);
         }
@@ -387,11 +419,20 @@
 
 SbSocketWaiterPrivate::Waitee* SbSocketWaiterPrivate::WaiteeRegistry::GetWaitee(
     SbSocket socket) {
-  auto iterator = socket_to_index_map_.find(socket);
-  if (iterator == socket_to_index_map_.end()) {
+  starboard::optional<int64_t> token = GetIndex(socket);
+  if (!token) {
     return nullptr;
   }
-  return waitees_[iterator->second].get();
+  return waitees_[token.value()].get();
+}
+
+starboard::optional<int64_t>
+SbSocketWaiterPrivate::WaiteeRegistry::GetIndex(SbSocket socket) {
+  auto iterator = socket_to_index_map_.find(socket);
+  if (iterator == socket_to_index_map_.end()) {
+    return starboard::nullopt;
+  }
+  return iterator->second;
 }
 
 SbSocketWaiterPrivate::WaiteeRegistry::LookupToken
diff --git a/src/starboard/shared/win32/socket_waiter_internal.h b/src/starboard/shared/win32/socket_waiter_internal.h
index a2147fa..e21d93a 100644
--- a/src/starboard/shared/win32/socket_waiter_internal.h
+++ b/src/starboard/shared/win32/socket_waiter_internal.h
@@ -23,6 +23,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "starboard/common/optional.h"
 #include "starboard/mutex.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/win32/auto_event_handle.h"
@@ -104,6 +105,10 @@
 
     // Gets the Waitee associated with the given socket, or nullptr.
     Waitee* GetWaitee(SbSocket socket);
+
+    // Gets the index by socket
+    starboard::optional<int64_t> GetIndex(SbSocket socket);
+
     // Gets the Waitee by index.
     Waitee* GetWaiteeByIndex(LookupToken socket_index);
 
diff --git a/src/starboard/shared/win32/video_decoder.h b/src/starboard/shared/win32/video_decoder.h
index ffe127e..402e312 100644
--- a/src/starboard/shared/win32/video_decoder.h
+++ b/src/starboard/shared/win32/video_decoder.h
@@ -79,10 +79,18 @@
   };
 
   struct Output {
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
     Output(SbMediaTime time, const RECT& video_area,
            const ComPtr<IMFSample>& video_sample)
         : time(time), video_area(video_area), video_sample(video_sample) {}
     SbMediaTime time;
+#else   // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+    Output(SbTime time,
+           const RECT& video_area,
+           const ComPtr<IMFSample>& video_sample)
+        : time(time), video_area(video_area), video_sample(video_sample) {}
+    SbTime time;
+#endif  // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
     RECT video_area;
     ComPtr<IMFSample> video_sample;
   };
diff --git a/src/starboard/starboard.gyp b/src/starboard/starboard.gyp
index 83c284b..bdb1ef0 100644
--- a/src/starboard/starboard.gyp
+++ b/src/starboard/starboard.gyp
@@ -21,50 +21,9 @@
     {
       'target_name': 'starboard',
       'type': 'none',
-      'sources': [
-        'atomic.h',
-        'audio_sink.h',
-        'blitter.h',
-        'byte_swap.h',
-        'character.h',
-        'condition_variable.h',
-        'configuration.h',
-        'decode_target.h',
-        'directory.h',
-        'double.h',
-        'drm.h',
-        'event.h',
-        'export.h',
-        'file.h',
-        'input.h',
-        'key.h',
-        'log.h',
-        'media.h',
-        'memory.h',
-        'microphone.h',
-        'mutex.h',
-        'once.h',
-        'player.h',
-        'queue.h',
-        'socket.h',
-        'socket_waiter.h',
-        'spin_lock.h',
-        'storage.h',
-        'string.h',
-        'system.h',
-        'thread.h',
-        'thread_types.h',
-        'time.h',
-        'time_zone.h',
-        'types.h',
-        'user.h',
-        'window.h',
-        '<(DEPTH)/starboard/shared/media_session/playback_state.h',
-        # Include private headers, if present.
-        '<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "*.h")',
-      ],
       'dependencies': [
         '<(DEPTH)/<(starboard_path)/starboard_platform.gyp:starboard_platform',
+        '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
         'common/common.gyp:common',
       ],
       'export_dependent_settings': [
diff --git a/src/starboard/starboard_all.gyp b/src/starboard/starboard_all.gyp
index c6584a0..68d0488 100644
--- a/src/starboard/starboard_all.gyp
+++ b/src/starboard/starboard_all.gyp
@@ -27,6 +27,7 @@
       'type': 'none',
       'dependencies': [
         '<(DEPTH)/starboard/client_porting/eztime/eztime.gyp:*',
+        '<(DEPTH)/starboard/client_porting/eztime/eztime_test.gyp:*',
         '<(DEPTH)/starboard/client_porting/icu_init/icu_init.gyp:*',
         '<(DEPTH)/starboard/client_porting/poem/poem.gyp:*',
         '<(DEPTH)/starboard/examples/examples.gyp:*',
diff --git a/src/starboard/starboard_headers_only.gyp b/src/starboard/starboard_headers_only.gyp
new file mode 100644
index 0000000..63c7280
--- /dev/null
+++ b/src/starboard/starboard_headers_only.gyp
@@ -0,0 +1,72 @@
+# Copyright 2018 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 common "starboard_headers_only" target, like the common "starboard" target
+# but without the starboard_platform implementation. If a non-executable target
+# depends on Starboard, but is also used by Starboard ports, the target should
+# depend on this common header target. This avoids file dependency cycles for
+# targets simultaneously above and below starboard. In contrast, executable
+# targets such as test targets, must in some way depend on the common
+# "starboard" target.
+
+{
+  'targets': [
+    {
+      'target_name': 'starboard_headers_only',
+      'type': 'none',
+      'sources': [
+        'atomic.h',
+        'audio_sink.h',
+        'blitter.h',
+        'byte_swap.h',
+        'character.h',
+        'condition_variable.h',
+        'configuration.h',
+        'decode_target.h',
+        'directory.h',
+        'double.h',
+        'drm.h',
+        'event.h',
+        'export.h',
+        'file.h',
+        'input.h',
+        'key.h',
+        'log.h',
+        'media.h',
+        'memory.h',
+        'microphone.h',
+        'mutex.h',
+        'once.h',
+        'player.h',
+        'queue.h',
+        'socket.h',
+        'socket_waiter.h',
+        'spin_lock.h',
+        'storage.h',
+        'string.h',
+        'system.h',
+        'thread.h',
+        'thread_types.h',
+        'time.h',
+        'time_zone.h',
+        'types.h',
+        'user.h',
+        'window.h',
+        '<(DEPTH)/starboard/shared/media_session/playback_state.h',
+        # Include private headers, if present.
+        '<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "*.h")',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/stub/BUILD.gn b/src/starboard/stub/BUILD.gn
index dedc7f5..048a1f5 100644
--- a/src/starboard/stub/BUILD.gn
+++ b/src/starboard/stub/BUILD.gn
@@ -374,6 +374,7 @@
     "//starboard/shared/stub/system_request_suspend.cc",
     "//starboard/shared/stub/system_request_unpause.cc",
     "//starboard/shared/stub/system_sort.cc",
+    '//starboard/shared/stub/system_supports_resume.cc',
     "//starboard/shared/stub/system_symbolize.cc",
     "//starboard/shared/stub/thread_create.cc",
     "//starboard/shared/stub/thread_create_local_key.cc",
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index b6f57e4..bea5605 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -398,6 +398,13 @@
 // non-zero on platforms with webm/vp9 support.
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
 
+// Specifies whether this platform updates audio frames asynchronously.  In such
+// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
+// indicate the absolute time that the consumed audio frames are reported.
+// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
+// details.
+#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 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.
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi
index 61373cd..61fcca4 100644
--- a/src/starboard/stub/gyp_configuration.gypi
+++ b/src/starboard/stub/gyp_configuration.gypi
@@ -16,9 +16,6 @@
     'target_arch': 'x64',
     'target_os': 'linux',
 
-    'javascript_engine': 'v8',
-    'cobalt_enable_jit': 1,
-
     # No GL drivers available.
     'gl_type': 'none',
 
diff --git a/src/starboard/stub/gyp_configuration.py b/src/starboard/stub/gyp_configuration.py
index 6c3da86..5f3bd46 100644
--- a/src/starboard/stub/gyp_configuration.py
+++ b/src/starboard/stub/gyp_configuration.py
@@ -21,20 +21,26 @@
 
 def CreatePlatformConfig():
   try:
-    return PlatformConfig('stub')
+    return StubConfiguration('stub')
   except RuntimeError as e:
     logging.critical(e)
     return None
 
 
-class PlatformConfig(config.base.PlatformConfigBase):
+class StubConfiguration(config.base.PlatformConfigBase):
   """Starboard stub platform configuration."""
 
   def __init__(self, platform):
-    super(PlatformConfig, self).__init__(platform)
+    super(StubConfiguration, self).__init__(platform)
 
   def GetVariables(self, configuration):
-    return super(PlatformConfig, self).GetVariables(configuration, use_clang=1)
+    variables = super(StubConfiguration, self).GetVariables(
+        configuration, use_clang=1)
+    variables.update({
+        'javascript_engine': 'v8',
+        'cobalt_enable_jit': 1,
+    })
+    return variables
 
   def GetEnvironmentVariables(self):
     if not hasattr(self, 'host_compiler_environment'):
diff --git a/src/starboard/stub/starboard_platform.gyp b/src/starboard/stub/starboard_platform.gyp
index 1a83ffc..b03f8ce 100644
--- a/src/starboard/stub/starboard_platform.gyp
+++ b/src/starboard/stub/starboard_platform.gyp
@@ -133,13 +133,16 @@
         '<(DEPTH)/starboard/shared/stub/player_destroy.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_current_frame.cc',
         '<(DEPTH)/starboard/shared/stub/player_get_info.cc',
+        '<(DEPTH)/starboard/shared/stub/player_get_info2.cc',
         '<(DEPTH)/starboard/shared/stub/player_output_mode_supported.cc',
         '<(DEPTH)/starboard/shared/stub/player_seek.cc',
+        '<(DEPTH)/starboard/shared/stub/player_seek2.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_bounds.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_playback_rate.cc',
         '<(DEPTH)/starboard/shared/stub/player_set_volume.cc',
         '<(DEPTH)/starboard/shared/stub/player_write_end_of_stream.cc',
         '<(DEPTH)/starboard/shared/stub/player_write_sample.cc',
+        '<(DEPTH)/starboard/shared/stub/player_write_sample2.cc',
         '<(DEPTH)/starboard/shared/stub/socket_accept.cc',
         '<(DEPTH)/starboard/shared/stub/socket_bind.cc',
         '<(DEPTH)/starboard/shared/stub/socket_clear_last_error.cc',
@@ -234,6 +237,7 @@
         '<(DEPTH)/starboard/shared/stub/system_request_suspend.cc',
         '<(DEPTH)/starboard/shared/stub/system_request_unpause.cc',
         '<(DEPTH)/starboard/shared/stub/system_sort.cc',
+        '<(DEPTH)/starboard/shared/stub/system_supports_resume.cc',
         '<(DEPTH)/starboard/shared/stub/system_symbolize.cc',
         '<(DEPTH)/starboard/shared/stub/thread_create.cc',
         '<(DEPTH)/starboard/shared/stub/thread_create_local_key.cc',
diff --git a/src/starboard/system.h b/src/starboard/system.h
index c330cf1..bf2c68e 100644
--- a/src/starboard/system.h
+++ b/src/starboard/system.h
@@ -534,6 +534,17 @@
 // from any thread and must be idempotent.
 SB_EXPORT void SbSystemHideSplashScreen();
 
+#if SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
+// Returns false if the platform doesn't need resume after suspend support. In
+// such case Cobalt will free up the resource it retains for resume after
+// suspend.
+// Note that if this function returns false, the Starboard implementation cannot
+// send kSbEventTypeResume to the event handler.
+// The return value of this function cannot change over the life time of the
+// application.
+bool SbSystemSupportsResume();
+#endif  // SB_API_VERSION >= SB_ALLOW_DISABLE_RESUME_VERSION
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/starboard/win/shared/configuration_public.h b/src/starboard/win/shared/configuration_public.h
index 73294a3..fc860f0 100644
--- a/src/starboard/win/shared/configuration_public.h
+++ b/src/starboard/win/shared/configuration_public.h
@@ -49,6 +49,8 @@
 #define SB_HAS_64_BIT_LONG 0
 #define SB_HAS_64_BIT_POINTERS 1
 
+#define SB_HAS_SOCKET_ERROR_CONNECTION_RESET_SUPPORT 1
+
 // 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
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi
index 611c9d8..36bc281 100644
--- a/src/starboard/win/shared/starboard_platform.gypi
+++ b/src/starboard/win/shared/starboard_platform.gypi
@@ -20,6 +20,8 @@
     'uwp_incompatible_win32': [
       '<(DEPTH)/starboard/shared/win32/application_win32_key_event.cc',
       '<(DEPTH)/starboard/shared/win32/application_win32.cc',
+      '<(DEPTH)/starboard/shared/win32/decode_target_internal.cc',
+      '<(DEPTH)/starboard/shared/win32/decode_target_internal.h',
       '<(DEPTH)/starboard/shared/win32/dialog.cc',
       '<(DEPTH)/starboard/shared/win32/get_home_directory.cc',
       '<(DEPTH)/starboard/shared/win32/log_file_impl.cc',
@@ -27,6 +29,8 @@
       '<(DEPTH)/starboard/shared/win32/log_raw.cc',
       '<(DEPTH)/starboard/shared/win32/log_raw_format.cc',
       '<(DEPTH)/starboard/shared/win32/media_is_audio_supported.cc',
+      '<(DEPTH)/starboard/shared/win32/media_is_video_supported.cc',
+      '<(DEPTH)/starboard/shared/win32/player_components_impl.cc',
       '<(DEPTH)/starboard/shared/win32/playready_license.cc',
       '<(DEPTH)/starboard/shared/win32/starboard_main.cc',
       '<(DEPTH)/starboard/shared/win32/system_clear_platform_error.cc',
@@ -66,8 +70,6 @@
       '<(DEPTH)/starboard/shared/win32/audio_decoder_thread.h',
       '<(DEPTH)/starboard/shared/win32/audio_transform.cc',
       '<(DEPTH)/starboard/shared/win32/audio_transform.h',
-      '<(DEPTH)/starboard/shared/win32/decode_target_internal.cc',
-      '<(DEPTH)/starboard/shared/win32/decode_target_internal.h',
       '<(DEPTH)/starboard/shared/win32/decrypting_decoder.cc',
       '<(DEPTH)/starboard/shared/win32/decrypting_decoder.h',
       '<(DEPTH)/starboard/shared/win32/dx_context_video_decoder.cc',
@@ -76,11 +78,9 @@
       '<(DEPTH)/starboard/shared/win32/media_common.h',
       '<(DEPTH)/starboard/shared/win32/media_foundation_utils.cc',
       '<(DEPTH)/starboard/shared/win32/media_foundation_utils.h',
-      '<(DEPTH)/starboard/shared/win32/media_is_video_supported.cc',
       '<(DEPTH)/starboard/shared/win32/media_is_supported.cc',
       '<(DEPTH)/starboard/shared/win32/media_transform.cc',
       '<(DEPTH)/starboard/shared/win32/media_transform.h',
-      '<(DEPTH)/starboard/shared/win32/player_components_impl.cc',
       '<(DEPTH)/starboard/shared/win32/video_decoder.cc',
       '<(DEPTH)/starboard/shared/win32/video_decoder.h',
       '<(DEPTH)/starboard/shared/win32/win32_audio_decoder.cc',
@@ -119,16 +119,19 @@
       '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
       '<(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/player_write_sample2.cc',
       '<(DEPTH)/starboard/shared/stub/media_is_transfer_characteristics_supported.cc',
 
       # Shared renderers
@@ -409,7 +412,6 @@
         # Include private stubs, if present.
         '<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "shared/stub/*.cc")',
         '<@(starboard_platform_dependent_files)',
-
       ],
       'defines': [
         # This must be defined when building Starboard, and must not when
diff --git a/src/starboard/win/win32/gyp_configuration.py b/src/starboard/win/win32/gyp_configuration.py
index 1aa4d7c..8e3f89d 100644
--- a/src/starboard/win/win32/gyp_configuration.py
+++ b/src/starboard/win/win32/gyp_configuration.py
@@ -24,10 +24,11 @@
 sys.path.append(
     os.path.realpath(
         os.path.join(
-            os.path.dirname(__file__), os.pardir,
-            os.pardir, 'shared', 'win32')))
+            os.path.dirname(__file__), os.pardir, os.pardir, 'shared',
+            'win32')))
 import gyp_configuration
 
+
 def CreatePlatformConfig():
   try:
     win_lib_config = WinWin32PlatformConfig('win-win32')
@@ -36,7 +37,8 @@
     logging.critical(e)
     return None
 
-class WinWin32PlatformConfig(gyp_configuration.PlatformConfig):
+
+class WinWin32PlatformConfig(gyp_configuration.Win32Configuration):
   """Starboard win-32 platform configuration."""
 
   def __init__(self, platform):
@@ -44,8 +46,8 @@
 
   def GetLauncher(self):
     """Gets the module used to launch applications on this platform."""
-    module_path = os.path.abspath(os.path.join(
-        os.path.dirname(__file__), 'launcher.py'))
+    module_path = os.path.abspath(
+        os.path.join(os.path.dirname(__file__), 'launcher.py'))
 
     launcher_module = imp.load_source('launcher', module_path)
     return launcher_module
diff --git a/src/starboard/win/win32/lib/gyp_configuration.py b/src/starboard/win/win32/lib/gyp_configuration.py
index 0d1deeb..efcffaa 100644
--- a/src/starboard/win/win32/lib/gyp_configuration.py
+++ b/src/starboard/win/win32/lib/gyp_configuration.py
@@ -11,6 +11,7 @@
 # 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 win-win32-lib platform configuration for gyp_cobalt."""
 
 import logging
 import os
@@ -20,14 +21,14 @@
 sys.path.append(
     os.path.realpath(
         os.path.join(
-            os.path.dirname(__file__), os.pardir,
-            os.pardir, os.pardir, 'shared', 'win32')))
+            os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
+            'shared', 'win32')))
 import gyp_configuration
 
 
 def CreatePlatformConfig():
   try:
-    return gyp_configuration.PlatformConfig('win-win32-lib')
+    return gyp_configuration.Win32Configuration('win-win32-lib')
   except RuntimeError as e:
     logging.critical(e)
     return None
diff --git a/src/starboard/win/win32/lib/starboard_platform_tests.gyp b/src/starboard/win/win32/lib/starboard_platform_tests.gyp
index 8961deb..66fc40e 100644
--- a/src/starboard/win/win32/lib/starboard_platform_tests.gyp
+++ b/src/starboard/win/win32/lib/starboard_platform_tests.gyp
@@ -38,7 +38,7 @@
       'variables': {
         'executable_name': 'starboard_platform_tests',
       },
-      'includes': [ '../../../build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/win/win32/starboard_platform.gypi b/src/starboard/win/win32/starboard_platform.gypi
index 225e479..26bbd6b 100644
--- a/src/starboard/win/win32/starboard_platform.gypi
+++ b/src/starboard/win/win32/starboard_platform.gypi
@@ -21,17 +21,13 @@
       'configuration_public.h',
       'thread_types_public.h',
       '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
-      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
       '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
       '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_supports_resume.cc',
       '<(DEPTH)/starboard/shared/stub/media_is_output_protected.cc',
       '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
       '<(DEPTH)/starboard/win/shared/system_get_path.cc',
diff --git a/src/starboard/win/win32/starboard_platform_tests.gyp b/src/starboard/win/win32/starboard_platform_tests.gyp
index 1eb83ba..66fc40e 100644
--- a/src/starboard/win/win32/starboard_platform_tests.gyp
+++ b/src/starboard/win/win32/starboard_platform_tests.gyp
@@ -38,7 +38,7 @@
       'variables': {
         'executable_name': 'starboard_platform_tests',
       },
-      'includes': [ '../../build/deploy.gypi' ],
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/third_party/QR-Code-generator/cpp/BitBuffer.cpp b/src/third_party/QR-Code-generator/cpp/BitBuffer.cpp
index d6e8cb2..4dc9f7b 100644
--- a/src/third_party/QR-Code-generator/cpp/BitBuffer.cpp
+++ b/src/third_party/QR-Code-generator/cpp/BitBuffer.cpp
@@ -23,6 +23,9 @@
 
 #include "BitBuffer.hpp"
 
+#include "starboard/log.h"
+
+#define throw SB_CHECK(false) <<
 
 namespace qrcodegen {
 
diff --git a/src/third_party/QR-Code-generator/cpp/QrCode.cpp b/src/third_party/QR-Code-generator/cpp/QrCode.cpp
index 75a5473..c73cf9c 100644
--- a/src/third_party/QR-Code-generator/cpp/QrCode.cpp
+++ b/src/third_party/QR-Code-generator/cpp/QrCode.cpp
@@ -30,6 +30,10 @@
 #include "BitBuffer.hpp"
 #include "QrCode.hpp"
 
+#include "starboard/log.h"
+
+#define throw SB_CHECK(false) <<
+
 using std::int8_t;
 using std::uint8_t;
 using std::size_t;
@@ -46,6 +50,7 @@
 		case Ecc::HIGH    :  return 2;
 		default:  throw "Assertion error";
 	}
+	return 0;
 }
 
 
@@ -490,11 +495,12 @@
 
 
 vector<int> QrCode::getAlignmentPatternPositions(int ver) {
-	if (ver < MIN_VERSION || ver > MAX_VERSION)
+	if (ver < MIN_VERSION || ver > MAX_VERSION) {
 		throw "Version number out of range";
-	else if (ver == 1)
 		return vector<int>();
-	else {
+	} else if (ver == 1) {
+		return vector<int>();
+	} else {
 		int numAlign = ver / 7 + 2;
 		int step;
 		if (ver != 32) {
diff --git a/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorWorker.cpp b/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorWorker.cpp
index 2e14607..aba5a24 100644
--- a/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorWorker.cpp
+++ b/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorWorker.cpp
@@ -33,6 +33,8 @@
 #include <vector>
 #include "QrCode.hpp"
 
+#include "starboard/string.h"
+
 using qrcodegen::QrCode;
 using qrcodegen::QrSegment;
 
@@ -92,7 +94,7 @@
 			}
 			
 		} catch (const char *msg) {
-			if (strcmp(msg, "Data too long") != 0) {
+			if (SbStringCompare(msg, "Data too long") != 0) {
 				std::cerr << msg << std::endl;
 				return EXIT_FAILURE;
 			}
diff --git a/src/third_party/QR-Code-generator/cpp/QrSegment.cpp b/src/third_party/QR-Code-generator/cpp/QrSegment.cpp
index f711461..4accb09 100644
--- a/src/third_party/QR-Code-generator/cpp/QrSegment.cpp
+++ b/src/third_party/QR-Code-generator/cpp/QrSegment.cpp
@@ -26,6 +26,10 @@
 #include <utility>
 #include "QrSegment.hpp"
 
+#include "starboard/log.h"
+
+#define throw SB_CHECK(false) <<
+
 using std::uint8_t;
 using std::vector;
 
@@ -50,6 +54,7 @@
 	else if (10 <= ver && ver <= 26)  return numBitsCharCount[1];
 	else if (27 <= ver && ver <= 40)  return numBitsCharCount[2];
 	else  throw "Version number out of range";
+	return 0;
 }
 
 
diff --git a/src/third_party/QR-Code-generator/qr_code_generator.gyp b/src/third_party/QR-Code-generator/qr_code_generator.gyp
new file mode 100644
index 0000000..29cdd4a
--- /dev/null
+++ b/src/third_party/QR-Code-generator/qr_code_generator.gyp
@@ -0,0 +1,34 @@
+# Copyright 2018 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'qr_code_generator',
+      'type': 'static_library',
+      'include_dirs': ['.'],
+      'sources': [
+        'cpp/BitBuffer.cpp',
+        'cpp/BitBuffer.hpp',
+        'cpp/QrCode.cpp',
+        'cpp/QrCode.hpp',
+        'cpp/QrSegment.cpp',
+        'cpp/QrSegment.hpp',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+      ],
+    },
+  ],
+}
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 518b65e..0948858 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -1307,6 +1307,8 @@
         case DXGI_FORMAT_R16G16B16A16_FLOAT:
         case DXGI_FORMAT_R32G32B32A32_FLOAT:
         case DXGI_FORMAT_NV12:
+        case DXGI_FORMAT_R8_UNORM:
+        case DXGI_FORMAT_R16_UNORM:
             break;
 
         default:
diff --git a/src/third_party/icu/icu.gyp b/src/third_party/icu/icu.gyp
index 02e6c21..39892f7 100644
--- a/src/third_party/icu/icu.gyp
+++ b/src/third_party/icu/icu.gyp
@@ -298,7 +298,7 @@
                 'U_HAVE_NL_LANGINFO=0'
               ],
               'dependencies': [
-                '<(DEPTH)/starboard/starboard.gyp:starboard',
+                '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
                ],
             }],
             ['(OS=="lb_shell" or OS=="starboard") and (target_os=="android" or target_os=="linux" or clang==1)', {
@@ -441,7 +441,7 @@
             }],
             ['OS=="starboard"', {
               'dependencies': [
-                '<(DEPTH)/starboard/starboard.gyp:starboard',
+                '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
                ],
             }],
             ['(OS=="lb_shell" or OS=="starboard") and (target_os=="android" or target_os=="linux" or clang==1)', {
diff --git a/src/third_party/mozjs-45/js/src/vm/GlobalObject.cpp b/src/third_party/mozjs-45/js/src/vm/GlobalObject.cpp
index 685299d..8519b5d 100644
--- a/src/third_party/mozjs-45/js/src/vm/GlobalObject.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/GlobalObject.cpp
@@ -420,10 +420,23 @@
            JS_DefineFunctions(cx, global, builtins);
 }
 
+#if defined(COBALT)
 /* static */ bool
-GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global)
+GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx, Handle<GlobalObject*> global)
 {
-    HeapSlot& v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED);
+    /*
+     * For Cobalt, do not cache the value. Allow the callback to be triggered
+     * every time so we can do proper CSP reporting.
+     */
+    JSCSPEvalChecker allows = cx->runtime()->securityCallbacks->contentSecurityPolicyAllows;
+    return !allows || allows(cx);
+}
+#else
+
+/* static */ bool
+GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx, Handle<GlobalObject*> global)
+{
+    HeapSlot &v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED);
     if (v.isUndefined()) {
         /*
          * If there are callbacks, make sure that the CSP callback is installed
@@ -435,6 +448,7 @@
     }
     return !v.isFalse();
 }
+#endif
 
 /* static */ bool
 GlobalObject::warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag,
diff --git a/src/third_party/openssl/openssl.gyp b/src/third_party/openssl/openssl.gyp
index ba790a5..f15fc56 100644
--- a/src/third_party/openssl/openssl.gyp
+++ b/src/third_party/openssl/openssl.gyp
@@ -683,6 +683,7 @@
           },
           'dependencies': [
             '<(DEPTH)/starboard/client_porting/eztime/eztime.gyp:eztime',
+            '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
           ],
         }],
         ['OS=="lb_shell"', {
diff --git a/src/third_party/openssl/openssl_workaround_starboard_dependency.gyp b/src/third_party/openssl/openssl_workaround_starboard_dependency.gyp
deleted file mode 100644
index 98bf92e..0000000
--- a/src/third_party/openssl/openssl_workaround_starboard_dependency.gyp
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2017 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-{
-  'targets': [
-    # TODO: Eliminate cyclic dependency between Widevine, OpenSSL, and Starboard
-    #       and get rid of this target in favor of 'openssl.gyp:openssl'.
-    {
-      'target_name': 'openssl_workaround_starboard_dependency',
-      'type': '<(library)',
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '<(DEPTH)/third_party/openssl/config/starboard',
-          '<(DEPTH)/third_party/openssl/openssl/include',
-        ],
-      },
-    },
-  ],
-}
diff --git a/src/third_party/quirc/LICENSE b/src/third_party/quirc/LICENSE
new file mode 100644
index 0000000..d47c026
--- /dev/null
+++ b/src/third_party/quirc/LICENSE
@@ -0,0 +1,16 @@
+quirc -- QR-code recognition library
+Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for
+any purpose with or without fee is hereby granted, provided that the
+above copyright notice and this permission notice appear in all
+copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/src/third_party/quirc/Makefile b/src/third_party/quirc/Makefile
new file mode 100644
index 0000000..6f5c2ec
--- /dev/null
+++ b/src/third_party/quirc/Makefile
@@ -0,0 +1,88 @@
+# quirc -- QR-code recognition library
+# Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+CC ?= gcc
+PREFIX ?= /usr/local
+SDL_CFLAGS != pkg-config --cflags sdl
+SDL_LIBS != pkg-config --libs sdl
+
+LIB_VERSION = 1.0
+
+CFLAGS ?= -O3 -Wall -fPIC
+QUIRC_CFLAGS = -Ilib $(CFLAGS) $(SDL_CFLAGS)
+LIB_OBJ = \
+    lib/decode.o \
+    lib/identify.o \
+    lib/quirc.o \
+    lib/version_db.o
+DEMO_OBJ = \
+    demo/camera.o \
+    demo/mjpeg.o \
+    demo/convert.o \
+    demo/dthash.o \
+    demo/demoutil.o
+
+all: libquirc.so qrtest inspect quirc-demo quirc-scanner
+
+qrtest: tests/dbgutil.o tests/qrtest.o libquirc.a
+	$(CC) -o $@ tests/dbgutil.o tests/qrtest.o libquirc.a $(LDFLAGS) -lm -ljpeg -lpng
+
+inspect: tests/dbgutil.o tests/inspect.o libquirc.a
+	$(CC) -o $@ tests/dbgutil.o tests/inspect.o libquirc.a $(LDFLAGS) -lm -ljpeg -lpng $(SDL_LIBS) -lSDL_gfx
+
+quirc-demo: $(DEMO_OBJ) demo/demo.o libquirc.a
+	$(CC) -o $@ $(DEMO_OBJ) demo/demo.o libquirc.a $(LDFLAGS) -lm -ljpeg $(SDL_LIBS) -lSDL_gfx
+
+quirc-scanner: $(DEMO_OBJ) demo/scanner.o libquirc.a
+	$(CC) -o $@ $(DEMO_OBJ) demo/scanner.o libquirc.a $(LDFLAGS) -lm -ljpeg
+
+libquirc.a: $(LIB_OBJ)
+	rm -f $@
+	ar cru $@ $(LIB_OBJ)
+	ranlib $@
+
+.PHONY: libquirc.so
+libquirc.so: libquirc.so.$(LIB_VERSION)
+
+libquirc.so.$(LIB_VERSION): $(LIB_OBJ)
+	$(CC) -shared -o $@ $(LIB_OBJ) $(LDFLAGS) -lm
+
+.c.o:
+	$(CC) $(QUIRC_CFLAGS) -o $@ -c $<
+
+install: libquirc.a libquirc.so.$(LIB_VERSION) quirc-demo quirc-scanner
+	install -o root -g root -m 0644 lib/quirc.h $(DESTDIR)$(PREFIX)/include
+	install -o root -g root -m 0644 libquirc.a $(DESTDIR)$(PREFIX)/lib
+	install -o root -g root -m 0755 libquirc.so.$(LIB_VERSION) \
+		$(DESTDIR)$(PREFIX)/lib
+	install -o root -g root -m 0755 quirc-demo $(DESTDIR)$(PREFIX)/bin
+	install -o root -g root -m 0755 quirc-scanner $(DESTDIR)$(PREFIX)/bin
+
+uninstall:
+	rm -f $(DESTDIR)$(PREFIX)/include/quirc.h
+	rm -f $(DESTDIR)$(PREFIX)/lib/libquirc.so.$(LIB_VERSION)
+	rm -f $(DESTDIR)$(PREFIX)/lib/libquirc.a
+	rm -f $(DESTDIR)$(PREFIX)/bin/quirc-demo
+	rm -f $(DESTDIR)$(PREFIX)/bin/quirc-scanner
+
+clean:
+	rm -f */*.o
+	rm -f */*.lo
+	rm -f libquirc.a
+	rm -f libquirc.so.$(LIB_VERSION)
+	rm -f qrtest
+	rm -f inspect
+	rm -f quirc-demo
+	rm -f quirc-scanner
diff --git a/src/third_party/quirc/README b/src/third_party/quirc/README
new file mode 100644
index 0000000..35f45c4
--- /dev/null
+++ b/src/third_party/quirc/README
@@ -0,0 +1,188 @@
+Quirc
+=====
+
+QR codes are a type of high-density matrix barcodes, and quirc is a
+library for extracting and decoding them from images. It has several
+features which make it a good choice for this purpose:
+
+  * It is fast enough to be used with realtime video: extracting and
+    decoding from VGA frame takes about 50 ms on a modern x86 core.
+
+  * It has a robust and tolerant recognition algorithm. It can
+    correctly recognise and decode QR codes which are rotated and/or
+    oblique to the camera. It can also distinguish and decode multiple
+    codes within the same image.
+
+  * It is easy to use, with a simple API described in a single
+    commented header file (see below for an overview).
+
+  * It is small and easily embeddable, with no dependencies other than
+    standard C functions.
+
+  * It has a very small memory footprint: one byte per image pixel,
+    plus a few kB per decoder object.
+
+  * It uses no global mutable state, and is safe to use in a
+    multithreaded application.
+
+  * BSD-licensed, with almost no restrictions regarding use and/or
+    modification.
+
+The distribution comes with, in addition to the library, several test
+programs. While the core library is very portable, these programs have
+some additional dependencies. All of them require libjpeg, and two
+(``quirc-demo`` and ``inspect``) require SDL. The camera demos use
+Linux-specific APIs:
+
+``quirc-demo``
+
+  ~ This is an real-time demo which requires a camera and a graphical
+    display. The video stream is displayed on screen as it's received,
+    and any QR codes recognised are highlighted in the image, with the
+    decoded information both displayed on the image and printed on
+    stdout.
+
+``quirc-scanner``
+
+  ~ This program turns your camera into a barcode scanner. It's almost
+    the same as the ``demo`` application, but it doesn't display the
+    video stream, and thus doesn't require a graphical display.
+
+``qrtest``
+
+  ~ This test is used to evaluate the performance of library. Given a
+    directory tree containing a bunch of JPEG images, it will attempt
+    to locate and decode QR codes in each image. Speed and success
+    statistics are collected and printed on stdout.
+
+``inspect``
+
+  ~ This test is used for debugging. Given a single JPEG image, it
+    will display a diagram showing the internal state of the decoder
+    as well as printing additional information on stdout.
+
+Installation
+------------
+
+To build the library and associated demos/tests, type ``make``. Type
+``make install`` to install the library, header file and camera demos.
+
+You can specify one or several of the following targets if you don't
+want, or are unable to build everything:
+
+  * libquirc.a
+  * libquirc.so
+  * qrtest
+  * inspect
+  * quirc-scanner
+  * quirc-demo
+
+Library use
+-----------
+
+All of the library's functionality is exposed through a single header
+file, which you should include:
+
+    #include <quirc.h>
+
+To decode images, you'll need to instantiate a ``struct quirc``
+object, which is done with the ``quirc_new`` function. Later, when you
+no longer need to decode anything, you should release the allocated
+memory with ``quirc_destroy``:
+
+    struct quirc *qr;
+
+    qr = quirc_new();
+    if (!qr) {
+	    perror("Failed to allocate memory");
+	    abort();
+    }
+
+    /* ... */
+
+    quirc_destroy(qr);
+
+Having obtained a decoder object, you need to set the image size that
+you'll be working with, which is done using ``quirc_resize``:
+
+    if (quirc_resize(qr, 640, 480) < 0) {
+	    perror("Failed to allocate video memory");
+	    abort();
+    }
+
+``quirc_resize`` and ``quirc_new`` are the only library functions
+which allocate memory. If you plan to process a series of frames (or a
+video stream), you probably want to allocate and size a single decoder
+and hold onto it to process each frame.
+
+Processing frames is done in two stages. The first stage is an
+image-recognition stage called identification, which takes a grayscale
+image and searches for QR codes. Using ``quirc_begin`` and
+``quirc_end``, you can feed a grayscale image directly into the buffer
+that ``quirc`` uses for image processing:
+
+    uint8_t *image;
+    int w, h;
+
+    image = quirc_begin(qr, &w, &h);
+
+    /* Fill out the image buffer here.
+     * image is a pointer to a w*h bytes.
+     * One byte per pixel, w pixels per line, h lines in the buffer.
+     */
+
+    quirc_end(qr);
+
+Note that ``quirc_begin`` simply returns a pointer to a previously
+allocated buffer. The buffer will contain uninitialized data. After
+the call to ``quirc_end``, the decoder holds a list of detected QR
+codes which can be queried via ``quirc_count`` and ``quirc_extract``.
+
+At this point, the second stage of processing occurs -- decoding. This
+is done via the call to ``quirc_decode``, which is not associated with
+a decoder object.
+
+    int num_codes;
+    int i;
+
+    /* We've previously fed an image to the decoder via
+     * quirc_begin/quirc_end.
+     */
+
+    num_codes = quirc_count(qr);
+    for (i = 0; i < num_codes; i++) {
+	    struct quirc_code code;
+	    struct quirc_data data;
+	    quirc_decode_error_t err;
+
+	    quirc_extract(qr, i, &code);
+
+	    /* Decoding stage */
+	    err = quirc_decode(&code, &data);
+	    if (err)
+		    printf("DECODE FAILED: %s\n", quirc_strerror(err));
+	    else
+		    printf("Data: %s\n", data.payload);
+    }
+
+``quirc_code`` and ``quirc_data`` are flat structures which don't need
+to be initialized or freed after use.
+
+Copyright
+---------
+
+Copyright (C) 2010-2012 Daniel Beer <<dlbeer@gmail.com>>
+
+Permission to use, copy, modify, and/or distribute this software for
+any purpose with or without fee is hereby granted, provided that the
+above copyright notice and this permission notice appear in all
+copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/src/third_party/quirc/demo/camera.c b/src/third_party/quirc/demo/camera.c
new file mode 100644
index 0000000..8e8de6b
--- /dev/null
+++ b/src/third_party/quirc/demo/camera.c
@@ -0,0 +1,591 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#ifdef __OpenBSD__
+#include <sys/videoio.h>
+#else
+#include <linux/videodev2.h>
+#endif
+#include "camera.h"
+
+/************************************************************************
+ * Fraction arithmetic
+ */
+
+static int gcd(int a, int b)
+{
+	if (a < 0)
+		a = -a;
+	if (b < 0)
+		b = -b;
+
+	for (;;) {
+		if (a < b) {
+			const int t = a;
+
+			a = b;
+			b = t;
+		}
+
+		if (!b)
+			break;
+
+		a %= b;
+	}
+
+	return a;
+}
+
+static void frac_reduce(const struct v4l2_fract *f, struct v4l2_fract *g)
+{
+	const int x = gcd(f->numerator, f->denominator);
+	int n = f->numerator;
+	int d = f->denominator;
+
+	if (d < 0) {
+		n = -n;
+		d = -d;
+	}
+
+	g->numerator = n / x;
+	g->denominator = d / x;
+}
+
+static void frac_add(const struct v4l2_fract *a, const struct v4l2_fract *b,
+		     struct v4l2_fract *r)
+{
+	r->numerator = a->numerator * b->denominator +
+		       b->numerator * b->denominator;
+	r->denominator = a->denominator * b->denominator;
+
+	frac_reduce(r, r);
+}
+
+static void frac_sub(const struct v4l2_fract *a, const struct v4l2_fract *b,
+		     struct v4l2_fract *r)
+{
+	r->numerator = a->numerator * b->denominator -
+		       b->numerator * b->denominator;
+	r->denominator = a->denominator * b->denominator;
+
+	frac_reduce(r, r);
+}
+
+static int frac_cmp(const struct v4l2_fract *a, const struct v4l2_fract *b)
+{
+	return a->numerator * b->denominator - b->numerator * b->denominator;
+}
+
+static void frac_mul(const struct v4l2_fract *a, const struct v4l2_fract *b,
+		     struct v4l2_fract *r)
+{
+	r->numerator = a->numerator * b->numerator;
+	r->denominator = a->denominator * b->denominator;
+
+	frac_reduce(r, r);
+}
+
+static void frac_div(const struct v4l2_fract *a, const struct v4l2_fract *b,
+		     struct v4l2_fract *r)
+{
+	r->numerator = a->numerator * b->denominator;
+	r->denominator = a->denominator * b->numerator;
+
+	frac_reduce(r, r);
+}
+
+static int frac_cmp_ref(const struct v4l2_fract *ref,
+			const struct v4l2_fract *a,
+			const struct v4l2_fract *b)
+{
+	struct v4l2_fract da;
+	struct v4l2_fract db;
+
+	frac_sub(a, ref, &da);
+	frac_sub(b, ref, &db);
+
+	if (da.numerator < 0)
+		da.numerator = -da.numerator;
+	if (db.numerator < 0)
+		db.numerator = -db.numerator;
+
+	return frac_cmp(&da, &db);
+}
+
+/************************************************************************
+ * Parameter searching and choosing
+ */
+
+static camera_format_t map_fmt(uint32_t pf)
+{
+	if (pf == V4L2_PIX_FMT_YUYV)
+		return CAMERA_FORMAT_YUYV;
+
+	if (pf == V4L2_PIX_FMT_MJPEG)
+		return CAMERA_FORMAT_MJPEG;
+
+	return CAMERA_FORMAT_UNKNOWN;
+}
+
+static int find_best_format(int fd, uint32_t *fmt_ret)
+{
+	struct v4l2_fmtdesc best;
+	int i = 1;
+
+	memset(&best, 0, sizeof(best));
+	best.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	best.index = 0;
+
+	if (ioctl(fd, VIDIOC_ENUM_FMT, &best) < 0)
+		return -1;
+
+	for (;;) {
+		struct v4l2_fmtdesc f;
+
+		memset(&f, 0, sizeof(f));
+		f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		f.index = ++i;
+
+		if (ioctl(fd, VIDIOC_ENUM_FMT, &f) < 0)
+			break;
+
+		if (map_fmt(f.pixelformat) > map_fmt(best.pixelformat))
+			memcpy(&best, &f, sizeof(best));
+	}
+
+	if (fmt_ret)
+		*fmt_ret = best.pixelformat;
+
+	return 0;
+}
+
+static int step_to_discrete(int min, int max, int step, int target)
+{
+	int offset;
+
+	if (target < min)
+		return min;
+
+	if (target > max)
+		return max;
+
+	offset = (target - min) % step;
+	if ((offset * 2 > step) && (target + step <= max))
+		target += step;
+
+	return target - offset;
+}
+
+static int score_discrete(const struct v4l2_frmsizeenum *f, int w, int h)
+{
+	const int dw = f->discrete.width - w;
+	const int dh = f->discrete.height - h;
+
+	return dw * dw + dh * dh;
+}
+
+static int find_best_size(int fd, uint32_t pixel_format,
+			  int target_w, int target_h,
+			  int *ret_w, int *ret_h)
+{
+	struct v4l2_frmsizeenum best;
+	int i = 1;
+
+	memset(&best, 0, sizeof(best));
+	best.index = 0;
+	best.pixel_format = pixel_format;
+
+	if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &best) < 0)
+		return -1;
+
+	if (best.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
+		*ret_w = step_to_discrete(best.stepwise.min_width,
+					  best.stepwise.max_width,
+					  best.stepwise.step_width,
+					  target_w);
+		*ret_h = step_to_discrete(best.stepwise.min_height,
+					  best.stepwise.max_height,
+					  best.stepwise.step_height,
+					  target_h);
+		return 0;
+	}
+
+	for (;;) {
+		struct v4l2_frmsizeenum f;
+
+		memset(&f, 0, sizeof(f));
+		f.index = ++i;
+		f.pixel_format = pixel_format;
+
+		if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &f) < 0)
+			break;
+
+		if (score_discrete(&f, target_w, target_h) <
+		    score_discrete(&best, target_w, target_h))
+			memcpy(&best, &f, sizeof(best));
+	}
+
+	*ret_w = best.discrete.width;
+	*ret_h = best.discrete.height;
+
+	return 0;
+}
+
+static int find_best_rate(int fd, uint32_t pixel_format,
+			  int w, int h, int target_n, int target_d,
+			  int *ret_n, int *ret_d)
+{
+	const struct v4l2_fract target = {
+		.numerator = target_n,
+		.denominator = target_d
+	};
+	struct v4l2_frmivalenum best;
+	int i = 1;
+
+	memset(&best, 0, sizeof(best));
+	best.index = 0;
+	best.pixel_format = pixel_format;
+	best.width = w;
+	best.height = h;
+
+	if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &best) < 0)
+		return -1;
+
+	if (best.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
+		struct v4l2_fract t;
+
+		if (frac_cmp(&target, &best.stepwise.min) < 0) {
+			*ret_n = best.stepwise.min.numerator;
+			*ret_d = best.stepwise.min.denominator;
+		}
+
+		if (frac_cmp(&target, &best.stepwise.max) > 0) {
+			*ret_n = best.stepwise.max.numerator;
+			*ret_d = best.stepwise.max.denominator;
+		}
+
+		frac_sub(&target, &best.stepwise.min, &t);
+		frac_div(&t, &best.stepwise.step, &t);
+		if (t.numerator * 2 >= t.denominator)
+			t.numerator += t.denominator;
+		t.numerator /= t.denominator;
+		t.denominator = 1;
+		frac_mul(&t, &best.stepwise.step, &t);
+		frac_add(&t, &best.stepwise.max, &t);
+
+		if (frac_cmp(&t, &best.stepwise.max) > 0) {
+			*ret_n = best.stepwise.max.numerator;
+			*ret_d = best.stepwise.max.denominator;
+		} else {
+			*ret_n = t.numerator;
+			*ret_d = t.denominator;
+		}
+
+		return 0;
+	}
+
+	for (;;) {
+		struct v4l2_frmivalenum f;
+
+		memset(&f, 0, sizeof(f));
+		f.index = ++i;
+		f.pixel_format = pixel_format;
+		f.width = w;
+		f.height = h;
+
+		if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &f) < 0)
+			break;
+
+		if (frac_cmp_ref(&target, &f.discrete, &best.discrete) < 0)
+			memcpy(&best, &f, sizeof(best));
+	}
+
+	*ret_n = best.discrete.numerator;
+	*ret_d = best.discrete.denominator;
+
+	return 0;
+}
+
+/************************************************************************
+ * Public interface
+ */
+
+void camera_init(struct camera *c)
+{
+	c->fd = -1;
+	c->buf_count = 0;
+	c->s_on = 0;
+}
+
+void camera_destroy(struct camera *c)
+{
+	camera_close(c);
+}
+
+int camera_open(struct camera *c, const char *path,
+		int target_w, int target_h,
+		int tr_n, int tr_d)
+{
+	struct v4l2_format fmt;
+	struct v4l2_streamparm parm;
+	uint32_t pf;
+	int w, h;
+	int n, d;
+
+	if (c->fd >= 0)
+		camera_close(c);
+
+	/* Open device and get basic properties */
+	c->fd = open(path, O_RDWR);
+	if (c->fd < 0)
+		return -1;
+
+	/* Find a pixel format from the list */
+	if (find_best_format(c->fd, &pf) < 0)
+		goto fail;
+
+	/* Find a frame size */
+	if (find_best_size(c->fd, pf, target_w, target_h, &w, &h) < 0)
+		goto fail;
+
+	/* Find a frame rate */
+	if (find_best_rate(c->fd, pf, w, h, tr_n, tr_d, &n, &d) < 0)
+		goto fail;
+
+	/* Set format */
+	memset(&fmt, 0, sizeof(fmt));
+	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	fmt.fmt.pix.width = w;
+	fmt.fmt.pix.height = h;
+	fmt.fmt.pix.pixelformat = pf;
+	if (ioctl(c->fd, VIDIOC_S_FMT, &fmt) < 0)
+		goto fail;
+
+	memset(&fmt, 0, sizeof(fmt));
+	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	if (ioctl(c->fd, VIDIOC_G_FMT, &fmt) < 0)
+		goto fail;
+
+	/* Set frame interval */
+	memset(&parm, 0, sizeof(parm));
+	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	parm.parm.capture.timeperframe.numerator = n;
+	parm.parm.capture.timeperframe.denominator = d;
+	if (ioctl(c->fd, VIDIOC_S_PARM, &parm) < 0)
+		goto fail;
+
+	memset(&parm, 0, sizeof(parm));
+	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	if (ioctl(c->fd, VIDIOC_G_PARM, &parm) < 0)
+		goto fail;
+
+	c->parms.format = map_fmt(fmt.fmt.pix.pixelformat);
+	c->parms.width = fmt.fmt.pix.width;
+	c->parms.height = fmt.fmt.pix.height;
+	c->parms.pitch_bytes = fmt.fmt.pix.bytesperline;
+	c->parms.interval_n = parm.parm.capture.timeperframe.numerator;
+	c->parms.interval_d = parm.parm.capture.timeperframe.denominator;
+
+	return 0;
+
+fail:
+	{
+		const int e = errno;
+
+		close(c->fd);
+		c->fd = -1;
+		errno = e;
+	}
+
+	return -1;
+}
+
+void camera_close(struct camera *c)
+{
+	camera_off(c);
+	camera_unmap(c);
+
+	if (c->fd < 0)
+		return;
+
+	close(c->fd);
+	c->fd = -1;
+}
+
+int camera_map(struct camera *c, int buf_count)
+{
+	struct v4l2_requestbuffers reqbuf;
+	int count;
+	int i;
+
+	if (buf_count > CAMERA_MAX_BUFFERS)
+		buf_count = CAMERA_MAX_BUFFERS;
+
+	if (buf_count <= 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (c->fd < 0) {
+		errno = EBADF;
+		return -1;
+	}
+
+	if (c->buf_count)
+		camera_unmap(c);
+
+	memset(&reqbuf, 0, sizeof(reqbuf));
+	reqbuf.count = buf_count;
+	reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	reqbuf.memory = V4L2_MEMORY_MMAP;
+
+	if (ioctl(c->fd, VIDIOC_REQBUFS, &reqbuf) < 0)
+		return -1;
+
+	count = reqbuf.count;
+	if (count > CAMERA_MAX_BUFFERS)
+		count = CAMERA_MAX_BUFFERS;
+
+	/* Query all buffers */
+	for (i = 0; i < count; i++) {
+		struct v4l2_buffer buf;
+		struct camera_buffer *cb = &c->buf_desc[i];
+
+		memset(&buf, 0, sizeof(buf));
+		buf.index = i;
+		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		buf.memory = V4L2_MEMORY_MMAP;
+		if (ioctl(c->fd, VIDIOC_QUERYBUF, &buf) < 0)
+			return -1;
+
+		cb->offset = buf.m.offset;
+		cb->size = buf.length;
+		cb->addr = mmap(NULL, cb->size, PROT_READ,
+			MAP_SHARED, c->fd, cb->offset);
+
+		if (cb->addr == MAP_FAILED) {
+			const int save = errno;
+
+			i--;
+			while (i >= 0) {
+				cb = &c->buf_desc[i--];
+				munmap(cb->addr, cb->size);
+			}
+
+			errno = save;
+			return -1;
+		}
+	}
+
+	c->buf_count = count;
+	return 0;
+}
+
+void camera_unmap(struct camera *c)
+{
+	int i;
+
+	for (i = 0; i < c->buf_count; i++) {
+		struct camera_buffer *cb = &c->buf_desc[i];
+
+		munmap(cb->addr, cb->size);
+	}
+
+	c->buf_count = 0;
+}
+
+int camera_on(struct camera *c)
+{
+	int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	if (c->s_on)
+		return 0;
+
+	if (c->fd < 0) {
+		errno = EBADF;
+		return -1;
+	}
+
+	if (ioctl(c->fd, VIDIOC_STREAMON, &type) < 0)
+		return -1;
+
+	c->s_on = 1;
+	c->s_qc = 0;
+	c->s_qhead = 0;
+	return 0;
+}
+
+void camera_off(struct camera *c)
+{
+	int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	if (!c->s_on)
+		return;
+
+	ioctl(c->fd, VIDIOC_STREAMOFF, &type);
+	c->s_on = 0;
+}
+
+int camera_enqueue_all(struct camera *c)
+{
+	while (c->s_qc < c->buf_count) {
+		struct v4l2_buffer buf;
+
+		memset(&buf, 0, sizeof(buf));
+		buf.index = (c->s_qc + c->s_qhead) % c->buf_count;
+		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		buf.memory = V4L2_MEMORY_MMAP;
+
+		if (ioctl(c->fd, VIDIOC_QBUF, &buf) < 0)
+			return -1;
+
+		c->s_qc++;
+	}
+
+	return 0;
+}
+
+int camera_dequeue_one(struct camera *c)
+{
+	struct v4l2_buffer buf;
+
+	if (!c->s_qc) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	memset(&buf, 0, sizeof(buf));
+	buf.memory = V4L2_MEMORY_MMAP;
+	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	if (ioctl(c->fd, VIDIOC_DQBUF, &buf) < 0)
+		return -1;
+
+	c->s_qc--;
+	if (++c->s_qhead >= c->buf_count)
+		c->s_qhead = 0;
+
+	return 0;
+}
diff --git a/src/third_party/quirc/demo/camera.h b/src/third_party/quirc/demo/camera.h
new file mode 100644
index 0000000..511b5bd
--- /dev/null
+++ b/src/third_party/quirc/demo/camera.h
@@ -0,0 +1,104 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CAMERA_H_
+#define CAMERA_H_
+
+#include <stddef.h>
+
+#define CAMERA_MAX_BUFFERS	32
+
+typedef enum {
+	CAMERA_FORMAT_UNKNOWN = 0,
+	CAMERA_FORMAT_MJPEG,
+	CAMERA_FORMAT_YUYV
+} camera_format_t;
+
+struct camera_parms {
+	camera_format_t		format;
+	int			width;
+	int			height;
+	int			pitch_bytes;
+	int			interval_n;
+	int			interval_d;
+};
+
+struct camera_buffer {
+	void			*addr;
+	size_t			size;
+	unsigned long		offset;
+};
+
+struct camera {
+	int			fd;
+
+	struct camera_parms	parms;
+
+	struct camera_buffer	buf_desc[CAMERA_MAX_BUFFERS];
+	int			buf_count;
+
+	/* Stream state */
+	int			s_on;
+	int			s_qc;
+	int			s_qhead;
+};
+
+/* Initialize/destroy a camera. No resources are allocated. */
+void camera_init(struct camera *c);
+void camera_destroy(struct camera *c);
+
+/* Open/close the camera device */
+int camera_open(struct camera *c, const char *path,
+		int target_w, int target_h,
+		int tr_n, int tr_d);
+void camera_close(struct camera *c);
+
+static inline int camera_get_fd(const struct camera *c)
+{
+	return c->fd;
+}
+
+static inline const struct camera_parms *camera_get_parms
+	(const struct camera *c)
+{
+	return &c->parms;
+}
+
+/* Map buffers */
+int camera_map(struct camera *c, int buf_count);
+void camera_unmap(struct camera *c);
+
+static inline int camera_get_buf_count(const struct camera *c)
+{
+	return c->buf_count;
+}
+
+/* Switch streaming on/off */
+int camera_on(struct camera *c);
+void camera_off(struct camera *c);
+
+/* Enqueue/dequeue buffers (count = 0 means enqueue all) */
+int camera_enqueue_all(struct camera *c);
+int camera_dequeue_one(struct camera *c);
+
+/* Fetch the oldest dequeued buffer */
+static inline const struct camera_buffer *camera_get_head
+	(const struct camera *c)
+{
+	return &c->buf_desc[(c->s_qhead + c->buf_count - 1) % c->buf_count];
+}
+
+#endif
diff --git a/src/third_party/quirc/demo/convert.c b/src/third_party/quirc/demo/convert.c
new file mode 100644
index 0000000..9e6f458
--- /dev/null
+++ b/src/third_party/quirc/demo/convert.c
@@ -0,0 +1,103 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "convert.h"
+
+#define CHANNEL_CLAMP(dst, tmp, lum, chrom) \
+	(tmp) = ((lum) + (chrom)) >> 8; \
+	if ((tmp) < 0) \
+		(tmp) = 0; \
+	if ((tmp) > 255) \
+		(tmp) = 255; \
+	(dst) = (tmp);
+
+void yuyv_to_rgb32(const uint8_t *src, int src_pitch,
+		   int w, int h,
+		   uint8_t *dst, int dst_pitch)
+{
+	int y;
+
+	for (y = 0; y < h; y++) {
+		int x;
+		const uint8_t *srow = src + y * src_pitch;
+		uint8_t *drow = dst + y * dst_pitch;
+
+		for (x = 0; x < w; x += 2) {
+			/* ITU-R colorspace assumed */
+			int y0 = (int)srow[0] * 256;
+			int y1 = (int)srow[2] * 256;
+			int cr = (int)srow[3] - 128;
+			int cb = (int)srow[1] - 128;
+			int r = cr * 359;
+			int g = -cb * 88 - 128 * cr;
+			int b = 454 * cb;
+			int z;
+
+			CHANNEL_CLAMP(drow[0], z, y0, b);
+			CHANNEL_CLAMP(drow[1], z, y0, g);
+			CHANNEL_CLAMP(drow[2], z, y0, r);
+			CHANNEL_CLAMP(drow[4], z, y1, b);
+			CHANNEL_CLAMP(drow[5], z, y1, g);
+			CHANNEL_CLAMP(drow[6], z, y1, r);
+
+			srow += 4;
+			drow += 8;
+		}
+	}
+}
+
+void yuyv_to_luma(const uint8_t *src, int src_pitch,
+		  int w, int h,
+		  uint8_t *dst, int dst_pitch)
+{
+	int y;
+
+	for (y = 0; y < h; y++) {
+		int x;
+		const uint8_t *srow = src + y * src_pitch;
+		uint8_t *drow = dst + y * dst_pitch;
+
+		for (x = 0; x < w; x += 2) {
+			*(drow++) = srow[0];
+			*(drow++) = srow[2];
+			srow += 4;
+		}
+	}
+}
+
+void rgb32_to_luma(const uint8_t *src, int src_pitch,
+		   int w, int h,
+		   uint8_t *dst, int dst_pitch)
+{
+	int y;
+
+	for (y = 0; y < h; y++) {
+		const uint8_t *rgb32 = src + src_pitch * y;
+		uint8_t *gray = dst + y * dst_pitch;
+		int i;
+
+		for (i = 0; i < w; i++) {
+			/* ITU-R colorspace assumed */
+			int r = (int)rgb32[2];
+			int g = (int)rgb32[1];
+			int b = (int)rgb32[0];
+			int sum = r * 59 + g * 150 + b * 29;
+
+			*(gray++) = sum >> 8;
+			rgb32 += 4;
+		}
+	}
+}
diff --git a/src/third_party/quirc/demo/convert.h b/src/third_party/quirc/demo/convert.h
new file mode 100644
index 0000000..ec2a14e
--- /dev/null
+++ b/src/third_party/quirc/demo/convert.h
@@ -0,0 +1,39 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CONVERT_H_
+#define CONVERT_H_
+
+#include <stdint.h>
+
+/* Convert 4:2:2 YUYV format to RGB32 format. The source and destination
+ * frames are expected to be the same size.
+ */
+void yuyv_to_rgb32(const uint8_t *src, int src_pitch,
+		   int w, int h,
+		   uint8_t *dst, int dst_pitch);
+
+/* Extract the luma channel from a 4:2:2 YUYV image. */
+void yuyv_to_luma(const uint8_t *src, int src_pitch,
+		  int w, int h,
+		  uint8_t *dst, int dst_pitch);
+
+/* Extract the luma channel from an RGB32 image. */
+void rgb32_to_luma(const uint8_t *src, int src_pitch,
+		   int w, int h,
+		   uint8_t *dst, int dst_pitch);
+
+#endif
diff --git a/src/third_party/quirc/demo/demo.c b/src/third_party/quirc/demo/demo.c
new file mode 100644
index 0000000..178fde8
--- /dev/null
+++ b/src/third_party/quirc/demo/demo.c
@@ -0,0 +1,333 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <SDL.h>
+#include <SDL_gfxPrimitives.h>
+#include <quirc.h>
+#include <time.h>
+#include <getopt.h>
+
+#include "camera.h"
+#include "mjpeg.h"
+#include "convert.h"
+#include "dthash.h"
+#include "demoutil.h"
+
+/* Collected command-line arguments */
+static const char *camera_path = "/dev/video0";
+static int video_width = 640;
+static int video_height = 480;
+static int want_frame_rate = 0;
+static int want_verbose = 0;
+static int printer_timeout = 2;
+
+static void fat_text(SDL_Surface *screen, int x, int y, const char *text)
+{
+	int i, j;
+
+	for (i = -1; i <= 1; i++)
+		for (j = -1; j <= 1; j++)
+			stringColor(screen, x + i, y + j, text, 0xffffffff);
+	stringColor(screen, x, y, text, 0x008000ff);
+}
+
+static void fat_text_cent(SDL_Surface *screen, int x, int y, const char *text)
+{
+	x -= strlen(text) * 4;
+
+	fat_text(screen, x, y, text);
+}
+
+static void draw_qr(SDL_Surface *screen, struct quirc *q, struct dthash *dt)
+{
+	int count = quirc_count(q);
+	int i;
+
+	for (i = 0; i < count; i++) {
+		struct quirc_code code;
+		struct quirc_data data;
+		quirc_decode_error_t err;
+		int j;
+		int xc = 0;
+		int yc = 0;
+		char buf[128];
+
+		quirc_extract(q, i, &code);
+
+		for (j = 0; j < 4; j++) {
+			struct quirc_point *a = &code.corners[j];
+			struct quirc_point *b = &code.corners[(j + 1) % 4];
+
+			xc += a->x;
+			yc += a->y;
+			lineColor(screen, a->x, a->y, b->x, b->y, 0x008000ff);
+		}
+
+		xc /= 4;
+		yc /= 4;
+
+		if (want_verbose) {
+			snprintf(buf, sizeof(buf), "Code size: %d cells",
+				 code.size);
+			fat_text_cent(screen, xc, yc - 20, buf);
+		}
+
+		err = quirc_decode(&code, &data);
+
+		if (err) {
+			if (want_verbose)
+				fat_text_cent(screen, xc, yc,
+						quirc_strerror(err));
+		} else {
+			fat_text_cent(screen, xc, yc, (char *)data.payload);
+			print_data(&data, dt, want_verbose);
+
+			if (want_verbose) {
+				snprintf(buf, sizeof(buf),
+					 "Ver: %d, ECC: %c, Mask: %d, Type: %d",
+					 data.version, "MLHQ"[data.ecc_level],
+					 data.mask, data.data_type);
+				fat_text_cent(screen, xc, yc + 20, buf);
+			}
+		}
+	}
+}
+
+static int main_loop(struct camera *cam, SDL_Surface *screen,
+		     struct quirc *q, struct mjpeg_decoder *mj)
+{
+	SDL_Event ev;
+	time_t last_rate = 0;
+	int frame_count = 0;
+	char rate_text[64];
+	struct dthash dt;
+
+	rate_text[0] = 0;
+	dthash_init(&dt, printer_timeout);
+
+	for (;;) {
+		time_t now = time(NULL);
+		const struct camera_buffer *head;
+		const struct camera_parms *parms = camera_get_parms(cam);
+
+		if (camera_dequeue_one(cam) < 0) {
+			perror("camera_dequeue_one");
+			return -1;
+		}
+
+		head = camera_get_head(cam);
+
+		SDL_LockSurface(screen);
+		switch (parms->format) {
+		case CAMERA_FORMAT_MJPEG:
+			mjpeg_decode_rgb32(mj, head->addr, head->size,
+					   screen->pixels, screen->pitch,
+					   screen->w, screen->h);
+			break;
+
+		case CAMERA_FORMAT_YUYV:
+			yuyv_to_rgb32(head->addr, parms->width * 2,
+				      parms->width, parms->height,
+				      screen->pixels, screen->pitch);
+			break;
+
+		default:
+			fprintf(stderr, "Unknown frame format\n");
+			return -1;
+		}
+
+		if (camera_enqueue_all(cam) < 0) {
+			perror("camera_enqueue_all");
+			return -1;
+		}
+
+		rgb32_to_luma(screen->pixels, screen->pitch,
+			      screen->w, screen->h,
+			      quirc_begin(q, NULL, NULL),
+			      screen->w);
+		quirc_end(q);
+		SDL_UnlockSurface(screen);
+
+		draw_qr(screen, q, &dt);
+		if (want_frame_rate)
+			fat_text(screen, 5, 5, rate_text);
+		SDL_Flip(screen);
+
+		while (SDL_PollEvent(&ev) > 0) {
+			if (ev.type == SDL_QUIT)
+				return 0;
+
+			if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == 'q')
+				return 0;
+		}
+
+		if (now != last_rate) {
+			snprintf(rate_text, sizeof(rate_text),
+				 "Frame rate: %d fps", frame_count);
+			frame_count = 0;
+			last_rate = now;
+		}
+
+		frame_count++;
+	}
+}
+
+static int run_demo(void)
+{
+	struct quirc *qr;
+	struct camera cam;
+	struct mjpeg_decoder mj;
+	const struct camera_parms *parms;
+	SDL_Surface *screen;
+
+	camera_init(&cam);
+	if (camera_open(&cam, camera_path, video_width, video_height,
+			25, 1) < 0) {
+		perror("camera_open");
+		goto fail_qr;
+	}
+
+	if (camera_map(&cam, 8) < 0) {
+		perror("camera_map");
+		goto fail_qr;
+	}
+
+	if (camera_on(&cam) < 0) {
+		perror("camera_on");
+		goto fail_qr;
+	}
+
+	if (camera_enqueue_all(&cam) < 0) {
+		perror("camera_enqueue_all");
+		goto fail_qr;
+	}
+
+	parms = camera_get_parms(&cam);
+
+	qr = quirc_new();
+	if (!qr) {
+		perror("couldn't allocate QR decoder");
+		goto fail_qr;
+	}
+
+	if (quirc_resize(qr, parms->width, parms->height) < 0) {
+		perror("couldn't allocate QR buffer");
+		goto fail_qr_resize;
+	}
+
+	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+		perror("couldn't init SDL");
+		goto fail_sdl_init;
+	}
+
+	screen = SDL_SetVideoMode(parms->width, parms->height, 32,
+				  SDL_SWSURFACE | SDL_DOUBLEBUF);
+	if (!screen) {
+		perror("couldn't init video mode");
+		goto fail_video_mode;
+	}
+
+	mjpeg_init(&mj);
+	if (main_loop(&cam, screen, qr, &mj) < 0)
+		goto fail_main_loop;
+	mjpeg_free(&mj);
+
+	SDL_Quit();
+	quirc_destroy(qr);
+	camera_destroy(&cam);
+
+	return 0;
+
+fail_main_loop:
+	mjpeg_free(&mj);
+fail_video_mode:
+	SDL_Quit();
+fail_qr_resize:
+fail_sdl_init:
+	quirc_destroy(qr);
+fail_qr:
+	camera_destroy(&cam);
+
+	return -1;
+}
+
+static void usage(const char *progname)
+{
+	printf("Usage: %s [options]\n\n"
+"Valid options are:\n\n"
+"    -f             Show frame rate on screen.\n"
+"    -v             Show extra data for detected codes.\n"
+"    -d <device>    Specify camera device path.\n"
+"    -s <WxH>       Specify video dimensions.\n"
+"    -p <timeout>   Set printer timeout (seconds).\n"
+"    --help         Show this information.\n"
+"    --version      Show library version information.\n",
+	progname);
+}
+
+int main(int argc, char **argv)
+{
+	static const struct option longopts[] = {
+		{"help",		0, 0, 'H'},
+		{"version",		0, 0, 'V'},
+		{NULL,			0, 0, 0}
+	};
+	int opt;
+
+	printf("quirc demo\n");
+	printf("Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>\n");
+	printf("\n");
+
+	while ((opt = getopt_long(argc, argv, "d:s:fvg:p:",
+				  longopts, NULL)) >= 0)
+		switch (opt) {
+		case 'V':
+			printf("Library version: %s\n", quirc_version());
+			return 0;
+
+		case 'H':
+			usage(argv[0]);
+			return 0;
+
+		case 'v':
+			want_verbose = 1;
+			break;
+
+		case 'f':
+			want_frame_rate = 1;
+			break;
+
+		case 's':
+			if (parse_size(optarg, &video_width, &video_height) < 0)
+				return -1;
+			break;
+
+		case 'p':
+			printer_timeout = atoi(optarg);
+			break;
+
+		case 'd':
+			camera_path = optarg;
+			break;
+
+		case '?':
+			fprintf(stderr, "Try --help for usage information\n");
+			return -1;
+		}
+
+	return run_demo();
+}
diff --git a/src/third_party/quirc/demo/demoutil.c b/src/third_party/quirc/demo/demoutil.c
new file mode 100644
index 0000000..857247a
--- /dev/null
+++ b/src/third_party/quirc/demo/demoutil.c
@@ -0,0 +1,71 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "demoutil.h"
+
+void print_data(const struct quirc_data *data, struct dthash *dt,
+		int want_verbose)
+{
+	if (dthash_seen(dt, data))
+		return;
+
+	printf("==> %s\n", data->payload);
+
+	if (want_verbose)
+		printf("    Version: %d, ECC: %c, Mask: %d, Type: %d\n\n",
+		       data->version, "MLHQ"[data->ecc_level],
+		       data->mask, data->data_type);
+}
+
+int parse_size(const char *text, int *video_width, int *video_height)
+{
+	int state = 0;
+	int w = 0, h = 0;
+	int i;
+
+	for (i = 0; text[i]; i++) {
+		if (text[i] == 'x' || text[i] == 'X') {
+			if (state == 0) {
+				state = 1;
+			} else {
+				fprintf(stderr, "parse_size: expected WxH\n");
+				return -1;
+			}
+		} else if (isdigit(text[i])) {
+			if (state == 0)
+				w = w * 10 + text[i] - '0';
+			else
+				h = h * 10 + text[i] - '0';
+		} else {
+			fprintf(stderr, "Invalid character in size: %c\n",
+				text[i]);
+			return -1;
+		}
+	}
+
+	if (w <= 0 || w >= 10000 || h <= 0 || h >= 10000) {
+		fprintf(stderr, "Invalid size: %dx%d\n", w, h);
+		return -1;
+	}
+
+	*video_width = w;
+	*video_height = h;
+
+	return 0;
+}
diff --git a/src/third_party/quirc/demo/demoutil.h b/src/third_party/quirc/demo/demoutil.h
new file mode 100644
index 0000000..b6f7dc9
--- /dev/null
+++ b/src/third_party/quirc/demo/demoutil.h
@@ -0,0 +1,34 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DEMOUTIL_H_
+#define DEMOUTIL_H_
+
+#include "dthash.h"
+#include "quirc.h"
+
+/* Check if we've seen the given code, and if not, print it on stdout.
+ * Include version info if requested.
+ */
+void print_data(const struct quirc_data *data, struct dthash *dt,
+		int want_verbose);
+
+/* Parse a string of the form "WxH" and return width and height as
+ * integers. Returns 0 on success or -1 if a parser error occurs.
+ */
+int parse_size(const char *text, int *video_width, int *video_height);
+
+#endif
diff --git a/src/third_party/quirc/demo/dthash.c b/src/third_party/quirc/demo/dthash.c
new file mode 100644
index 0000000..888dc1f
--- /dev/null
+++ b/src/third_party/quirc/demo/dthash.c
@@ -0,0 +1,139 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include "dthash.h"
+
+static const uint32_t crc32_tab[] = {
+	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+	0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+	0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+	0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+	0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+static uint32_t calc_crc(uint32_t crc, const uint8_t *buf, int len)
+{
+	while (len--) {
+		crc = crc32_tab[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+		buf++;
+	}
+
+	return crc;
+}
+
+static uint32_t code_hash(const struct quirc_data *data)
+{
+	uint8_t extra[4] = {data->version, data->ecc_level,
+			    data->mask, data->data_type};
+	uint32_t crc = calc_crc(0xffffffff, extra, 4);
+
+	return calc_crc(crc, data->payload, data->payload_len);
+}
+
+static void flush_old(struct dthash *d, time_t now)
+{
+	int i;
+
+	for (i = 0; i < d->count; i++) {
+		struct dthash_code *c = &d->codes[i];
+
+		if (c->when + d->timeout <= now) {
+			if (i + 1 < d->count)
+				memcpy(c, &d->codes[d->count - 1], sizeof(*c));
+			d->count--;
+		}
+	}
+}
+
+void dthash_init(struct dthash *d, int timeout)
+{
+	d->count = 0;
+	d->timeout = timeout;
+}
+
+int dthash_seen(struct dthash *d, const struct quirc_data *data)
+{
+	time_t now = time(NULL);
+	uint32_t hash = code_hash(data);
+	struct dthash_code *c;
+	int i;
+
+	flush_old(d, now);
+
+	/* If the code is already seen, update its timestamp */
+	for (i = 0; i < d->count; i++) {
+		c = &d->codes[i];
+		if (c->hash == hash) {
+			c->when = now;
+			return 1;
+		}
+	}
+
+	/* Otherwise, find a place to put it. If necessary, push the
+	 * oldset code out of the table.
+	 */
+	if (d->count + 1 < DTHASH_MAX_CODES) {
+		c = &d->codes[d->count++];
+	} else {
+		c = &d->codes[0];
+		for (i = 1; i < d->count; i++)
+			if (d->codes[i].when < c->when)
+				c = &d->codes[i];
+	}
+
+	c->hash = hash;
+	c->when = now;
+	return 0;
+}
diff --git a/src/third_party/quirc/demo/dthash.h b/src/third_party/quirc/demo/dthash.h
new file mode 100644
index 0000000..661f691
--- /dev/null
+++ b/src/third_party/quirc/demo/dthash.h
@@ -0,0 +1,53 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DTHASH_H_
+#define DTHASH_H_
+
+#include <stdint.h>
+#include <time.h>
+#include "quirc.h"
+
+/* Detector hash.
+ *
+ * This structure keeps track of codes that have been seen within the
+ * last N seconds, and allows us to print out codes at a reasonable
+ * rate as we see them.
+ */
+#define DTHASH_MAX_CODES	32
+
+struct dthash_code {
+	uint32_t		hash;
+	time_t			when;
+};
+
+struct dthash {
+	struct dthash_code	codes[DTHASH_MAX_CODES];
+	int			count;
+	int			timeout;
+};
+
+/* Initialise a detector hash with the given timeout. */
+void dthash_init(struct dthash *d, int timeout);
+
+/* When a code is discovered, this function should be called to see if
+ * it should be printed. The hash will record having seen the code, and
+ * return non-zero if it's the first time we've seen it within the
+ * configured timeout period.
+ */
+int dthash_seen(struct dthash *d, const struct quirc_data *data);
+
+#endif
diff --git a/src/third_party/quirc/demo/mjpeg.c b/src/third_party/quirc/demo/mjpeg.c
new file mode 100644
index 0000000..09d2197
--- /dev/null
+++ b/src/third_party/quirc/demo/mjpeg.c
@@ -0,0 +1,284 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <jpeglib.h>
+#include <assert.h>
+#include "mjpeg.h"
+
+struct huffman_table {
+	uint8_t		bits[17];
+	uint8_t		huffval[256];
+};
+
+static const struct huffman_table dc_lum = {
+	.bits = {
+		0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
+		0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00
+	},
+	.huffval = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b
+	}
+};
+
+static const struct huffman_table ac_lum = {
+	.bits = {
+		0x00, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04,
+		0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+		0x7d
+	},
+	.huffval = {
+		0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+		0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+		0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+		0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+		0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+		0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+		0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+		0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+		0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+		0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+		0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+		0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+		0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+		0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+		0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+		0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+		0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+		0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+		0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+		0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+		0xf9, 0xfa
+	}
+};
+
+static const struct huffman_table dc_chroma = {
+	.bits = {
+		0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
+		0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+		0x00
+	},
+	.huffval = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b
+	}
+};
+
+static const struct huffman_table ac_chroma = {
+	.bits = {
+		0x00, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
+		0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02,
+		0x77
+	},
+	.huffval = {
+		0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+		0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+		0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+		0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+		0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
+		0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+		0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+		0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+		0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+		0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+		0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+		0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+		0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
+		0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+		0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+		0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+		0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+		0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+		0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+		0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+		0xf9, 0xfa
+	}
+};
+
+static void init_source(j_decompress_ptr cinfo)
+{
+}
+
+static boolean fill_input_buffer(j_decompress_ptr cinfo)
+{
+	static const uint8_t eoi_marker[] = {0xff, 0xd9};
+
+	cinfo->src->next_input_byte = eoi_marker;
+	cinfo->src->bytes_in_buffer = 2;
+
+	return TRUE;
+}
+
+static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+	if (num_bytes < 0)
+		return;
+	if (num_bytes > cinfo->src->bytes_in_buffer)
+		num_bytes = cinfo->src->bytes_in_buffer;
+
+	cinfo->src->bytes_in_buffer -= num_bytes;
+	cinfo->src->next_input_byte += num_bytes;
+}
+
+static void term_source(j_decompress_ptr cinfo)
+{
+}
+
+struct my_jpeg_error {
+	struct jpeg_error_mgr   base;
+	jmp_buf                 env;
+};
+
+static void my_output_message(struct jpeg_common_struct *com)
+{
+	struct mjpeg_decoder *mj = (struct mjpeg_decoder *)com->err;
+	char buf[JMSG_LENGTH_MAX];
+
+	mj->err.format_message(com, buf);
+	fprintf(stderr, "MJPEG error: %s\n", buf);
+}
+
+static void my_error_exit(struct jpeg_common_struct *com)
+{
+	struct mjpeg_decoder *mj = (struct mjpeg_decoder *)com->err;
+
+	my_output_message(com);
+	longjmp(mj->env, 0);
+}
+
+static void setup_table(struct jpeg_decompress_struct *jpeg,
+			JHUFF_TBL **tbl_ptr, const struct huffman_table *tab)
+{
+	assert (*tbl_ptr == NULL);
+
+	*tbl_ptr = jpeg_alloc_huff_table((j_common_ptr)jpeg);
+	memcpy((*tbl_ptr)->bits, tab->bits, 17);
+	memcpy((*tbl_ptr)->huffval, tab->huffval, 256);
+}
+
+void mjpeg_init(struct mjpeg_decoder *mj)
+{
+	memset(mj, 0, sizeof(*mj));
+
+	/* Set up error management */
+	mj->dinfo.err = jpeg_std_error(&mj->err);
+	mj->err.error_exit = my_error_exit;
+	mj->err.output_message = my_output_message;
+
+	mj->src.init_source = init_source;
+	mj->src.fill_input_buffer = fill_input_buffer;
+	mj->src.skip_input_data = skip_input_data;
+	mj->src.resync_to_restart = jpeg_resync_to_restart;
+	mj->src.term_source = term_source;
+
+	jpeg_create_decompress(&mj->dinfo);
+	mj->dinfo.src = &mj->src;
+	mj->dinfo.err = &mj->err;
+
+	setup_table(&mj->dinfo, &mj->dinfo.dc_huff_tbl_ptrs[0], &dc_lum);
+	setup_table(&mj->dinfo, &mj->dinfo.ac_huff_tbl_ptrs[0], &ac_lum);
+	setup_table(&mj->dinfo, &mj->dinfo.dc_huff_tbl_ptrs[1], &dc_chroma);
+	setup_table(&mj->dinfo, &mj->dinfo.ac_huff_tbl_ptrs[1], &ac_chroma);
+}
+
+void mjpeg_free(struct mjpeg_decoder *mj)
+{
+	jpeg_destroy_decompress(&mj->dinfo);
+}
+
+int mjpeg_decode_rgb32(struct mjpeg_decoder *mj,
+		       const uint8_t *data, int datalen,
+		       uint8_t *out, int pitch, int max_w, int max_h)
+{
+	if (setjmp(mj->env))
+		return -1;
+
+	mj->dinfo.src->bytes_in_buffer = datalen;
+	mj->dinfo.src->next_input_byte = data;
+
+	jpeg_read_header(&mj->dinfo, TRUE);
+	mj->dinfo.output_components = 3;
+	mj->dinfo.out_color_space = JCS_RGB;
+	jpeg_start_decompress(&mj->dinfo);
+
+	if (mj->dinfo.image_height > max_h ||
+	    mj->dinfo.image_width > max_w) {
+		fprintf(stderr, "MJPEG: frame too big\n");
+		return -1;
+	}
+
+	uint8_t *rgb = calloc(mj->dinfo.image_width, 3);
+	if (!rgb) {
+		fprintf(stderr, "memory allocation failed\n");
+		return -1;
+	}
+	while (mj->dinfo.output_scanline < mj->dinfo.image_height) {
+		uint8_t *scr = out + pitch * mj->dinfo.output_scanline;
+		uint8_t *output = rgb;
+		int i;
+
+		jpeg_read_scanlines(&mj->dinfo, &output, 1);
+		for (i = 0; i < mj->dinfo.image_width; i++) {
+			scr[0] = output[2];
+			scr[1] = output[1];
+			scr[2] = output[0];
+			scr += 4;
+			output += 3;
+		}
+	}
+	free(rgb);
+
+	jpeg_finish_decompress(&mj->dinfo);
+
+	return 0;
+}
+
+int mjpeg_decode_gray(struct mjpeg_decoder *mj,
+		      const uint8_t *data, int datalen,
+		      uint8_t *out, int pitch, int max_w, int max_h)
+{
+	if (setjmp(mj->env))
+		return -1;
+
+	mj->dinfo.src->bytes_in_buffer = datalen;
+	mj->dinfo.src->next_input_byte = data;
+
+	jpeg_read_header(&mj->dinfo, TRUE);
+	mj->dinfo.output_components = 1;
+	mj->dinfo.out_color_space = JCS_GRAYSCALE;
+	jpeg_start_decompress(&mj->dinfo);
+
+	if (mj->dinfo.image_height > max_h ||
+	    mj->dinfo.image_width > max_w) {
+		fprintf(stderr, "MJPEG: frame too big\n");
+		return -1;
+	}
+
+	while (mj->dinfo.output_scanline < mj->dinfo.image_height) {
+		uint8_t *scr = out + pitch * mj->dinfo.output_scanline;
+
+		jpeg_read_scanlines(&mj->dinfo, &scr, 1);
+	}
+
+	jpeg_finish_decompress(&mj->dinfo);
+
+	return 0;
+}
diff --git a/src/third_party/quirc/demo/mjpeg.h b/src/third_party/quirc/demo/mjpeg.h
new file mode 100644
index 0000000..caac855
--- /dev/null
+++ b/src/third_party/quirc/demo/mjpeg.h
@@ -0,0 +1,54 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MJPEG_H_
+#define MJPEG_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <jpeglib.h>
+#include <setjmp.h>
+
+struct mjpeg_decoder {
+	/* The error manager must be the first item in this struct */
+	struct jpeg_error_mgr			err;
+	struct jpeg_decompress_struct		dinfo;
+	struct jpeg_source_mgr			src;
+	jmp_buf					env;
+};
+
+/* Construct an MJPEG decoder. */
+void mjpeg_init(struct mjpeg_decoder *mj);
+
+/* Free any memory allocated while decoding MJPEG frames. */
+void mjpeg_free(struct mjpeg_decoder *mj);
+
+/* Decode a single MJPEG image to the buffer given, in RGB format.
+ * Returns 0 on success, -1 if an error occurs (bad data, or image too
+ * big for buffer).
+ */
+int mjpeg_decode_rgb32(struct mjpeg_decoder *mj,
+		       const uint8_t *data, int datalen,
+		       uint8_t *out, int pitch, int max_w, int max_h);
+
+/* Decode a single MJPEG image to the buffer given in 8-bit grayscale.
+ * Returns 0 on success, -1 if an error occurs.
+ */
+int mjpeg_decode_gray(struct mjpeg_decoder *mj,
+		      const uint8_t *data, int datalen,
+		      uint8_t *out, int pitch, int max_w, int max_h);
+
+#endif
diff --git a/src/third_party/quirc/demo/scanner.c b/src/third_party/quirc/demo/scanner.c
new file mode 100644
index 0000000..7c6c38e
--- /dev/null
+++ b/src/third_party/quirc/demo/scanner.c
@@ -0,0 +1,214 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <quirc.h>
+#include <time.h>
+#include <getopt.h>
+
+#include "camera.h"
+#include "mjpeg.h"
+#include "convert.h"
+#include "dthash.h"
+#include "demoutil.h"
+
+/* Collected command-line arguments */
+static const char *camera_path = "/dev/video0";
+static int video_width = 640;
+static int video_height = 480;
+static int want_verbose = 0;
+static int printer_timeout = 2;
+
+static int main_loop(struct camera *cam,
+		     struct quirc *q, struct mjpeg_decoder *mj)
+{
+	struct dthash dt;
+
+	dthash_init(&dt, printer_timeout);
+
+	for (;;) {
+		int w, h;
+		int i, count;
+		uint8_t *buf = quirc_begin(q, &w, &h);
+		const struct camera_buffer *head;
+		const struct camera_parms *parms = camera_get_parms(cam);
+
+		if (camera_dequeue_one(cam) < 0) {
+			perror("camera_dequeue_one");
+			return -1;
+		}
+
+		head = camera_get_head(cam);
+
+		switch (parms->format) {
+		case CAMERA_FORMAT_MJPEG:
+			mjpeg_decode_gray(mj, head->addr, head->size,
+					  buf, w, w, h);
+			break;
+
+		case CAMERA_FORMAT_YUYV:
+			yuyv_to_luma(head->addr, w * 2, w, h, buf, w);
+			break;
+
+		default:
+			fprintf(stderr, "Unknown frame format\n");
+			return -1;
+		}
+
+		if (camera_enqueue_all(cam) < 0) {
+			perror("camera_enqueue_all");
+			return -1;
+		}
+
+		quirc_end(q);
+
+		count = quirc_count(q);
+		for (i = 0; i < count; i++) {
+			struct quirc_code code;
+			struct quirc_data data;
+
+			quirc_extract(q, i, &code);
+			if (!quirc_decode(&code, &data))
+				print_data(&data, &dt, want_verbose);
+		}
+	}
+}
+
+static int run_scanner(void)
+{
+	struct quirc *qr;
+	struct camera cam;
+	struct mjpeg_decoder mj;
+	const struct camera_parms *parms;
+
+	camera_init(&cam);
+	if (camera_open(&cam, camera_path, video_width, video_height,
+			25, 1) < 0) {
+		perror("camera_open");
+		goto fail_qr;
+	}
+
+	if (camera_map(&cam, 8) < 0) {
+		perror("camera_map");
+		goto fail_qr;
+	}
+
+	if (camera_on(&cam) < 0) {
+		perror("camera_on");
+		goto fail_qr;
+	}
+
+	if (camera_enqueue_all(&cam) < 0) {
+		perror("camera_enqueue_all");
+		goto fail_qr;
+	}
+
+	parms = camera_get_parms(&cam);
+
+	qr = quirc_new();
+	if (!qr) {
+		perror("couldn't allocate QR decoder");
+		goto fail_qr;
+	}
+
+	if (quirc_resize(qr, parms->width, parms->height) < 0) {
+		perror("couldn't allocate QR buffer");
+		goto fail_qr_resize;
+	}
+
+	mjpeg_init(&mj);
+	if (main_loop(&cam, qr, &mj) < 0)
+		goto fail_main_loop;
+	mjpeg_free(&mj);
+
+	quirc_destroy(qr);
+	camera_destroy(&cam);
+
+	return 0;
+
+fail_main_loop:
+	mjpeg_free(&mj);
+fail_qr_resize:
+	quirc_destroy(qr);
+fail_qr:
+	camera_destroy(&cam);
+
+	return -1;
+}
+
+static void usage(const char *progname)
+{
+	printf("Usage: %s [options]\n\n"
+"Valid options are:\n\n"
+"    -v             Show extra data for detected codes.\n"
+"    -d <device>    Specify camera device path.\n"
+"    -s <WxH>       Specify video dimensions.\n"
+"    -p <timeout>   Set printer timeout (seconds).\n"
+"    --help         Show this information.\n"
+"    --version      Show library version information.\n",
+	progname);
+}
+
+int main(int argc, char **argv)
+{
+	static const struct option longopts[] = {
+		{"help",		0, 0, 'H'},
+		{"version",		0, 0, 'V'},
+		{NULL,			0, 0, 0}
+	};
+	int opt;
+
+	printf("quirc scanner demo\n");
+	printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
+	printf("\n");
+
+	while ((opt = getopt_long(argc, argv, "d:s:vg:p:",
+				  longopts, NULL)) >= 0)
+		switch (opt) {
+		case 'V':
+			printf("Library version: %s\n", quirc_version());
+			return 0;
+
+		case 'H':
+			usage(argv[0]);
+			return 0;
+
+		case 'v':
+			want_verbose = 1;
+			break;
+
+		case 's':
+			if (parse_size(optarg, &video_width, &video_height) < 0)
+				return -1;
+			break;
+
+		case 'p':
+			printer_timeout = atoi(optarg);
+			break;
+
+		case 'd':
+			camera_path = optarg;
+			break;
+
+		case '?':
+			fprintf(stderr, "Try --help for usage information\n");
+			return -1;
+		}
+
+	return run_scanner();
+}
diff --git a/src/third_party/quirc/lib/decode.c b/src/third_party/quirc/lib/decode.c
new file mode 100644
index 0000000..13782b8
--- /dev/null
+++ b/src/third_party/quirc/lib/decode.c
@@ -0,0 +1,919 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "quirc_internal.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#define MAX_POLY       64
+
+/************************************************************************
+ * Galois fields
+ */
+
+struct galois_field {
+	int p;
+	const uint8_t *log;
+	const uint8_t *exp;
+};
+
+static const uint8_t gf16_exp[16] = {
+	0x01, 0x02, 0x04, 0x08, 0x03, 0x06, 0x0c, 0x0b,
+	0x05, 0x0a, 0x07, 0x0e, 0x0f, 0x0d, 0x09, 0x01
+};
+
+static const uint8_t gf16_log[16] = {
+	0x00, 0x0f, 0x01, 0x04, 0x02, 0x08, 0x05, 0x0a,
+	0x03, 0x0e, 0x09, 0x07, 0x06, 0x0d, 0x0b, 0x0c
+};
+
+static const struct galois_field gf16 = {
+	.p = 15,
+	.log = gf16_log,
+	.exp = gf16_exp
+};
+
+static const uint8_t gf256_exp[256] = {
+	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+	0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
+	0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
+	0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
+	0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
+	0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
+	0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
+	0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
+	0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
+	0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
+	0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
+	0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
+	0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
+	0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
+	0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
+	0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
+	0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
+	0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
+	0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
+	0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
+	0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
+	0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
+	0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
+	0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
+	0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
+	0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
+	0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
+	0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
+	0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
+	0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
+	0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
+	0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01
+};
+
+static const uint8_t gf256_log[256] = {
+	0x00, 0xff, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
+	0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
+	0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
+	0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
+	0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
+	0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
+	0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
+	0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
+	0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
+	0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
+	0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
+	0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
+	0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
+	0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
+	0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
+	0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
+	0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
+	0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
+	0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
+	0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
+	0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
+	0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
+	0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
+	0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
+	0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
+	0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
+	0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
+	0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
+	0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
+	0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
+	0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
+	0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
+};
+
+static const struct galois_field gf256 = {
+	.p = 255,
+	.log = gf256_log,
+	.exp = gf256_exp
+};
+
+/************************************************************************
+ * Polynomial operations
+ */
+
+static void poly_add(uint8_t *dst, const uint8_t *src, uint8_t c,
+		     int shift, const struct galois_field *gf)
+{
+	int i;
+	int log_c = gf->log[c];
+
+	if (!c)
+		return;
+
+	for (i = 0; i < MAX_POLY; i++) {
+		int p = i + shift;
+		uint8_t v = src[i];
+
+		if (p < 0 || p >= MAX_POLY)
+			continue;
+		if (!v)
+			continue;
+
+		dst[p] ^= gf->exp[(gf->log[v] + log_c) % gf->p];
+	}
+}
+
+static uint8_t poly_eval(const uint8_t *s, uint8_t x,
+			 const struct galois_field *gf)
+{
+	int i;
+	uint8_t sum = 0;
+	uint8_t log_x = gf->log[x];
+
+	if (!x)
+		return s[0];
+
+	for (i = 0; i < MAX_POLY; i++) {
+		uint8_t c = s[i];
+
+		if (!c)
+			continue;
+
+		sum ^= gf->exp[(gf->log[c] + log_x * i) % gf->p];
+	}
+
+	return sum;
+}
+
+/************************************************************************
+ * Berlekamp-Massey algorithm for finding error locator polynomials.
+ */
+
+static void berlekamp_massey(const uint8_t *s, int N,
+			     const struct galois_field *gf,
+			     uint8_t *sigma)
+{
+	uint8_t C[MAX_POLY];
+	uint8_t B[MAX_POLY];
+	int L = 0;
+	int m = 1;
+	uint8_t b = 1;
+	int n;
+
+	memset(B, 0, sizeof(B));
+	memset(C, 0, sizeof(C));
+	B[0] = 1;
+	C[0] = 1;
+
+	for (n = 0; n < N; n++) {
+		uint8_t d = s[n];
+		uint8_t mult;
+		int i;
+
+		for (i = 1; i <= L; i++) {
+			if (!(C[i] && s[n - i]))
+				continue;
+
+			d ^= gf->exp[(gf->log[C[i]] +
+				      gf->log[s[n - i]]) %
+				     gf->p];
+		}
+
+		mult = gf->exp[(gf->p - gf->log[b] + gf->log[d]) % gf->p];
+
+		if (!d) {
+			m++;
+		} else if (L * 2 <= n) {
+			uint8_t T[MAX_POLY];
+
+			memcpy(T, C, sizeof(T));
+			poly_add(C, B, mult, m, gf);
+			memcpy(B, T, sizeof(B));
+			L = n + 1 - L;
+			b = d;
+			m = 1;
+		} else {
+			poly_add(C, B, mult, m, gf);
+			m++;
+		}
+	}
+
+	memcpy(sigma, C, MAX_POLY);
+}
+
+/************************************************************************
+ * Code stream error correction
+ *
+ * Generator polynomial for GF(2^8) is x^8 + x^4 + x^3 + x^2 + 1
+ */
+
+static int block_syndromes(const uint8_t *data, int bs, int npar, uint8_t *s)
+{
+	int nonzero = 0;
+	int i;
+
+	memset(s, 0, MAX_POLY);
+
+	for (i = 0; i < npar; i++) {
+		int j;
+
+		for (j = 0; j < bs; j++) {
+			uint8_t c = data[bs - j - 1];
+
+			if (!c)
+				continue;
+
+			s[i] ^= gf256_exp[((int)gf256_log[c] +
+				    i * j) % 255];
+		}
+
+		if (s[i])
+			nonzero = 1;
+	}
+
+	return nonzero;
+}
+
+static void eloc_poly(uint8_t *omega,
+		      const uint8_t *s, const uint8_t *sigma,
+		      int npar)
+{
+	int i;
+
+	memset(omega, 0, MAX_POLY);
+
+	for (i = 0; i < npar; i++) {
+		const uint8_t a = sigma[i];
+		const uint8_t log_a = gf256_log[a];
+		int j;
+
+		if (!a)
+			continue;
+
+		for (j = 0; j + 1 < MAX_POLY; j++) {
+			const uint8_t b = s[j + 1];
+
+			if (i + j >= npar)
+				break;
+
+			if (!b)
+				continue;
+
+			omega[i + j] ^=
+			    gf256_exp[(log_a + gf256_log[b]) % 255];
+		}
+	}
+}
+
+static quirc_decode_error_t correct_block(uint8_t *data,
+					  const struct quirc_rs_params *ecc)
+{
+	int npar = ecc->bs - ecc->dw;
+	uint8_t s[MAX_POLY];
+	uint8_t sigma[MAX_POLY];
+	uint8_t sigma_deriv[MAX_POLY];
+	uint8_t omega[MAX_POLY];
+	int i;
+
+	/* Compute syndrome vector */
+	if (!block_syndromes(data, ecc->bs, npar, s))
+		return QUIRC_SUCCESS;
+
+	berlekamp_massey(s, npar, &gf256, sigma);
+
+	/* Compute derivative of sigma */
+	memset(sigma_deriv, 0, MAX_POLY);
+	for (i = 0; i + 1 < MAX_POLY; i += 2)
+		sigma_deriv[i] = sigma[i + 1];
+
+	/* Compute error evaluator polynomial */
+	eloc_poly(omega, s, sigma, npar - 1);
+
+	/* Find error locations and magnitudes */
+	for (i = 0; i < ecc->bs; i++) {
+		uint8_t xinv = gf256_exp[255 - i];
+
+		if (!poly_eval(sigma, xinv, &gf256)) {
+			uint8_t sd_x = poly_eval(sigma_deriv, xinv, &gf256);
+			uint8_t omega_x = poly_eval(omega, xinv, &gf256);
+			uint8_t error = gf256_exp[(255 - gf256_log[sd_x] +
+						   gf256_log[omega_x]) % 255];
+
+			data[ecc->bs - i - 1] ^= error;
+		}
+	}
+
+	if (block_syndromes(data, ecc->bs, npar, s))
+		return QUIRC_ERROR_DATA_ECC;
+
+	return QUIRC_SUCCESS;
+}
+
+/************************************************************************
+ * Format value error correction
+ *
+ * Generator polynomial for GF(2^4) is x^4 + x + 1
+ */
+
+#define FORMAT_MAX_ERROR        3
+#define FORMAT_SYNDROMES        (FORMAT_MAX_ERROR * 2)
+#define FORMAT_BITS             15
+
+static int format_syndromes(uint16_t u, uint8_t *s)
+{
+	int i;
+	int nonzero = 0;
+
+	memset(s, 0, MAX_POLY);
+
+	for (i = 0; i < FORMAT_SYNDROMES; i++) {
+		int j;
+
+		s[i] = 0;
+		for (j = 0; j < FORMAT_BITS; j++)
+			if (u & (1 << j))
+				s[i] ^= gf16_exp[((i + 1) * j) % 15];
+
+		if (s[i])
+			nonzero = 1;
+	}
+
+	return nonzero;
+}
+
+static quirc_decode_error_t correct_format(uint16_t *f_ret)
+{
+	uint16_t u = *f_ret;
+	int i;
+	uint8_t s[MAX_POLY];
+	uint8_t sigma[MAX_POLY];
+
+	/* Evaluate U (received codeword) at each of alpha_1 .. alpha_6
+	 * to get S_1 .. S_6 (but we index them from 0).
+	 */
+	if (!format_syndromes(u, s))
+		return QUIRC_SUCCESS;
+
+	berlekamp_massey(s, FORMAT_SYNDROMES, &gf16, sigma);
+
+	/* Now, find the roots of the polynomial */
+	for (i = 0; i < 15; i++)
+		if (!poly_eval(sigma, gf16_exp[15 - i], &gf16))
+			u ^= (1 << i);
+
+	if (format_syndromes(u, s))
+		return QUIRC_ERROR_FORMAT_ECC;
+
+	*f_ret = u;
+	return QUIRC_SUCCESS;
+}
+
+/************************************************************************
+ * Decoder algorithm
+ */
+
+struct datastream {
+	uint8_t		raw[QUIRC_MAX_PAYLOAD];
+	int		data_bits;
+	int		ptr;
+
+	uint8_t         data[QUIRC_MAX_PAYLOAD];
+};
+
+static inline int grid_bit(const struct quirc_code *code, int x, int y)
+{
+	int p = y * code->size + x;
+
+	return (code->cell_bitmap[p >> 3] >> (p & 7)) & 1;
+}
+
+static quirc_decode_error_t read_format(const struct quirc_code *code,
+					struct quirc_data *data, int which)
+{
+	int i;
+	uint16_t format = 0;
+	uint16_t fdata;
+	quirc_decode_error_t err;
+
+	if (which) {
+		for (i = 0; i < 7; i++)
+			format = (format << 1) |
+				grid_bit(code, 8, code->size - 1 - i);
+		for (i = 0; i < 8; i++)
+			format = (format << 1) |
+				grid_bit(code, code->size - 8 + i, 8);
+	} else {
+		static const int xs[15] = {
+			8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0
+		};
+		static const int ys[15] = {
+			0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8
+		};
+
+		for (i = 14; i >= 0; i--)
+			format = (format << 1) | grid_bit(code, xs[i], ys[i]);
+	}
+
+	format ^= 0x5412;
+
+	err = correct_format(&format);
+	if (err)
+		return err;
+
+	fdata = format >> 10;
+	data->ecc_level = fdata >> 3;
+	data->mask = fdata & 7;
+
+	return QUIRC_SUCCESS;
+}
+
+static int mask_bit(int mask, int i, int j)
+{
+	switch (mask) {
+	case 0: return !((i + j) % 2);
+	case 1: return !(i % 2);
+	case 2: return !(j % 3);
+	case 3: return !((i + j) % 3);
+	case 4: return !(((i / 2) + (j / 3)) % 2);
+	case 5: return !((i * j) % 2 + (i * j) % 3);
+	case 6: return !(((i * j) % 2 + (i * j) % 3) % 2);
+	case 7: return !(((i * j) % 3 + (i + j) % 2) % 2);
+	}
+
+	return 0;
+}
+
+static int reserved_cell(int version, int i, int j)
+{
+	const struct quirc_version_info *ver = &quirc_version_db[version];
+	int size = version * 4 + 17;
+	int ai = -1, aj = -1, a;
+
+	/* Finder + format: top left */
+	if (i < 9 && j < 9)
+		return 1;
+
+	/* Finder + format: bottom left */
+	if (i + 8 >= size && j < 9)
+		return 1;
+
+	/* Finder + format: top right */
+	if (i < 9 && j + 8 >= size)
+		return 1;
+
+	/* Exclude timing patterns */
+	if (i == 6 || j == 6)
+		return 1;
+
+	/* Exclude version info, if it exists. Version info sits adjacent to
+	 * the top-right and bottom-left finders in three rows, bounded by
+	 * the timing pattern.
+	 */
+	if (version >= 7) {
+		if (i < 6 && j + 11 >= size)
+			return 1;
+		if (i + 11 >= size && j < 6)
+			return 1;
+	}
+
+	/* Exclude alignment patterns */
+	for (a = 0; a < QUIRC_MAX_ALIGNMENT && ver->apat[a]; a++) {
+		int p = ver->apat[a];
+
+		if (abs(p - i) < 3)
+			ai = a;
+		if (abs(p - j) < 3)
+			aj = a;
+	}
+
+	if (ai >= 0 && aj >= 0) {
+		a--;
+		if (ai > 0 && ai < a)
+			return 1;
+		if (aj > 0 && aj < a)
+			return 1;
+		if (aj == a && ai == a)
+			return 1;
+	}
+
+	return 0;
+}
+
+static void read_bit(const struct quirc_code *code,
+		     struct quirc_data *data,
+		     struct datastream *ds, int i, int j)
+{
+	int bitpos = ds->data_bits & 7;
+	int bytepos = ds->data_bits >> 3;
+	int v = grid_bit(code, j, i);
+
+	if (mask_bit(data->mask, i, j))
+		v ^= 1;
+
+	if (v)
+		ds->raw[bytepos] |= (0x80 >> bitpos);
+
+	ds->data_bits++;
+}
+
+static void read_data(const struct quirc_code *code,
+		      struct quirc_data *data,
+		      struct datastream *ds)
+{
+	int y = code->size - 1;
+	int x = code->size - 1;
+	int dir = -1;
+
+	while (x > 0) {
+		if (x == 6)
+			x--;
+
+		if (!reserved_cell(data->version, y, x))
+			read_bit(code, data, ds, y, x);
+
+		if (!reserved_cell(data->version, y, x - 1))
+			read_bit(code, data, ds, y, x - 1);
+
+		y += dir;
+		if (y < 0 || y >= code->size) {
+			dir = -dir;
+			x -= 2;
+			y += dir;
+		}
+	}
+}
+
+static quirc_decode_error_t codestream_ecc(struct quirc_data *data,
+					   struct datastream *ds)
+{
+	const struct quirc_version_info *ver =
+		&quirc_version_db[data->version];
+	const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level];
+	struct quirc_rs_params lb_ecc;
+	const int lb_count =
+	    (ver->data_bytes - sb_ecc->bs * sb_ecc->ns) / (sb_ecc->bs + 1);
+	const int bc = lb_count + sb_ecc->ns;
+	const int ecc_offset = sb_ecc->dw * bc + lb_count;
+	int dst_offset = 0;
+	int i;
+
+	memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc));
+	lb_ecc.dw++;
+	lb_ecc.bs++;
+
+	for (i = 0; i < bc; i++) {
+		uint8_t *dst = ds->data + dst_offset;
+		const struct quirc_rs_params *ecc =
+		    (i < sb_ecc->ns) ? sb_ecc : &lb_ecc;
+		const int num_ec = ecc->bs - ecc->dw;
+		quirc_decode_error_t err;
+		int j;
+
+		for (j = 0; j < ecc->dw; j++)
+			dst[j] = ds->raw[j * bc + i];
+		for (j = 0; j < num_ec; j++)
+			dst[ecc->dw + j] = ds->raw[ecc_offset + j * bc + i];
+
+		err = correct_block(dst, ecc);
+		if (err)
+			return err;
+
+		dst_offset += ecc->dw;
+	}
+
+	ds->data_bits = dst_offset * 8;
+
+	return QUIRC_SUCCESS;
+}
+
+static inline int bits_remaining(const struct datastream *ds)
+{
+	return ds->data_bits - ds->ptr;
+}
+
+static int take_bits(struct datastream *ds, int len)
+{
+	int ret = 0;
+
+	while (len && (ds->ptr < ds->data_bits)) {
+		uint8_t b = ds->data[ds->ptr >> 3];
+		int bitpos = ds->ptr & 7;
+
+		ret <<= 1;
+		if ((b << bitpos) & 0x80)
+			ret |= 1;
+
+		ds->ptr++;
+		len--;
+	}
+
+	return ret;
+}
+
+static int numeric_tuple(struct quirc_data *data,
+			 struct datastream *ds,
+			 int bits, int digits)
+{
+	int tuple;
+	int i;
+
+	if (bits_remaining(ds) < bits)
+		return -1;
+
+	tuple = take_bits(ds, bits);
+
+	for (i = digits - 1; i >= 0; i--) {
+		data->payload[data->payload_len + i] = tuple % 10 + '0';
+		tuple /= 10;
+	}
+
+	data->payload_len += digits;
+	return 0;
+}
+
+static quirc_decode_error_t decode_numeric(struct quirc_data *data,
+					   struct datastream *ds)
+{
+	int bits = 14;
+	int count;
+
+	if (data->version < 10)
+		bits = 10;
+	else if (data->version < 27)
+		bits = 12;
+
+	count = take_bits(ds, bits);
+	if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
+		return QUIRC_ERROR_DATA_OVERFLOW;
+
+	while (count >= 3) {
+		if (numeric_tuple(data, ds, 10, 3) < 0)
+			return QUIRC_ERROR_DATA_UNDERFLOW;
+		count -= 3;
+	}
+
+	if (count >= 2) {
+		if (numeric_tuple(data, ds, 7, 2) < 0)
+			return QUIRC_ERROR_DATA_UNDERFLOW;
+		count -= 2;
+	}
+
+	if (count) {
+		if (numeric_tuple(data, ds, 4, 1) < 0)
+			return QUIRC_ERROR_DATA_UNDERFLOW;
+		count--;
+	}
+
+	return QUIRC_SUCCESS;
+}
+
+static int alpha_tuple(struct quirc_data *data,
+		       struct datastream *ds,
+		       int bits, int digits)
+{
+	int tuple;
+	int i;
+
+	if (bits_remaining(ds) < bits)
+		return -1;
+
+	tuple = take_bits(ds, bits);
+
+	for (i = 0; i < digits; i++) {
+		static const char *alpha_map =
+			"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
+
+		data->payload[data->payload_len + digits - i - 1] =
+			alpha_map[tuple % 45];
+		tuple /= 45;
+	}
+
+	data->payload_len += digits;
+	return 0;
+}
+
+static quirc_decode_error_t decode_alpha(struct quirc_data *data,
+					 struct datastream *ds)
+{
+	int bits = 13;
+	int count;
+
+	if (data->version < 10)
+		bits = 9;
+	else if (data->version < 27)
+		bits = 11;
+
+	count = take_bits(ds, bits);
+	if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
+		return QUIRC_ERROR_DATA_OVERFLOW;
+
+	while (count >= 2) {
+		if (alpha_tuple(data, ds, 11, 2) < 0)
+			return QUIRC_ERROR_DATA_UNDERFLOW;
+		count -= 2;
+	}
+
+	if (count) {
+		if (alpha_tuple(data, ds, 6, 1) < 0)
+			return QUIRC_ERROR_DATA_UNDERFLOW;
+		count--;
+	}
+
+	return QUIRC_SUCCESS;
+}
+
+static quirc_decode_error_t decode_byte(struct quirc_data *data,
+					struct datastream *ds)
+{
+	int bits = 16;
+	int count;
+	int i;
+
+	if (data->version < 10)
+		bits = 8;
+
+	count = take_bits(ds, bits);
+	if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
+		return QUIRC_ERROR_DATA_OVERFLOW;
+	if (bits_remaining(ds) < count * 8)
+		return QUIRC_ERROR_DATA_UNDERFLOW;
+
+	for (i = 0; i < count; i++)
+		data->payload[data->payload_len++] = take_bits(ds, 8);
+
+	return QUIRC_SUCCESS;
+}
+
+static quirc_decode_error_t decode_kanji(struct quirc_data *data,
+					 struct datastream *ds)
+{
+	int bits = 12;
+	int count;
+	int i;
+
+	if (data->version < 10)
+		bits = 8;
+	else if (data->version < 27)
+		bits = 10;
+
+	count = take_bits(ds, bits);
+	if (data->payload_len + count * 2 + 1 > QUIRC_MAX_PAYLOAD)
+		return QUIRC_ERROR_DATA_OVERFLOW;
+	if (bits_remaining(ds) < count * 13)
+		return QUIRC_ERROR_DATA_UNDERFLOW;
+
+	for (i = 0; i < count; i++) {
+		int d = take_bits(ds, 13);
+		int msB = d / 0xc0;
+		int lsB = d % 0xc0;
+		int intermediate = (msB << 8) | lsB;
+		uint16_t sjw;
+
+		if (intermediate + 0x8140 <= 0x9ffc) {
+			/* bytes are in the range 0x8140 to 0x9FFC */
+			sjw = intermediate + 0x8140;
+		} else {
+			/* bytes are in the range 0xE040 to 0xEBBF */
+			sjw = intermediate + 0xc140;
+		}
+
+		data->payload[data->payload_len++] = sjw >> 8;
+		data->payload[data->payload_len++] = sjw & 0xff;
+	}
+
+	return QUIRC_SUCCESS;
+}
+
+static quirc_decode_error_t decode_eci(struct quirc_data *data,
+				       struct datastream *ds)
+{
+	if (bits_remaining(ds) < 8)
+		return QUIRC_ERROR_DATA_UNDERFLOW;
+
+	data->eci = take_bits(ds, 8);
+
+	if ((data->eci & 0xc0) == 0x80) {
+		if (bits_remaining(ds) < 8)
+			return QUIRC_ERROR_DATA_UNDERFLOW;
+
+		data->eci = (data->eci << 8) | take_bits(ds, 8);
+	} else if ((data->eci & 0xe0) == 0xc0) {
+		if (bits_remaining(ds) < 16)
+			return QUIRC_ERROR_DATA_UNDERFLOW;
+
+		data->eci = (data->eci << 16) | take_bits(ds, 16);
+	}
+
+	return QUIRC_SUCCESS;
+}
+
+static quirc_decode_error_t decode_payload(struct quirc_data *data,
+					   struct datastream *ds)
+{
+	while (bits_remaining(ds) >= 4) {
+		quirc_decode_error_t err = QUIRC_SUCCESS;
+		int type = take_bits(ds, 4);
+
+		switch (type) {
+		case QUIRC_DATA_TYPE_NUMERIC:
+			err = decode_numeric(data, ds);
+			break;
+
+		case QUIRC_DATA_TYPE_ALPHA:
+			err = decode_alpha(data, ds);
+			break;
+
+		case QUIRC_DATA_TYPE_BYTE:
+			err = decode_byte(data, ds);
+			break;
+
+		case QUIRC_DATA_TYPE_KANJI:
+			err = decode_kanji(data, ds);
+			break;
+
+		case 7:
+			err = decode_eci(data, ds);
+			break;
+
+		default:
+			goto done;
+		}
+
+		if (err)
+			return err;
+
+		if (!(type & (type - 1)) && (type > data->data_type))
+			data->data_type = type;
+	}
+done:
+
+	/* Add nul terminator to all payloads */
+	if (data->payload_len >= sizeof(data->payload))
+		data->payload_len--;
+	data->payload[data->payload_len] = 0;
+
+	return QUIRC_SUCCESS;
+}
+
+quirc_decode_error_t quirc_decode(const struct quirc_code *code,
+				  struct quirc_data *data)
+{
+	quirc_decode_error_t err;
+	struct datastream ds;
+
+	if ((code->size - 17) % 4)
+		return QUIRC_ERROR_INVALID_GRID_SIZE;
+
+	memset(data, 0, sizeof(*data));
+	memset(&ds, 0, sizeof(ds));
+
+	data->version = (code->size - 17) / 4;
+
+	if (data->version < 1 ||
+	    data->version > QUIRC_MAX_VERSION)
+		return QUIRC_ERROR_INVALID_VERSION;
+
+	/* Read format information -- try both locations */
+	err = read_format(code, data, 0);
+	if (err)
+		err = read_format(code, data, 1);
+	if (err)
+		return err;
+
+	read_data(code, data, &ds);
+	err = codestream_ecc(data, &ds);
+	if (err)
+		return err;
+
+	err = decode_payload(data, &ds);
+	if (err)
+		return err;
+
+	return QUIRC_SUCCESS;
+}
diff --git a/src/third_party/quirc/lib/identify.c b/src/third_party/quirc/lib/identify.c
new file mode 100644
index 0000000..d6f5516
--- /dev/null
+++ b/src/third_party/quirc/lib/identify.c
@@ -0,0 +1,1149 @@
+/* quirc - QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include "quirc_internal.h"
+
+/************************************************************************
+ * Linear algebra routines
+ */
+
+static int line_intersect(const struct quirc_point *p0,
+			  const struct quirc_point *p1,
+			  const struct quirc_point *q0,
+			  const struct quirc_point *q1,
+			  struct quirc_point *r)
+{
+	/* (a, b) is perpendicular to line p */
+	int a = -(p1->y - p0->y);
+	int b = p1->x - p0->x;
+
+	/* (c, d) is perpendicular to line q */
+	int c = -(q1->y - q0->y);
+	int d = q1->x - q0->x;
+
+	/* e and f are dot products of the respective vectors with p and q */
+	int e = a * p1->x + b * p1->y;
+	int f = c * q1->x + d * q1->y;
+
+	/* Now we need to solve:
+	 *     [a b] [rx]   [e]
+	 *     [c d] [ry] = [f]
+	 *
+	 * We do this by inverting the matrix and applying it to (e, f):
+	 *       [ d -b] [e]   [rx]
+	 * 1/det [-c  a] [f] = [ry]
+	 */
+	int det = (a * d) - (b * c);
+
+	if (!det)
+		return 0;
+
+	r->x = (d * e - b * f) / det;
+	r->y = (-c * e + a * f) / det;
+
+	return 1;
+}
+
+static void perspective_setup(double *c,
+			      const struct quirc_point *rect,
+			      double w, double h)
+{
+	double x0 = rect[0].x;
+	double y0 = rect[0].y;
+	double x1 = rect[1].x;
+	double y1 = rect[1].y;
+	double x2 = rect[2].x;
+	double y2 = rect[2].y;
+	double x3 = rect[3].x;
+	double y3 = rect[3].y;
+
+	double wden = w * (x2*y3 - x3*y2 + (x3-x2)*y1 + x1*(y2-y3));
+	double hden = h * (x2*y3 + x1*(y2-y3) - x3*y2 + (x3-x2)*y1);
+
+	c[0] = (x1*(x2*y3-x3*y2) + x0*(-x2*y3+x3*y2+(x2-x3)*y1) +
+		x1*(x3-x2)*y0) / wden;
+	c[1] = -(x0*(x2*y3+x1*(y2-y3)-x2*y1) - x1*x3*y2 + x2*x3*y1
+		 + (x1*x3-x2*x3)*y0) / hden;
+	c[2] = x0;
+	c[3] = (y0*(x1*(y3-y2)-x2*y3+x3*y2) + y1*(x2*y3-x3*y2) +
+		x0*y1*(y2-y3)) / wden;
+	c[4] = (x0*(y1*y3-y2*y3) + x1*y2*y3 - x2*y1*y3 +
+		y0*(x3*y2-x1*y2+(x2-x3)*y1)) / hden;
+	c[5] = y0;
+	c[6] = (x1*(y3-y2) + x0*(y2-y3) + (x2-x3)*y1 + (x3-x2)*y0) / wden;
+	c[7] = (-x2*y3 + x1*y3 + x3*y2 + x0*(y1-y2) - x3*y1 + (x2-x1)*y0) /
+		hden;
+}
+
+static void perspective_map(const double *c,
+			    double u, double v, struct quirc_point *ret)
+{
+	double den = c[6]*u + c[7]*v + 1.0;
+	double x = (c[0]*u + c[1]*v + c[2]) / den;
+	double y = (c[3]*u + c[4]*v + c[5]) / den;
+
+	ret->x = rint(x);
+	ret->y = rint(y);
+}
+
+static void perspective_unmap(const double *c,
+			      const struct quirc_point *in,
+			      double *u, double *v)
+{
+	double x = in->x;
+	double y = in->y;
+	double den = -c[0]*c[7]*y + c[1]*c[6]*y + (c[3]*c[7]-c[4]*c[6])*x +
+		c[0]*c[4] - c[1]*c[3];
+
+	*u = -(c[1]*(y-c[5]) - c[2]*c[7]*y + (c[5]*c[7]-c[4])*x + c[2]*c[4]) /
+		den;
+	*v = (c[0]*(y-c[5]) - c[2]*c[6]*y + (c[5]*c[6]-c[3])*x + c[2]*c[3]) /
+		den;
+}
+
+/************************************************************************
+ * Span-based floodfill routine
+ */
+
+#define FLOOD_FILL_MAX_DEPTH		4096
+
+typedef void (*span_func_t)(void *user_data, int y, int left, int right);
+
+static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
+			    span_func_t func, void *user_data,
+			    int depth)
+{
+	int left = x;
+	int right = x;
+	int i;
+	quirc_pixel_t *row = q->pixels + y * q->w;
+
+	if (depth >= FLOOD_FILL_MAX_DEPTH)
+		return;
+
+	while (left > 0 && row[left - 1] == from)
+		left--;
+
+	while (right < q->w - 1 && row[right + 1] == from)
+		right++;
+
+	/* Fill the extent */
+	for (i = left; i <= right; i++)
+		row[i] = to;
+
+	if (func)
+		func(user_data, y, left, right);
+
+	/* Seed new flood-fills */
+	if (y > 0) {
+		row = q->pixels + (y - 1) * q->w;
+
+		for (i = left; i <= right; i++)
+			if (row[i] == from)
+				flood_fill_seed(q, i, y - 1, from, to,
+						func, user_data, depth + 1);
+	}
+
+	if (y < q->h - 1) {
+		row = q->pixels + (y + 1) * q->w;
+
+		for (i = left; i <= right; i++)
+			if (row[i] == from)
+				flood_fill_seed(q, i, y + 1, from, to,
+						func, user_data, depth + 1);
+	}
+}
+
+/************************************************************************
+ * Adaptive thresholding
+ */
+
+#define THRESHOLD_S_MIN		1
+#define THRESHOLD_S_DEN		8
+#define THRESHOLD_T		5
+
+static void threshold(struct quirc *q)
+{
+	int x, y;
+	int avg_w = 0;
+	int avg_u = 0;
+	int threshold_s = q->w / THRESHOLD_S_DEN;
+	quirc_pixel_t *row = q->pixels;
+
+	/*
+	 * Ensure a sane, non-zero value for threshold_s.
+	 *
+	 * threshold_s can be zero if the image width is small. We need to avoid
+	 * SIGFPE as it will be used as divisor.
+	 */
+	if (threshold_s < THRESHOLD_S_MIN)
+		threshold_s = THRESHOLD_S_MIN;
+
+	for (y = 0; y < q->h; y++) {
+		memset(q->row_average, 0, q->w * sizeof(int));
+
+		for (x = 0; x < q->w; x++) {
+			int w, u;
+
+			if (y & 1) {
+				w = x;
+				u = q->w - 1 - x;
+			} else {
+				w = q->w - 1 - x;
+				u = x;
+			}
+
+			avg_w = (avg_w * (threshold_s - 1)) /
+				threshold_s + row[w];
+			avg_u = (avg_u * (threshold_s - 1)) /
+				threshold_s + row[u];
+
+			q->row_average[w] += avg_w;
+			q->row_average[u] += avg_u;
+		}
+
+		for (x = 0; x < q->w; x++) {
+			if (row[x] < q->row_average[x] *
+			    (100 - THRESHOLD_T) / (200 * threshold_s))
+				row[x] = QUIRC_PIXEL_BLACK;
+			else
+				row[x] = QUIRC_PIXEL_WHITE;
+		}
+
+		row += q->w;
+	}
+}
+
+static void area_count(void *user_data, int y, int left, int right)
+{
+	((struct quirc_region *)user_data)->count += right - left + 1;
+}
+
+static int region_code(struct quirc *q, int x, int y)
+{
+	int pixel;
+	struct quirc_region *box;
+	int region;
+
+	if (x < 0 || y < 0 || x >= q->w || y >= q->h)
+		return -1;
+
+	pixel = q->pixels[y * q->w + x];
+
+	if (pixel >= QUIRC_PIXEL_REGION)
+		return pixel;
+
+	if (pixel == QUIRC_PIXEL_WHITE)
+		return -1;
+
+	if (q->num_regions >= QUIRC_MAX_REGIONS)
+		return -1;
+
+	region = q->num_regions;
+	box = &q->regions[q->num_regions++];
+
+	memset(box, 0, sizeof(*box));
+
+	box->seed.x = x;
+	box->seed.y = y;
+	box->capstone = -1;
+
+	flood_fill_seed(q, x, y, pixel, region, area_count, box, 0);
+
+	return region;
+}
+
+struct polygon_score_data {
+	struct quirc_point	ref;
+
+	int			scores[4];
+	struct quirc_point	*corners;
+};
+
+static void find_one_corner(void *user_data, int y, int left, int right)
+{
+	struct polygon_score_data *psd =
+		(struct polygon_score_data *)user_data;
+	int xs[2] = {left, right};
+	int dy = y - psd->ref.y;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		int dx = xs[i] - psd->ref.x;
+		int d = dx * dx + dy * dy;
+
+		if (d > psd->scores[0]) {
+			psd->scores[0] = d;
+			psd->corners[0].x = xs[i];
+			psd->corners[0].y = y;
+		}
+	}
+}
+
+static void find_other_corners(void *user_data, int y, int left, int right)
+{
+	struct polygon_score_data *psd =
+		(struct polygon_score_data *)user_data;
+	int xs[2] = {left, right};
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		int up = xs[i] * psd->ref.x + y * psd->ref.y;
+		int right = xs[i] * -psd->ref.y + y * psd->ref.x;
+		int scores[4] = {up, right, -up, -right};
+		int j;
+
+		for (j = 0; j < 4; j++) {
+			if (scores[j] > psd->scores[j]) {
+				psd->scores[j] = scores[j];
+				psd->corners[j].x = xs[i];
+				psd->corners[j].y = y;
+			}
+		}
+	}
+}
+
+static void find_region_corners(struct quirc *q,
+				int rcode, const struct quirc_point *ref,
+				struct quirc_point *corners)
+{
+	struct quirc_region *region = &q->regions[rcode];
+	struct polygon_score_data psd;
+	int i;
+
+	memset(&psd, 0, sizeof(psd));
+	psd.corners = corners;
+
+	memcpy(&psd.ref, ref, sizeof(psd.ref));
+	psd.scores[0] = -1;
+	flood_fill_seed(q, region->seed.x, region->seed.y,
+			rcode, QUIRC_PIXEL_BLACK,
+			find_one_corner, &psd, 0);
+
+	psd.ref.x = psd.corners[0].x - psd.ref.x;
+	psd.ref.y = psd.corners[0].y - psd.ref.y;
+
+	for (i = 0; i < 4; i++)
+		memcpy(&psd.corners[i], &region->seed,
+		       sizeof(psd.corners[i]));
+
+	i = region->seed.x * psd.ref.x + region->seed.y * psd.ref.y;
+	psd.scores[0] = i;
+	psd.scores[2] = -i;
+	i = region->seed.x * -psd.ref.y + region->seed.y * psd.ref.x;
+	psd.scores[1] = i;
+	psd.scores[3] = -i;
+
+	flood_fill_seed(q, region->seed.x, region->seed.y,
+			QUIRC_PIXEL_BLACK, rcode,
+			find_other_corners, &psd, 0);
+}
+
+static void record_capstone(struct quirc *q, int ring, int stone)
+{
+	struct quirc_region *stone_reg = &q->regions[stone];
+	struct quirc_region *ring_reg = &q->regions[ring];
+	struct quirc_capstone *capstone;
+	int cs_index;
+
+	if (q->num_capstones >= QUIRC_MAX_CAPSTONES)
+		return;
+
+	cs_index = q->num_capstones;
+	capstone = &q->capstones[q->num_capstones++];
+
+	memset(capstone, 0, sizeof(*capstone));
+
+	capstone->qr_grid = -1;
+	capstone->ring = ring;
+	capstone->stone = stone;
+	stone_reg->capstone = cs_index;
+	ring_reg->capstone = cs_index;
+
+	/* Find the corners of the ring */
+	find_region_corners(q, ring, &stone_reg->seed, capstone->corners);
+
+	/* Set up the perspective transform and find the center */
+	perspective_setup(capstone->c, capstone->corners, 7.0, 7.0);
+	perspective_map(capstone->c, 3.5, 3.5, &capstone->center);
+}
+
+static void test_capstone(struct quirc *q, int x, int y, int *pb)
+{
+	int ring_right = region_code(q, x - pb[4], y);
+	int stone = region_code(q, x - pb[4] - pb[3] - pb[2], y);
+	int ring_left = region_code(q, x - pb[4] - pb[3] -
+				    pb[2] - pb[1] - pb[0],
+				    y);
+	struct quirc_region *stone_reg;
+	struct quirc_region *ring_reg;
+	int ratio;
+
+	if (ring_left < 0 || ring_right < 0 || stone < 0)
+		return;
+
+	/* Left and ring of ring should be connected */
+	if (ring_left != ring_right)
+		return;
+
+	/* Ring should be disconnected from stone */
+	if (ring_left == stone)
+		return;
+
+	stone_reg = &q->regions[stone];
+	ring_reg = &q->regions[ring_left];
+
+	/* Already detected */
+	if (stone_reg->capstone >= 0 || ring_reg->capstone >= 0)
+		return;
+
+	/* Ratio should ideally be 37.5 */
+	ratio = stone_reg->count * 100 / ring_reg->count;
+	if (ratio < 10 || ratio > 70)
+		return;
+
+	record_capstone(q, ring_left, stone);
+}
+
+static void finder_scan(struct quirc *q, int y)
+{
+	quirc_pixel_t *row = q->pixels + y * q->w;
+	int x;
+	int last_color = 0;
+	int run_length = 0;
+	int run_count = 0;
+	int pb[5];
+
+	memset(pb, 0, sizeof(pb));
+	for (x = 0; x < q->w; x++) {
+		int color = row[x] ? 1 : 0;
+
+		if (x && color != last_color) {
+			memmove(pb, pb + 1, sizeof(pb[0]) * 4);
+			pb[4] = run_length;
+			run_length = 0;
+			run_count++;
+
+			if (!color && run_count >= 5) {
+				static int check[5] = {1, 1, 3, 1, 1};
+				int avg, err;
+				int i;
+				int ok = 1;
+
+				avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4;
+				err = avg * 3 / 4;
+
+				for (i = 0; i < 5; i++)
+					if (pb[i] < check[i] * avg - err ||
+					    pb[i] > check[i] * avg + err)
+						ok = 0;
+
+				if (ok)
+					test_capstone(q, x, y, pb);
+			}
+		}
+
+		run_length++;
+		last_color = color;
+	}
+}
+
+static void find_alignment_pattern(struct quirc *q, int index)
+{
+	struct quirc_grid *qr = &q->grids[index];
+	struct quirc_capstone *c0 = &q->capstones[qr->caps[0]];
+	struct quirc_capstone *c2 = &q->capstones[qr->caps[2]];
+	struct quirc_point a;
+	struct quirc_point b;
+	struct quirc_point c;
+	int size_estimate;
+	int step_size = 1;
+	int dir = 0;
+	double u, v;
+
+	/* Grab our previous estimate of the alignment pattern corner */
+	memcpy(&b, &qr->align, sizeof(b));
+
+	/* Guess another two corners of the alignment pattern so that we
+	 * can estimate its size.
+	 */
+	perspective_unmap(c0->c, &b, &u, &v);
+	perspective_map(c0->c, u, v + 1.0, &a);
+	perspective_unmap(c2->c, &b, &u, &v);
+	perspective_map(c2->c, u + 1.0, v, &c);
+
+	size_estimate = abs((a.x - b.x) * -(c.y - b.y) +
+			    (a.y - b.y) * (c.x - b.x));
+
+	/* Spiral outwards from the estimate point until we find something
+	 * roughly the right size. Don't look too far from the estimate
+	 * point.
+	 */
+	while (step_size * step_size < size_estimate * 100) {
+		static const int dx_map[] = {1, 0, -1, 0};
+		static const int dy_map[] = {0, -1, 0, 1};
+		int i;
+
+		for (i = 0; i < step_size; i++) {
+			int code = region_code(q, b.x, b.y);
+
+			if (code >= 0) {
+				struct quirc_region *reg = &q->regions[code];
+
+				if (reg->count >= size_estimate / 2 &&
+				    reg->count <= size_estimate * 2) {
+					qr->align_region = code;
+					return;
+				}
+			}
+
+			b.x += dx_map[dir];
+			b.y += dy_map[dir];
+		}
+
+		dir = (dir + 1) % 4;
+		if (!(dir & 1))
+			step_size++;
+	}
+}
+
+static void find_leftmost_to_line(void *user_data, int y, int left, int right)
+{
+	struct polygon_score_data *psd =
+		(struct polygon_score_data *)user_data;
+	int xs[2] = {left, right};
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		int d = -psd->ref.y * xs[i] + psd->ref.x * y;
+
+		if (d < psd->scores[0]) {
+			psd->scores[0] = d;
+			psd->corners[0].x = xs[i];
+			psd->corners[0].y = y;
+		}
+	}
+}
+
+/* Do a Bresenham scan from one point to another and count the number
+ * of black/white transitions.
+ */
+static int timing_scan(const struct quirc *q,
+		       const struct quirc_point *p0,
+		       const struct quirc_point *p1)
+{
+	int n = p1->x - p0->x;
+	int d = p1->y - p0->y;
+	int x = p0->x;
+	int y = p0->y;
+	int *dom, *nondom;
+	int dom_step;
+	int nondom_step;
+	int a = 0;
+	int i;
+	int run_length = 0;
+	int count = 0;
+
+	if (p0->x < 0 || p0->y < 0 || p0->x >= q->w || p0->y >= q->h)
+		return -1;
+	if (p1->x < 0 || p1->y < 0 || p1->x >= q->w || p1->y >= q->h)
+		return -1;
+
+	if (abs(n) > abs(d)) {
+		int swap = n;
+
+		n = d;
+		d = swap;
+
+		dom = &x;
+		nondom = &y;
+	} else {
+		dom = &y;
+		nondom = &x;
+	}
+
+	if (n < 0) {
+		n = -n;
+		nondom_step = -1;
+	} else {
+		nondom_step = 1;
+	}
+
+	if (d < 0) {
+		d = -d;
+		dom_step = -1;
+	} else {
+		dom_step = 1;
+	}
+
+	x = p0->x;
+	y = p0->y;
+	for (i = 0; i <= d; i++) {
+		int pixel;
+
+		if (y < 0 || y >= q->h || x < 0 || x >= q->w)
+			break;
+
+		pixel = q->pixels[y * q->w + x];
+
+		if (pixel) {
+			if (run_length >= 2)
+				count++;
+			run_length = 0;
+		} else {
+			run_length++;
+		}
+
+		a += n;
+		*dom += dom_step;
+		if (a >= d) {
+			*nondom += nondom_step;
+			a -= d;
+		}
+	}
+
+	return count;
+}
+
+/* Try the measure the timing pattern for a given QR code. This does
+ * not require the global perspective to have been set up, but it
+ * does require that the capstone corners have been set to their
+ * canonical rotation.
+ *
+ * For each capstone, we find a point in the middle of the ring band
+ * which is nearest the centre of the code. Using these points, we do
+ * a horizontal and a vertical timing scan.
+ */
+static int measure_timing_pattern(struct quirc *q, int index)
+{
+	struct quirc_grid *qr = &q->grids[index];
+	int i;
+	int scan;
+	int ver;
+	int size;
+
+	for (i = 0; i < 3; i++) {
+		static const double us[] = {6.5, 6.5, 0.5};
+		static const double vs[] = {0.5, 6.5, 6.5};
+		struct quirc_capstone *cap = &q->capstones[qr->caps[i]];
+
+		perspective_map(cap->c, us[i], vs[i], &qr->tpep[i]);
+	}
+
+	qr->hscan = timing_scan(q, &qr->tpep[1], &qr->tpep[2]);
+	qr->vscan = timing_scan(q, &qr->tpep[1], &qr->tpep[0]);
+
+	scan = qr->hscan;
+	if (qr->vscan > scan)
+		scan = qr->vscan;
+
+	/* If neither scan worked, we can't go any further. */
+	if (scan < 0)
+		return -1;
+
+	/* Choose the nearest allowable grid size */
+	size = scan * 2 + 13;
+	ver = (size - 15) / 4;
+	qr->grid_size = ver * 4 + 17;
+
+	return 0;
+}
+
+/* Read a cell from a grid using the currently set perspective
+ * transform. Returns +/- 1 for black/white, 0 for cells which are
+ * out of image bounds.
+ */
+static int read_cell(const struct quirc *q, int index, int x, int y)
+{
+	const struct quirc_grid *qr = &q->grids[index];
+	struct quirc_point p;
+
+	perspective_map(qr->c, x + 0.5, y + 0.5, &p);
+	if (p.y < 0 || p.y >= q->h || p.x < 0 || p.x >= q->w)
+		return 0;
+
+	return q->pixels[p.y * q->w + p.x] ? 1 : -1;
+}
+
+static int fitness_cell(const struct quirc *q, int index, int x, int y)
+{
+	const struct quirc_grid *qr = &q->grids[index];
+	int score = 0;
+	int u, v;
+
+	for (v = 0; v < 3; v++)
+		for (u = 0; u < 3; u++) {
+			static const double offsets[] = {0.3, 0.5, 0.7};
+			struct quirc_point p;
+
+			perspective_map(qr->c, x + offsets[u],
+					       y + offsets[v], &p);
+			if (p.y < 0 || p.y >= q->h || p.x < 0 || p.x >= q->w)
+				continue;
+
+			if (q->pixels[p.y * q->w + p.x])
+				score++;
+			else
+				score--;
+		}
+
+	return score;
+}
+
+static int fitness_ring(const struct quirc *q, int index, int cx, int cy,
+			int radius)
+{
+	int i;
+	int score = 0;
+
+	for (i = 0; i < radius * 2; i++) {
+		score += fitness_cell(q, index, cx - radius + i, cy - radius);
+		score += fitness_cell(q, index, cx - radius, cy + radius - i);
+		score += fitness_cell(q, index, cx + radius, cy - radius + i);
+		score += fitness_cell(q, index, cx + radius - i, cy + radius);
+	}
+
+	return score;
+}
+
+static int fitness_apat(const struct quirc *q, int index, int cx, int cy)
+{
+	return fitness_cell(q, index, cx, cy) -
+		fitness_ring(q, index, cx, cy, 1) +
+		fitness_ring(q, index, cx, cy, 2);
+}
+
+static int fitness_capstone(const struct quirc *q, int index, int x, int y)
+{
+	x += 3;
+	y += 3;
+
+	return fitness_cell(q, index, x, y) +
+		fitness_ring(q, index, x, y, 1) -
+		fitness_ring(q, index, x, y, 2) +
+		fitness_ring(q, index, x, y, 3);
+}
+
+/* Compute a fitness score for the currently configured perspective
+ * transform, using the features we expect to find by scanning the
+ * grid.
+ */
+static int fitness_all(const struct quirc *q, int index)
+{
+	const struct quirc_grid *qr = &q->grids[index];
+	int version = (qr->grid_size - 17) / 4;
+	const struct quirc_version_info *info = &quirc_version_db[version];
+	int score = 0;
+	int i, j;
+	int ap_count;
+
+	/* Check the timing pattern */
+	for (i = 0; i < qr->grid_size - 14; i++) {
+		int expect = (i & 1) ? 1 : -1;
+
+		score += fitness_cell(q, index, i + 7, 6) * expect;
+		score += fitness_cell(q, index, 6, i + 7) * expect;
+	}
+
+	/* Check capstones */
+	score += fitness_capstone(q, index, 0, 0);
+	score += fitness_capstone(q, index, qr->grid_size - 7, 0);
+	score += fitness_capstone(q, index, 0, qr->grid_size - 7);
+
+	if (version < 0 || version > QUIRC_MAX_VERSION)
+		return score;
+
+	/* Check alignment patterns */
+	ap_count = 0;
+	while ((ap_count < QUIRC_MAX_ALIGNMENT) && info->apat[ap_count])
+		ap_count++;
+
+	for (i = 1; i + 1 < ap_count; i++) {
+		score += fitness_apat(q, index, 6, info->apat[i]);
+		score += fitness_apat(q, index, info->apat[i], 6);
+	}
+
+	for (i = 1; i < ap_count; i++)
+		for (j = 1; j < ap_count; j++)
+			score += fitness_apat(q, index,
+					info->apat[i], info->apat[j]);
+
+	return score;
+}
+
+static void jiggle_perspective(struct quirc *q, int index)
+{
+	struct quirc_grid *qr = &q->grids[index];
+	int best = fitness_all(q, index);
+	int pass;
+	double adjustments[8];
+	int i;
+
+	for (i = 0; i < 8; i++)
+		adjustments[i] = qr->c[i] * 0.02;
+
+	for (pass = 0; pass < 5; pass++) {
+		for (i = 0; i < 16; i++) {
+			int j = i >> 1;
+			int test;
+			double old = qr->c[j];
+			double step = adjustments[j];
+			double new;
+
+			if (i & 1)
+				new = old + step;
+			else
+				new = old - step;
+
+			qr->c[j] = new;
+			test = fitness_all(q, index);
+
+			if (test > best)
+				best = test;
+			else
+				qr->c[j] = old;
+		}
+
+		for (i = 0; i < 8; i++)
+			adjustments[i] *= 0.5;
+	}
+}
+
+/* Once the capstones are in place and an alignment point has been
+ * chosen, we call this function to set up a grid-reading perspective
+ * transform.
+ */
+static void setup_qr_perspective(struct quirc *q, int index)
+{
+	struct quirc_grid *qr = &q->grids[index];
+	struct quirc_point rect[4];
+
+	/* Set up the perspective map for reading the grid */
+	memcpy(&rect[0], &q->capstones[qr->caps[1]].corners[0],
+	       sizeof(rect[0]));
+	memcpy(&rect[1], &q->capstones[qr->caps[2]].corners[0],
+	       sizeof(rect[0]));
+	memcpy(&rect[2], &qr->align, sizeof(rect[0]));
+	memcpy(&rect[3], &q->capstones[qr->caps[0]].corners[0],
+	       sizeof(rect[0]));
+	perspective_setup(qr->c, rect, qr->grid_size - 7, qr->grid_size - 7);
+
+	jiggle_perspective(q, index);
+}
+
+/* Rotate the capstone with so that corner 0 is the leftmost with respect
+ * to the given reference line.
+ */
+static void rotate_capstone(struct quirc_capstone *cap,
+			    const struct quirc_point *h0,
+			    const struct quirc_point *hd)
+{
+	struct quirc_point copy[4];
+	int j;
+	int best;
+	int best_score;
+
+	for (j = 0; j < 4; j++) {
+		struct quirc_point *p = &cap->corners[j];
+		int score = (p->x - h0->x) * -hd->y +
+			(p->y - h0->y) * hd->x;
+
+		if (!j || score < best_score) {
+			best = j;
+			best_score = score;
+		}
+	}
+
+	/* Rotate the capstone */
+	for (j = 0; j < 4; j++)
+		memcpy(&copy[j], &cap->corners[(j + best) % 4],
+		       sizeof(copy[j]));
+	memcpy(cap->corners, copy, sizeof(cap->corners));
+	perspective_setup(cap->c, cap->corners, 7.0, 7.0);
+}
+
+static void record_qr_grid(struct quirc *q, int a, int b, int c)
+{
+	struct quirc_point h0, hd;
+	int i;
+	int qr_index;
+	struct quirc_grid *qr;
+
+	if (q->num_grids >= QUIRC_MAX_GRIDS)
+		return;
+
+	/* Construct the hypotenuse line from A to C. B should be to
+	 * the left of this line.
+	 */
+	memcpy(&h0, &q->capstones[a].center, sizeof(h0));
+	hd.x = q->capstones[c].center.x - q->capstones[a].center.x;
+	hd.y = q->capstones[c].center.y - q->capstones[a].center.y;
+
+	/* Make sure A-B-C is clockwise */
+	if ((q->capstones[b].center.x - h0.x) * -hd.y +
+	    (q->capstones[b].center.y - h0.y) * hd.x > 0) {
+		int swap = a;
+
+		a = c;
+		c = swap;
+		hd.x = -hd.x;
+		hd.y = -hd.y;
+	}
+
+	/* Record the grid and its components */
+	qr_index = q->num_grids;
+	qr = &q->grids[q->num_grids++];
+
+	memset(qr, 0, sizeof(*qr));
+	qr->caps[0] = a;
+	qr->caps[1] = b;
+	qr->caps[2] = c;
+	qr->align_region = -1;
+
+	/* Rotate each capstone so that corner 0 is top-left with respect
+	 * to the grid.
+	 */
+	for (i = 0; i < 3; i++) {
+		struct quirc_capstone *cap = &q->capstones[qr->caps[i]];
+
+		rotate_capstone(cap, &h0, &hd);
+		cap->qr_grid = qr_index;
+	}
+
+	/* Check the timing pattern. This doesn't require a perspective
+	 * transform.
+	 */
+	if (measure_timing_pattern(q, qr_index) < 0)
+		goto fail;
+
+	/* Make an estimate based for the alignment pattern based on extending
+	 * lines from capstones A and C.
+	 */
+	if (!line_intersect(&q->capstones[a].corners[0],
+			    &q->capstones[a].corners[1],
+			    &q->capstones[c].corners[0],
+			    &q->capstones[c].corners[3],
+			    &qr->align))
+		goto fail;
+
+	/* On V2+ grids, we should use the alignment pattern. */
+	if (qr->grid_size > 21) {
+		/* Try to find the actual location of the alignment pattern. */
+		find_alignment_pattern(q, qr_index);
+
+		/* Find the point of the alignment pattern closest to the
+		 * top-left of the QR grid.
+		 */
+		if (qr->align_region >= 0) {
+			struct polygon_score_data psd;
+			struct quirc_region *reg =
+				&q->regions[qr->align_region];
+
+			/* Start from some point inside the alignment pattern */
+			memcpy(&qr->align, &reg->seed, sizeof(qr->align));
+
+			memcpy(&psd.ref, &hd, sizeof(psd.ref));
+			psd.corners = &qr->align;
+			psd.scores[0] = -hd.y * qr->align.x +
+				hd.x * qr->align.y;
+
+			flood_fill_seed(q, reg->seed.x, reg->seed.y,
+					qr->align_region, QUIRC_PIXEL_BLACK,
+					NULL, NULL, 0);
+			flood_fill_seed(q, reg->seed.x, reg->seed.y,
+					QUIRC_PIXEL_BLACK, qr->align_region,
+					find_leftmost_to_line, &psd, 0);
+		}
+	}
+
+	setup_qr_perspective(q, qr_index);
+	return;
+
+fail:
+	/* We've been unable to complete setup for this grid. Undo what we've
+	 * recorded and pretend it never happened.
+	 */
+	for (i = 0; i < 3; i++)
+		q->capstones[qr->caps[i]].qr_grid = -1;
+	q->num_grids--;
+}
+
+struct neighbour {
+	int		index;
+	double		distance;
+};
+
+struct neighbour_list {
+	struct neighbour	n[QUIRC_MAX_CAPSTONES];
+	int			count;
+};
+
+static void test_neighbours(struct quirc *q, int i,
+			    const struct neighbour_list *hlist,
+			    const struct neighbour_list *vlist)
+{
+	int j, k;
+	double best_score = 0.0;
+	int best_h = -1, best_v = -1;
+
+	/* Test each possible grouping */
+	for (j = 0; j < hlist->count; j++)
+		for (k = 0; k < vlist->count; k++) {
+			const struct neighbour *hn = &hlist->n[j];
+			const struct neighbour *vn = &vlist->n[k];
+			double score = fabs(1.0 - hn->distance / vn->distance);
+
+			if (score > 2.5)
+				continue;
+
+			if (best_h < 0 || score < best_score) {
+				best_h = hn->index;
+				best_v = vn->index;
+				best_score = score;
+			}
+		}
+
+	if (best_h < 0 || best_v < 0)
+		return;
+
+	record_qr_grid(q, best_h, i, best_v);
+}
+
+static void test_grouping(struct quirc *q, int i)
+{
+	struct quirc_capstone *c1 = &q->capstones[i];
+	int j;
+	struct neighbour_list hlist;
+	struct neighbour_list vlist;
+
+	if (c1->qr_grid >= 0)
+		return;
+
+	hlist.count = 0;
+	vlist.count = 0;
+
+	/* Look for potential neighbours by examining the relative gradients
+	 * from this capstone to others.
+	 */
+	for (j = 0; j < q->num_capstones; j++) {
+		struct quirc_capstone *c2 = &q->capstones[j];
+		double u, v;
+
+		if (i == j || c2->qr_grid >= 0)
+			continue;
+
+		perspective_unmap(c1->c, &c2->center, &u, &v);
+
+		u = fabs(u - 3.5);
+		v = fabs(v - 3.5);
+
+		if (u < 0.2 * v) {
+			struct neighbour *n = &hlist.n[hlist.count++];
+
+			n->index = j;
+			n->distance = v;
+		}
+
+		if (v < 0.2 * u) {
+			struct neighbour *n = &vlist.n[vlist.count++];
+
+			n->index = j;
+			n->distance = u;
+		}
+	}
+
+	if (!(hlist.count && vlist.count))
+		return;
+
+	test_neighbours(q, i, &hlist, &vlist);
+}
+
+static void pixels_setup(struct quirc *q)
+{
+	if (sizeof(*q->image) == sizeof(*q->pixels)) {
+		q->pixels = (quirc_pixel_t *)q->image;
+	} else {
+		int x, y;
+		for (y = 0; y < q->h; y++) {
+			for (x = 0; x < q->w; x++) {
+				q->pixels[y * q->w + x] = q->image[y * q->w + x];
+			}
+		}
+	}
+}
+
+uint8_t *quirc_begin(struct quirc *q, int *w, int *h)
+{
+	q->num_regions = QUIRC_PIXEL_REGION;
+	q->num_capstones = 0;
+	q->num_grids = 0;
+
+	if (w)
+		*w = q->w;
+	if (h)
+		*h = q->h;
+
+	return q->image;
+}
+
+void quirc_end(struct quirc *q)
+{
+	int i;
+
+	pixels_setup(q);
+	threshold(q);
+
+	for (i = 0; i < q->h; i++)
+		finder_scan(q, i);
+
+	for (i = 0; i < q->num_capstones; i++)
+		test_grouping(q, i);
+}
+
+void quirc_extract(const struct quirc *q, int index,
+		   struct quirc_code *code)
+{
+	const struct quirc_grid *qr = &q->grids[index];
+	int y;
+	int i = 0;
+
+	if (index < 0 || index > q->num_grids)
+		return;
+
+	memset(code, 0, sizeof(*code));
+
+	perspective_map(qr->c, 0.0, 0.0, &code->corners[0]);
+	perspective_map(qr->c, qr->grid_size, 0.0, &code->corners[1]);
+	perspective_map(qr->c, qr->grid_size, qr->grid_size,
+			&code->corners[2]);
+	perspective_map(qr->c, 0.0, qr->grid_size, &code->corners[3]);
+
+	code->size = qr->grid_size;
+
+	for (y = 0; y < qr->grid_size; y++) {
+		int x;
+
+		for (x = 0; x < qr->grid_size; x++) {
+			if (read_cell(q, index, x, y) > 0)
+				code->cell_bitmap[i >> 3] |= (1 << (i & 7));
+
+			i++;
+		}
+	}
+}
diff --git a/src/third_party/quirc/lib/quirc.c b/src/third_party/quirc/lib/quirc.c
new file mode 100644
index 0000000..9d1c102
--- /dev/null
+++ b/src/third_party/quirc/lib/quirc.c
@@ -0,0 +1,140 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "quirc_internal.h"
+
+const char *quirc_version(void)
+{
+	return "1.0";
+}
+
+struct quirc *quirc_new(void)
+{
+	struct quirc *q = malloc(sizeof(*q));
+
+	if (!q)
+		return NULL;
+
+	memset(q, 0, sizeof(*q));
+	return q;
+}
+
+void quirc_destroy(struct quirc *q)
+{
+	free(q->image);
+	/* q->pixels may alias q->image when their type representation is of the
+	   same size, so we need to be careful here to avoid a double free */
+	if (sizeof(*q->image) != sizeof(*q->pixels))
+		free(q->pixels);
+	free(q->row_average);
+	free(q);
+}
+
+int quirc_resize(struct quirc *q, int w, int h)
+{
+	uint8_t		*image  = NULL;
+	quirc_pixel_t	*pixels = NULL;
+	int		*row_average = NULL;
+
+	/*
+	 * XXX: w and h should be size_t (or at least unsigned) as negatives
+	 * values would not make much sense. The downside is that it would break
+	 * both the API and ABI. Thus, at the moment, let's just do a sanity
+	 * check.
+	 */
+	if (w < 0 || h < 0)
+		goto fail;
+
+	/*
+	 * alloc a new buffer for q->image. We avoid realloc(3) because we want
+	 * on failure to be leave `q` in a consistant, unmodified state.
+	 */
+	image = calloc(w, h);
+	if (!image)
+		goto fail;
+
+	/* compute the "old" (i.e. currently allocated) and the "new"
+	   (i.e. requested) image dimensions */
+	size_t olddim = q->w * q->h;
+	size_t newdim = w * h;
+	size_t min = (olddim < newdim ? olddim : newdim);
+
+	/*
+	 * copy the data into the new buffer, avoiding (a) to read beyond the
+	 * old buffer when the new size is greater and (b) to write beyond the
+	 * new buffer when the new size is smaller, hence the min computation.
+	 */
+	(void)memcpy(image, q->image, min);
+
+	/* alloc a new buffer for q->pixels if needed */
+	if (sizeof(*q->image) != sizeof(*q->pixels)) {
+		pixels = calloc(newdim, sizeof(quirc_pixel_t));
+		if (!pixels)
+			goto fail;
+	}
+
+	/* alloc a new buffer for q->row_average */
+	row_average = calloc(w, sizeof(int));
+	if (!row_average)
+		goto fail;
+
+	/* alloc succeeded, update `q` with the new size and buffers */
+	q->w = w;
+	q->h = h;
+	free(q->image);
+	q->image = image;
+	if (sizeof(*q->image) != sizeof(*q->pixels)) {
+		free(q->pixels);
+		q->pixels = pixels;
+	}
+	free(q->row_average);
+	q->row_average = row_average;
+
+	return 0;
+	/* NOTREACHED */
+fail:
+	free(image);
+	free(pixels);
+	free(row_average);
+
+	return -1;
+}
+
+int quirc_count(const struct quirc *q)
+{
+	return q->num_grids;
+}
+
+static const char *const error_table[] = {
+	[QUIRC_SUCCESS] = "Success",
+	[QUIRC_ERROR_INVALID_GRID_SIZE] = "Invalid grid size",
+	[QUIRC_ERROR_INVALID_VERSION] = "Invalid version",
+	[QUIRC_ERROR_FORMAT_ECC] = "Format data ECC failure",
+	[QUIRC_ERROR_DATA_ECC] = "ECC failure",
+	[QUIRC_ERROR_UNKNOWN_DATA_TYPE] = "Unknown data type",
+	[QUIRC_ERROR_DATA_OVERFLOW] = "Data overflow",
+	[QUIRC_ERROR_DATA_UNDERFLOW] = "Data underflow"
+};
+
+const char *quirc_strerror(quirc_decode_error_t err)
+{
+	if (err >= 0 && err < sizeof(error_table) / sizeof(error_table[0]))
+		return error_table[err];
+
+	return "Unknown error";
+}
diff --git a/src/third_party/quirc/lib/quirc.h b/src/third_party/quirc/lib/quirc.h
new file mode 100644
index 0000000..0e7cb94
--- /dev/null
+++ b/src/third_party/quirc/lib/quirc.h
@@ -0,0 +1,173 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef QUIRC_H_
+#define QUIRC_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct quirc;
+
+/* Obtain the library version string. */
+const char *quirc_version(void);
+
+/* Construct a new QR-code recognizer. This function will return NULL
+ * if sufficient memory could not be allocated.
+ */
+struct quirc *quirc_new(void);
+
+/* Destroy a QR-code recognizer. */
+void quirc_destroy(struct quirc *q);
+
+/* Resize the QR-code recognizer. The size of an image must be
+ * specified before codes can be analyzed.
+ *
+ * This function returns 0 on success, or -1 if sufficient memory could
+ * not be allocated.
+ */
+int quirc_resize(struct quirc *q, int w, int h);
+
+/* These functions are used to process images for QR-code recognition.
+ * quirc_begin() must first be called to obtain access to a buffer into
+ * which the input image should be placed. Optionally, the current
+ * width and height may be returned.
+ *
+ * After filling the buffer, quirc_end() should be called to process
+ * the image for QR-code recognition. The locations and content of each
+ * code may be obtained using accessor functions described below.
+ */
+uint8_t *quirc_begin(struct quirc *q, int *w, int *h);
+void quirc_end(struct quirc *q);
+
+/* This structure describes a location in the input image buffer. */
+struct quirc_point {
+	int	x;
+	int	y;
+};
+
+/* This enum describes the various decoder errors which may occur. */
+typedef enum {
+	QUIRC_SUCCESS = 0,
+	QUIRC_ERROR_INVALID_GRID_SIZE,
+	QUIRC_ERROR_INVALID_VERSION,
+	QUIRC_ERROR_FORMAT_ECC,
+	QUIRC_ERROR_DATA_ECC,
+	QUIRC_ERROR_UNKNOWN_DATA_TYPE,
+	QUIRC_ERROR_DATA_OVERFLOW,
+	QUIRC_ERROR_DATA_UNDERFLOW
+} quirc_decode_error_t;
+
+/* Return a string error message for an error code. */
+const char *quirc_strerror(quirc_decode_error_t err);
+
+/* Limits on the maximum size of QR-codes and their content. */
+#define QUIRC_MAX_BITMAP	3917
+#define QUIRC_MAX_PAYLOAD	8896
+
+/* QR-code ECC types. */
+#define QUIRC_ECC_LEVEL_M     0
+#define QUIRC_ECC_LEVEL_L     1
+#define QUIRC_ECC_LEVEL_H     2
+#define QUIRC_ECC_LEVEL_Q     3
+
+/* QR-code data types. */
+#define QUIRC_DATA_TYPE_NUMERIC       1
+#define QUIRC_DATA_TYPE_ALPHA         2
+#define QUIRC_DATA_TYPE_BYTE          4
+#define QUIRC_DATA_TYPE_KANJI         8
+
+/* Common character encodings */
+#define QUIRC_ECI_ISO_8859_1		1
+#define QUIRC_ECI_IBM437		2
+#define QUIRC_ECI_ISO_8859_2		4
+#define QUIRC_ECI_ISO_8859_3		5
+#define QUIRC_ECI_ISO_8859_4		6
+#define QUIRC_ECI_ISO_8859_5		7
+#define QUIRC_ECI_ISO_8859_6		8
+#define QUIRC_ECI_ISO_8859_7		9
+#define QUIRC_ECI_ISO_8859_8		10
+#define QUIRC_ECI_ISO_8859_9		11
+#define QUIRC_ECI_WINDOWS_874		13
+#define QUIRC_ECI_ISO_8859_13		15
+#define QUIRC_ECI_ISO_8859_15		17
+#define QUIRC_ECI_SHIFT_JIS		20
+#define QUIRC_ECI_UTF_8			26
+
+/* This structure is used to return information about detected QR codes
+ * in the input image.
+ */
+struct quirc_code {
+	/* The four corners of the QR-code, from top left, clockwise */
+	struct quirc_point	corners[4];
+
+	/* The number of cells across in the QR-code. The cell bitmap
+	 * is a bitmask giving the actual values of cells. If the cell
+	 * at (x, y) is black, then the following bit is set:
+	 *
+	 *     cell_bitmap[i >> 3] & (1 << (i & 7))
+	 *
+	 * where i = (y * size) + x.
+	 */
+	int			size;
+	uint8_t			cell_bitmap[QUIRC_MAX_BITMAP];
+};
+
+/* This structure holds the decoded QR-code data */
+struct quirc_data {
+	/* Various parameters of the QR-code. These can mostly be
+	 * ignored if you only care about the data.
+	 */
+	int			version;
+	int			ecc_level;
+	int			mask;
+
+	/* This field is the highest-valued data type found in the QR
+	 * code.
+	 */
+	int			data_type;
+
+	/* Data payload. For the Kanji datatype, payload is encoded as
+	 * Shift-JIS. For all other datatypes, payload is ASCII text.
+	 */
+	uint8_t			payload[QUIRC_MAX_PAYLOAD];
+	int			payload_len;
+
+	/* ECI assignment number */
+	uint32_t		eci;
+};
+
+/* Return the number of QR-codes identified in the last processed
+ * image.
+ */
+int quirc_count(const struct quirc *q);
+
+/* Extract the QR-code specified by the given index. */
+void quirc_extract(const struct quirc *q, int index,
+		   struct quirc_code *code);
+
+/* Decode a QR-code, returning the payload data. */
+quirc_decode_error_t quirc_decode(const struct quirc_code *code,
+				  struct quirc_data *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/third_party/quirc/lib/quirc_internal.h b/src/third_party/quirc/lib/quirc_internal.h
new file mode 100644
index 0000000..70d481d
--- /dev/null
+++ b/src/third_party/quirc/lib/quirc_internal.h
@@ -0,0 +1,115 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef QUIRC_INTERNAL_H_
+#define QUIRC_INTERNAL_H_
+
+#include "quirc.h"
+
+#define QUIRC_PIXEL_WHITE	0
+#define QUIRC_PIXEL_BLACK	1
+#define QUIRC_PIXEL_REGION	2
+
+#ifndef QUIRC_MAX_REGIONS
+#define QUIRC_MAX_REGIONS	254
+#endif
+#define QUIRC_MAX_CAPSTONES	32
+#define QUIRC_MAX_GRIDS		8
+
+#define QUIRC_PERSPECTIVE_PARAMS	8
+
+#if QUIRC_MAX_REGIONS < UINT8_MAX
+typedef uint8_t quirc_pixel_t;
+#elif QUIRC_MAX_REGIONS < UINT16_MAX
+typedef uint16_t quirc_pixel_t;
+#else
+#error "QUIRC_MAX_REGIONS > 65534 is not supported"
+#endif
+
+struct quirc_region {
+	struct quirc_point	seed;
+	int			count;
+	int			capstone;
+};
+
+struct quirc_capstone {
+	int			ring;
+	int			stone;
+
+	struct quirc_point	corners[4];
+	struct quirc_point	center;
+	double			c[QUIRC_PERSPECTIVE_PARAMS];
+
+	int			qr_grid;
+};
+
+struct quirc_grid {
+	/* Capstone indices */
+	int			caps[3];
+
+	/* Alignment pattern region and corner */
+	int			align_region;
+	struct quirc_point	align;
+
+	/* Timing pattern endpoints */
+	struct quirc_point	tpep[3];
+	int			hscan;
+	int			vscan;
+
+	/* Grid size and perspective transform */
+	int			grid_size;
+	double			c[QUIRC_PERSPECTIVE_PARAMS];
+};
+
+struct quirc {
+	uint8_t			*image;
+	quirc_pixel_t		*pixels;
+	int			*row_average; /* used by threshold() */
+	int			w;
+	int			h;
+
+	int			num_regions;
+	struct quirc_region	regions[QUIRC_MAX_REGIONS];
+
+	int			num_capstones;
+	struct quirc_capstone	capstones[QUIRC_MAX_CAPSTONES];
+
+	int			num_grids;
+	struct quirc_grid	grids[QUIRC_MAX_GRIDS];
+};
+
+/************************************************************************
+ * QR-code version information database
+ */
+
+#define QUIRC_MAX_VERSION     40
+#define QUIRC_MAX_ALIGNMENT   7
+
+struct quirc_rs_params {
+	int             bs; /* Small block size */
+	int             dw; /* Small data words */
+	int		ns; /* Number of small blocks */
+};
+
+struct quirc_version_info {
+	int				data_bytes;
+	int				apat[QUIRC_MAX_ALIGNMENT];
+	struct quirc_rs_params          ecc[4];
+};
+
+extern const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1];
+
+#endif
diff --git a/src/third_party/quirc/lib/version_db.c b/src/third_party/quirc/lib/version_db.c
new file mode 100644
index 0000000..fea8146
--- /dev/null
+++ b/src/third_party/quirc/lib/version_db.c
@@ -0,0 +1,421 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "quirc_internal.h"
+
+const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1] = {
+	    {0},
+	    { /* Version 1 */
+		    .data_bytes = 26,
+		    .apat = {0},
+		    .ecc = {
+			    {.bs = 26, .dw = 16, .ns = 1},
+			    {.bs = 26, .dw = 19, .ns = 1},
+			    {.bs = 26, .dw = 9, .ns = 1},
+			    {.bs = 26, .dw = 13, .ns = 1}
+		    }
+	    },
+	    { /* Version 2 */
+		    .data_bytes = 44,
+		    .apat = {6, 18, 0},
+		    .ecc = {
+			    {.bs = 44, .dw = 28, .ns = 1},
+			    {.bs = 44, .dw = 34, .ns = 1},
+			    {.bs = 44, .dw = 16, .ns = 1},
+			    {.bs = 44, .dw = 22, .ns = 1}
+		    }
+	    },
+	    { /* Version 3 */
+		    .data_bytes = 70,
+		    .apat = {6, 22, 0},
+		    .ecc = {
+			    {.bs = 70, .dw = 44, .ns = 1},
+			    {.bs = 70, .dw = 55, .ns = 1},
+			    {.bs = 35, .dw = 13, .ns = 2},
+			    {.bs = 35, .dw = 17, .ns = 2}
+		    }
+	    },
+	    { /* Version 4 */
+		    .data_bytes = 100,
+		    .apat = {6, 26, 0},
+		    .ecc = {
+			    {.bs = 50, .dw = 32, .ns = 2},
+			    {.bs = 100, .dw = 80, .ns = 1},
+			    {.bs = 25, .dw = 9, .ns = 4},
+			    {.bs = 50, .dw = 24, .ns = 2}
+		    }
+	    },
+	    { /* Version 5 */
+		    .data_bytes = 134,
+		    .apat = {6, 30, 0},
+		    .ecc = {
+			    {.bs = 67, .dw = 43, .ns = 2},
+			    {.bs = 134, .dw = 108, .ns = 1},
+			    {.bs = 33, .dw = 11, .ns = 2},
+			    {.bs = 33, .dw = 15, .ns = 2}
+		    }
+	    },
+	    { /* Version 6 */
+		    .data_bytes = 172,
+		    .apat = {6, 34, 0},
+		    .ecc = {
+			    {.bs = 43, .dw = 27, .ns = 4},
+			    {.bs = 86, .dw = 68, .ns = 2},
+			    {.bs = 43, .dw = 15, .ns = 4},
+			    {.bs = 43, .dw = 19, .ns = 4}
+		    }
+	    },
+	    { /* Version 7 */
+		    .data_bytes = 196,
+		    .apat = {6, 22, 38, 0},
+		    .ecc = {
+			    {.bs = 49, .dw = 31, .ns = 4},
+			    {.bs = 98, .dw = 78, .ns = 2},
+			    {.bs = 39, .dw = 13, .ns = 4},
+			    {.bs = 32, .dw = 14, .ns = 2}
+		    }
+	    },
+	    { /* Version 8 */
+		    .data_bytes = 242,
+		    .apat = {6, 24, 42, 0},
+		    .ecc = {
+			    {.bs = 60, .dw = 38, .ns = 2},
+			    {.bs = 121, .dw = 97, .ns = 2},
+			    {.bs = 40, .dw = 14, .ns = 4},
+			    {.bs = 40, .dw = 18, .ns = 4}
+		    }
+	    },
+	    { /* Version 9 */
+		    .data_bytes = 292,
+		    .apat = {6, 26, 46, 0},
+		    .ecc = {
+			    {.bs = 58, .dw = 36, .ns = 3},
+			    {.bs = 146, .dw = 116, .ns = 2},
+			    {.bs = 36, .dw = 12, .ns = 4},
+			    {.bs = 36, .dw = 16, .ns = 4}
+		    }
+	    },
+	    { /* Version 10 */
+		    .data_bytes = 346,
+		    .apat = {6, 28, 50, 0},
+		    .ecc = {
+			    {.bs = 69, .dw = 43, .ns = 4},
+			    {.bs = 86, .dw = 68, .ns = 2},
+			    {.bs = 43, .dw = 15, .ns = 6},
+			    {.bs = 43, .dw = 19, .ns = 6}
+		    }
+	    },
+	    { /* Version 11 */
+		    .data_bytes = 404,
+		    .apat = {6, 30, 54, 0},
+		    .ecc = {
+			    {.bs = 80, .dw = 50, .ns = 1},
+			    {.bs = 101, .dw = 81, .ns = 4},
+			    {.bs = 36, .dw = 12, .ns = 3},
+			    {.bs = 50, .dw = 22, .ns = 4}
+		    }
+	    },
+	    { /* Version 12 */
+		    .data_bytes = 466,
+		    .apat = {6, 32, 58, 0},
+		    .ecc = {
+			    {.bs = 58, .dw = 36, .ns = 6},
+			    {.bs = 116, .dw = 92, .ns = 2},
+			    {.bs = 42, .dw = 14, .ns = 7},
+			    {.bs = 46, .dw = 20, .ns = 4}
+		    }
+	    },
+	    { /* Version 13 */
+		    .data_bytes = 532,
+		    .apat = {6, 34, 62, 0},
+		    .ecc = {
+			    {.bs = 59, .dw = 37, .ns = 8},
+			    {.bs = 133, .dw = 107, .ns = 4},
+			    {.bs = 33, .dw = 11, .ns = 12},
+			    {.bs = 44, .dw = 20, .ns = 8}
+		    }
+	    },
+	    { /* Version 14 */
+		    .data_bytes = 581,
+		    .apat = {6, 26, 46, 66, 0},
+		    .ecc = {
+			    {.bs = 64, .dw = 40, .ns = 4},
+			    {.bs = 145, .dw = 115, .ns = 3},
+			    {.bs = 36, .dw = 12, .ns = 11},
+			    {.bs = 36, .dw = 16, .ns = 11}
+		    }
+	    },
+	    { /* Version 15 */
+		    .data_bytes = 655,
+		    .apat = {6, 26, 48, 70, 0},
+		    .ecc = {
+			    {.bs = 65, .dw = 41, .ns = 5},
+			    {.bs = 109, .dw = 87, .ns = 5},
+			    {.bs = 36, .dw = 12, .ns = 11},
+			    {.bs = 54, .dw = 24, .ns = 5}
+		    }
+	    },
+	    { /* Version 16 */
+		    .data_bytes = 733,
+		    .apat = {6, 26, 50, 74, 0},
+		    .ecc = {
+			    {.bs = 73, .dw = 45, .ns = 7},
+			    {.bs = 122, .dw = 98, .ns = 5},
+			    {.bs = 45, .dw = 15, .ns = 3},
+			    {.bs = 43, .dw = 19, .ns = 15}
+		    }
+	    },
+	    { /* Version 17 */
+		    .data_bytes = 815,
+		    .apat = {6, 30, 54, 78, 0},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 10},
+			    {.bs = 135, .dw = 107, .ns = 1},
+			    {.bs = 42, .dw = 14, .ns = 2},
+			    {.bs = 50, .dw = 22, .ns = 1}
+		    }
+	    },
+	    { /* Version 18 */
+		    .data_bytes = 901,
+		    .apat = {6, 30, 56, 82, 0},
+		    .ecc = {
+			    {.bs = 69, .dw = 43, .ns = 9},
+			    {.bs = 150, .dw = 120, .ns = 5},
+			    {.bs = 42, .dw = 14, .ns = 2},
+			    {.bs = 50, .dw = 22, .ns = 17}
+		    }
+	    },
+	    { /* Version 19 */
+		    .data_bytes = 991,
+		    .apat = {6, 30, 58, 86, 0},
+		    .ecc = {
+			    {.bs = 70, .dw = 44, .ns = 3},
+			    {.bs = 141, .dw = 113, .ns = 3},
+			    {.bs = 39, .dw = 13, .ns = 9},
+			    {.bs = 47, .dw = 21, .ns = 17}
+		    }
+	    },
+	    { /* Version 20 */
+		    .data_bytes = 1085,
+		    .apat = {6, 34, 62, 90, 0},
+		    .ecc = {
+			    {.bs = 67, .dw = 41, .ns = 3},
+			    {.bs = 135, .dw = 107, .ns = 3},
+			    {.bs = 43, .dw = 15, .ns = 15},
+			    {.bs = 54, .dw = 24, .ns = 15}
+		    }
+	    },
+	    { /* Version 21 */
+		    .data_bytes = 1156,
+		    .apat = {6, 28, 50, 72, 92, 0},
+		    .ecc = {
+			    {.bs = 68, .dw = 42, .ns = 17},
+			    {.bs = 144, .dw = 116, .ns = 4},
+			    {.bs = 46, .dw = 16, .ns = 19},
+			    {.bs = 50, .dw = 22, .ns = 17}
+		    }
+	    },
+	    { /* Version 22 */
+		    .data_bytes = 1258,
+		    .apat = {6, 26, 50, 74, 98, 0},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 17},
+			    {.bs = 139, .dw = 111, .ns = 2},
+			    {.bs = 37, .dw = 13, .ns = 34},
+			    {.bs = 54, .dw = 24, .ns = 7}
+		    }
+	    },
+	    { /* Version 23 */
+		    .data_bytes = 1364,
+		    .apat = {6, 30, 54, 78, 102, 0},
+		    .ecc = {
+			    {.bs = 75, .dw = 47, .ns = 4},
+			    {.bs = 151, .dw = 121, .ns = 4},
+			    {.bs = 45, .dw = 15, .ns = 16},
+			    {.bs = 54, .dw = 24, .ns = 11}
+		    }
+	    },
+	    { /* Version 24 */
+		    .data_bytes = 1474,
+		    .apat = {6, 28, 54, 80, 106, 0},
+		    .ecc = {
+			    {.bs = 73, .dw = 45, .ns = 6},
+			    {.bs = 147, .dw = 117, .ns = 6},
+			    {.bs = 46, .dw = 16, .ns = 30},
+			    {.bs = 54, .dw = 24, .ns = 11}
+		    }
+	    },
+	    { /* Version 25 */
+		    .data_bytes = 1588,
+		    .apat = {6, 32, 58, 84, 110, 0},
+		    .ecc = {
+			    {.bs = 75, .dw = 47, .ns = 8},
+			    {.bs = 132, .dw = 106, .ns = 8},
+			    {.bs = 45, .dw = 15, .ns = 22},
+			    {.bs = 54, .dw = 24, .ns = 7}
+		    }
+	    },
+	    { /* Version 26 */
+		    .data_bytes = 1706,
+		    .apat = {6, 30, 58, 86, 114, 0},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 19},
+			    {.bs = 142, .dw = 114, .ns = 10},
+			    {.bs = 46, .dw = 16, .ns = 33},
+			    {.bs = 50, .dw = 22, .ns = 28}
+		    }
+	    },
+	    { /* Version 27 */
+		    .data_bytes = 1828,
+		    .apat = {6, 34, 62, 90, 118, 0},
+		    .ecc = {
+			    {.bs = 73, .dw = 45, .ns = 22},
+			    {.bs = 152, .dw = 122, .ns = 8},
+			    {.bs = 45, .dw = 15, .ns = 12},
+			    {.bs = 53, .dw = 23, .ns = 8}
+		    }
+	    },
+	    { /* Version 28 */
+		    .data_bytes = 1921,
+		    .apat = {6, 26, 50, 74, 98, 122, 0},
+		    .ecc = {
+			    {.bs = 73, .dw = 45, .ns = 3},
+			    {.bs = 147, .dw = 117, .ns = 3},
+			    {.bs = 45, .dw = 15, .ns = 11},
+			    {.bs = 54, .dw = 24, .ns = 4}
+		    }
+	    },
+	    { /* Version 29 */
+		    .data_bytes = 2051,
+		    .apat = {6, 30, 54, 78, 102, 126, 0},
+		    .ecc = {
+			    {.bs = 73, .dw = 45, .ns = 21},
+			    {.bs = 146, .dw = 116, .ns = 7},
+			    {.bs = 45, .dw = 15, .ns = 19},
+			    {.bs = 53, .dw = 23, .ns = 1}
+		    }
+	    },
+	    { /* Version 30 */
+		    .data_bytes = 2185,
+		    .apat = {6, 26, 52, 78, 104, 130, 0},
+		    .ecc = {
+			    {.bs = 75, .dw = 47, .ns = 19},
+			    {.bs = 145, .dw = 115, .ns = 5},
+			    {.bs = 45, .dw = 15, .ns = 23},
+			    {.bs = 54, .dw = 24, .ns = 15}
+		    }
+	    },
+	    { /* Version 31 */
+		    .data_bytes = 2323,
+		    .apat = {6, 30, 56, 82, 108, 134, 0},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 2},
+			    {.bs = 145, .dw = 115, .ns = 13},
+			    {.bs = 45, .dw = 15, .ns = 23},
+			    {.bs = 54, .dw = 24, .ns = 42}
+		    }
+	    },
+	    { /* Version 32 */
+		    .data_bytes = 2465,
+		    .apat = {6, 34, 60, 86, 112, 138, 0},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 10},
+			    {.bs = 145, .dw = 115, .ns = 17},
+			    {.bs = 45, .dw = 15, .ns = 19},
+			    {.bs = 54, .dw = 24, .ns = 10}
+		    }
+	    },
+	    { /* Version 33 */
+		    .data_bytes = 2611,
+		    .apat = {6, 30, 58, 86, 114, 142, 0},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 14},
+			    {.bs = 145, .dw = 115, .ns = 17},
+			    {.bs = 45, .dw = 15, .ns = 11},
+			    {.bs = 54, .dw = 24, .ns = 29}
+		    }
+	    },
+	    { /* Version 34 */
+		    .data_bytes = 2761,
+		    .apat = {6, 34, 62, 90, 118, 146, 0},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 14},
+			    {.bs = 145, .dw = 115, .ns = 13},
+			    {.bs = 46, .dw = 16, .ns = 59},
+			    {.bs = 54, .dw = 24, .ns = 44}
+		    }
+	    },
+	    { /* Version 35 */
+		    .data_bytes = 2876,
+		    .apat = {6, 30, 54, 78, 102, 126, 150},
+		    .ecc = {
+			    {.bs = 75, .dw = 47, .ns = 12},
+			    {.bs = 151, .dw = 121, .ns = 12},
+			    {.bs = 45, .dw = 15, .ns = 22},
+			    {.bs = 54, .dw = 24, .ns = 39}
+		    }
+	    },
+	    { /* Version 36 */
+		    .data_bytes = 3034,
+		    .apat = {6, 24, 50, 76, 102, 128, 154},
+		    .ecc = {
+			    {.bs = 75, .dw = 47, .ns = 6},
+			    {.bs = 151, .dw = 121, .ns = 6},
+			    {.bs = 45, .dw = 15, .ns = 2},
+			    {.bs = 54, .dw = 24, .ns = 46}
+		    }
+	    },
+	    { /* Version 37 */
+		    .data_bytes = 3196,
+		    .apat = {6, 28, 54, 80, 106, 132, 158},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 29},
+			    {.bs = 152, .dw = 122, .ns = 17},
+			    {.bs = 45, .dw = 15, .ns = 24},
+			    {.bs = 54, .dw = 24, .ns = 49}
+		    }
+	    },
+	    { /* Version 38 */
+		    .data_bytes = 3362,
+		    .apat = {6, 32, 58, 84, 110, 136, 162},
+		    .ecc = {
+			    {.bs = 74, .dw = 46, .ns = 13},
+			    {.bs = 152, .dw = 122, .ns = 4},
+			    {.bs = 45, .dw = 15, .ns = 42},
+			    {.bs = 54, .dw = 24, .ns = 48}
+		    }
+	    },
+	    { /* Version 39 */
+		    .data_bytes = 3532,
+		    .apat = {6, 26, 54, 82, 110, 138, 166},
+		    .ecc = {
+			    {.bs = 75, .dw = 47, .ns = 40},
+			    {.bs = 147, .dw = 117, .ns = 20},
+			    {.bs = 45, .dw = 15, .ns = 10},
+			    {.bs = 54, .dw = 24, .ns = 43}
+		    }
+	    },
+	    { /* Version 40 */
+		    .data_bytes = 3706,
+		    .apat = {6, 30, 58, 86, 114, 142, 170},
+		    .ecc = {
+			    {.bs = 75, .dw = 47, .ns = 18},
+			    {.bs = 148, .dw = 118, .ns = 19},
+			    {.bs = 45, .dw = 15, .ns = 20},
+			    {.bs = 54, .dw = 24, .ns = 34}
+		    }
+	    }
+};
diff --git a/src/third_party/quirc/tests/dbgutil.c b/src/third_party/quirc/tests/dbgutil.c
new file mode 100644
index 0000000..c023d91
--- /dev/null
+++ b/src/third_party/quirc/tests/dbgutil.c
@@ -0,0 +1,307 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jpeglib.h>
+#include <png.h>
+
+#include <quirc.h>
+
+#include "dbgutil.h"
+
+static const char *data_type_str(int dt)
+{
+	switch (dt) {
+	case QUIRC_DATA_TYPE_NUMERIC: return "NUMERIC";
+	case QUIRC_DATA_TYPE_ALPHA:   return "ALPHA";
+	case QUIRC_DATA_TYPE_BYTE:    return "BYTE";
+	case QUIRC_DATA_TYPE_KANJI:   return "KANJI";
+	}
+
+	return "unknown";
+}
+
+void dump_data(const struct quirc_data *data)
+{
+	printf("    Version: %d\n", data->version);
+	printf("    ECC level: %c\n", "MLHQ"[data->ecc_level]);
+	printf("    Mask: %d\n", data->mask);
+	printf("    Data type: %d (%s)\n",
+	    data->data_type, data_type_str(data->data_type));
+	printf("    Length: %d\n", data->payload_len);
+	printf("    Payload: %s\n", data->payload);
+
+	if (data->eci)
+		printf("    ECI: %d\n", data->eci);
+}
+
+void dump_cells(const struct quirc_code *code)
+{
+	int u, v;
+
+	printf("    %d cells, corners:", code->size);
+	for (u = 0; u < 4; u++)
+		printf(" (%d,%d)", code->corners[u].x,
+				   code->corners[u].y);
+	printf("\n");
+
+	for (v = 0; v < code->size; v++) {
+		printf("    ");
+		for (u = 0; u < code->size; u++) {
+			int p = v * code->size + u;
+
+			if (code->cell_bitmap[p >> 3] & (1 << (p & 7)))
+				printf("[]");
+			else
+				printf("  ");
+		}
+		printf("\n");
+	}
+}
+
+struct my_jpeg_error {
+	struct jpeg_error_mgr   base;
+	jmp_buf                 env;
+};
+
+static void my_output_message(struct jpeg_common_struct *com)
+{
+	struct my_jpeg_error *err = (struct my_jpeg_error *)com->err;
+	char buf[JMSG_LENGTH_MAX];
+
+	err->base.format_message(com, buf);
+	fprintf(stderr, "JPEG error: %s\n", buf);
+}
+
+static void my_error_exit(struct jpeg_common_struct *com)
+{
+	struct my_jpeg_error *err = (struct my_jpeg_error *)com->err;
+
+	my_output_message(com);
+	longjmp(err->env, 0);
+}
+
+static struct jpeg_error_mgr *my_error_mgr(struct my_jpeg_error *err)
+{
+	jpeg_std_error(&err->base);
+
+	err->base.error_exit = my_error_exit;
+	err->base.output_message = my_output_message;
+
+	return &err->base;
+}
+
+int load_jpeg(struct quirc *q, const char *filename)
+{
+	FILE *infile = fopen(filename, "rb");
+	struct jpeg_decompress_struct dinfo;
+	struct my_jpeg_error err;
+	uint8_t *image;
+	int y;
+
+	if (!infile) {
+		perror("can't open input file");
+		return -1;
+	}
+
+	memset(&dinfo, 0, sizeof(dinfo));
+	dinfo.err = my_error_mgr(&err);
+
+	if (setjmp(err.env))
+		goto fail;
+
+	jpeg_create_decompress(&dinfo);
+	jpeg_stdio_src(&dinfo, infile);
+
+	jpeg_read_header(&dinfo, TRUE);
+	dinfo.output_components = 1;
+	dinfo.out_color_space = JCS_GRAYSCALE;
+	jpeg_start_decompress(&dinfo);
+
+	if (dinfo.output_components != 1) {
+		fprintf(stderr, "Unexpected number of output components: %d",
+			 dinfo.output_components);
+		goto fail;
+	}
+
+	if (quirc_resize(q, dinfo.output_width, dinfo.output_height) < 0)
+		goto fail;
+
+	image = quirc_begin(q, NULL, NULL);
+
+	for (y = 0; y < dinfo.output_height; y++) {
+		JSAMPROW row_pointer = image + y * dinfo.output_width;
+
+		jpeg_read_scanlines(&dinfo, &row_pointer, 1);
+	}
+
+	jpeg_finish_decompress(&dinfo);
+	fclose(infile);
+	jpeg_destroy_decompress(&dinfo);
+	return 0;
+
+fail:
+	fclose(infile);
+	jpeg_destroy_decompress(&dinfo);
+	return -1;
+}
+
+/* hacked from https://dev.w3.org/Amaya/libpng/example.c
+ *
+ * Check if a file is a PNG image using png_sig_cmp(). Returns 1 if the given
+ * file is a PNG and 0 otherwise.
+ */
+#define PNG_BYTES_TO_CHECK 4
+int check_if_png(const char *filename)
+{
+	int ret = 0;
+	FILE *infile = NULL;
+	unsigned char buf[PNG_BYTES_TO_CHECK];
+
+	/* Open the prospective PNG file. */
+	if ((infile = fopen(filename, "rb")) == NULL)
+		goto out;
+
+	/* Read in some of the signature bytes */
+	if (fread(buf, 1, PNG_BYTES_TO_CHECK, infile) != PNG_BYTES_TO_CHECK)
+		goto out;
+
+	/*
+	 * Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
+	 * png_sig_cmp() returns zero if the image is a PNG and nonzero if it
+	 * isn't a PNG.
+	 */
+	if (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) == 0)
+		ret = 1;
+
+	/* FALLTHROUGH */
+out:
+	if (infile)
+		fclose(infile);
+	return (ret);
+}
+
+int load_png(struct quirc *q, const char *filename)
+{
+	int width, height, rowbytes, interlace_type, number_passes = 1;
+	png_uint_32 trns;
+	png_byte color_type, bit_depth;
+	png_structp png_ptr = NULL;
+	png_infop info_ptr = NULL;
+	FILE *infile = NULL;
+	uint8_t *image;
+	int ret = -1;
+	int pass;
+
+	if ((infile = fopen(filename, "rb")) == NULL)
+		goto out;
+
+	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+	if (!png_ptr)
+		goto out;
+
+	info_ptr = png_create_info_struct(png_ptr);
+	if (!info_ptr)
+		goto out;
+
+	if (setjmp(png_jmpbuf(png_ptr)))
+		goto out;
+
+	png_init_io(png_ptr, infile);
+
+	png_read_info(png_ptr, info_ptr);
+
+	color_type     = png_get_color_type(png_ptr, info_ptr);
+	bit_depth      = png_get_bit_depth(png_ptr, info_ptr);
+	interlace_type = png_get_interlace_type(png_ptr, info_ptr);
+
+	// Read any color_type into 8bit depth, Grayscale format.
+	// See http://www.libpng.org/pub/png/libpng-manual.txt
+
+	// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
+	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+		png_set_expand_gray_1_2_4_to_8(png_ptr);
+
+	if ((trns = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
+		png_set_tRNS_to_alpha(png_ptr);
+
+	if (bit_depth == 16)
+#if PNG_LIBPNG_VER >= 10504
+		png_set_scale_16(png_ptr);
+#else
+		png_set_strip_16(png_ptr);
+#endif
+
+	if ((trns) || color_type & PNG_COLOR_MASK_ALPHA)
+		png_set_strip_alpha(png_ptr);
+
+	if (color_type == PNG_COLOR_TYPE_PALETTE)
+		png_set_palette_to_rgb(png_ptr);
+
+	if (color_type == PNG_COLOR_TYPE_PALETTE ||
+	    color_type == PNG_COLOR_TYPE_RGB ||
+	    color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+		png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
+	}
+
+	if (interlace_type != PNG_INTERLACE_NONE)
+		number_passes = png_set_interlace_handling(png_ptr);
+
+	png_read_update_info(png_ptr, info_ptr);
+
+	width    = png_get_image_width(png_ptr, info_ptr);
+	height   = png_get_image_height(png_ptr, info_ptr);
+	rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+	if (rowbytes != width) {
+		fprintf(stderr,
+		    "load_png: expected rowbytes to be %u but got %u\n",
+		    width, rowbytes);
+		goto out;
+	}
+
+	if (quirc_resize(q, width, height) < 0)
+		goto out;
+
+	image = quirc_begin(q, NULL, NULL);
+
+	for (pass = 0; pass < number_passes; pass++) {
+		int y;
+
+		for (y = 0; y < height; y++) {
+			png_bytep row_pointer = image + y * width;
+			png_read_rows(png_ptr, &row_pointer, NULL, 1);
+		}
+	}
+
+	png_read_end(png_ptr, info_ptr);
+
+	ret = 0;
+	/* FALLTHROUGH */
+out:
+	/* cleanup */
+	if (png_ptr) {
+		if (info_ptr)
+			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+		else
+			png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+	}
+	if (infile)
+		fclose(infile);
+	return (ret);
+}
diff --git a/src/third_party/quirc/tests/dbgutil.h b/src/third_party/quirc/tests/dbgutil.h
new file mode 100644
index 0000000..7a5cc6a
--- /dev/null
+++ b/src/third_party/quirc/tests/dbgutil.h
@@ -0,0 +1,48 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DBGUTIL_H_
+#define DBGUTIL_H_
+
+#include "quirc.h"
+
+/* Dump decoded information on stdout. */
+void dump_data(const struct quirc_data *data);
+
+/* Dump a grid cell map on stdout. */
+void dump_cells(const struct quirc_code *code);
+
+/* Read a JPEG image into the decoder.
+ *
+ * Note that you must call quirc_end() if the function returns
+ * successfully (0).
+ */
+int load_jpeg(struct quirc *q, const char *filename);
+
+/* Check if a file is a PNG image.
+ *
+ * returns 1 if the given file is a PNG and 0 otherwise.
+ */
+int check_if_png(const char *filename);
+
+/* Read a PNG image into the decoder.
+ *
+ * Note that you must call quirc_end() if the function returns
+ * successfully (0).
+ */
+int load_png(struct quirc *q, const char *filename);
+
+#endif
diff --git a/src/third_party/quirc/tests/inspect.c b/src/third_party/quirc/tests/inspect.c
new file mode 100644
index 0000000..d420a45
--- /dev/null
+++ b/src/third_party/quirc/tests/inspect.c
@@ -0,0 +1,260 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <SDL.h>
+#include <SDL_gfxPrimitives.h>
+#include "quirc_internal.h"
+#include "dbgutil.h"
+
+static void dump_info(struct quirc *q)
+{
+	int count = quirc_count(q);
+	int i;
+
+	printf("%d QR-codes found:\n\n", count);
+	for (i = 0; i < count; i++) {
+		struct quirc_code code;
+		struct quirc_data data;
+		quirc_decode_error_t err;
+
+		quirc_extract(q, i, &code);
+		err = quirc_decode(&code, &data);
+
+		dump_cells(&code);
+		printf("\n");
+
+		if (err) {
+			printf("  Decoding FAILED: %s\n", quirc_strerror(err));
+		} else {
+			printf("  Decoding successful:\n");
+			dump_data(&data);
+		}
+
+		printf("\n");
+	}
+}
+
+static void draw_frame(SDL_Surface *screen, struct quirc *q)
+{
+	uint8_t *pix;
+	uint8_t *raw = q->image;
+	int x, y;
+
+	SDL_LockSurface(screen);
+	pix = screen->pixels;
+	for (y = 0; y < q->h; y++) {
+		uint32_t *row = (uint32_t *)pix;
+
+		for (x = 0; x < q->w; x++) {
+			uint8_t v = *(raw++);
+			uint32_t color = (v << 16) | (v << 8) | v;
+			struct quirc_region *reg = &q->regions[v];
+
+			switch (v) {
+			case QUIRC_PIXEL_WHITE:
+				color = 0x00ffffff;
+				break;
+
+			case QUIRC_PIXEL_BLACK:
+				color = 0x00000000;
+				break;
+
+			default:
+				if (reg->capstone >= 0)
+					color = 0x00008000;
+				else
+					color = 0x00808080;
+				break;
+			}
+
+			*(row++) = color;
+		}
+
+		pix += screen->pitch;
+	}
+	SDL_UnlockSurface(screen);
+}
+
+static void draw_blob(SDL_Surface *screen, int x, int y)
+{
+	int i, j;
+
+	for (i = -2; i <= 2; i++)
+		for (j = -2; j <= 2; j++)
+			pixelColor(screen, x + i, y + j, 0x0000ffff);
+}
+
+static void draw_mark(SDL_Surface *screen, int x, int y)
+{
+	pixelColor(screen, x, y, 0xff0000ff);
+	pixelColor(screen, x + 1, y, 0xff0000ff);
+	pixelColor(screen, x - 1, y, 0xff0000ff);
+	pixelColor(screen, x, y + 1, 0xff0000ff);
+	pixelColor(screen, x, y - 1, 0xff0000ff);
+}
+
+static void draw_capstone(SDL_Surface *screen, struct quirc *q, int index)
+{
+	struct quirc_capstone *cap = &q->capstones[index];
+	int j;
+	char buf[8];
+
+	for (j = 0; j < 4; j++) {
+		struct quirc_point *p0 = &cap->corners[j];
+		struct quirc_point *p1 = &cap->corners[(j + 1) % 4];
+
+		lineColor(screen, p0->x, p0->y, p1->x, p1->y,
+			  0x800080ff);
+	}
+
+	draw_blob(screen, cap->corners[0].x, cap->corners[0].y);
+
+	if (cap->qr_grid < 0) {
+		snprintf(buf, sizeof(buf), "?%d", index);
+		stringColor(screen, cap->center.x, cap->center.y, buf,
+			    0x000000ff);
+	}
+}
+
+static void perspective_map(const double *c,
+			    double u, double v, struct quirc_point *ret)
+{
+	double den = c[6]*u + c[7]*v + 1.0;
+	double x = (c[0]*u + c[1]*v + c[2]) / den;
+	double y = (c[3]*u + c[4]*v + c[5]) / den;
+
+	ret->x = rint(x);
+	ret->y = rint(y);
+}
+
+static void draw_grid(SDL_Surface *screen, struct quirc *q, int index)
+{
+	struct quirc_grid *qr = &q->grids[index];
+	int x, y;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		struct quirc_capstone *cap = &q->capstones[qr->caps[i]];
+		char buf[8];
+
+		snprintf(buf, sizeof(buf), "%d.%c", index, "ABC"[i]);
+		stringColor(screen, cap->center.x, cap->center.y, buf,
+			    0x000000ff);
+	}
+
+	lineColor(screen, qr->tpep[0].x, qr->tpep[0].y,
+		  qr->tpep[1].x, qr->tpep[1].y, 0xff00ffff);
+	lineColor(screen, qr->tpep[1].x, qr->tpep[1].y,
+		  qr->tpep[2].x, qr->tpep[2].y, 0xff00ffff);
+
+	if (qr->align_region >= 0)
+		draw_blob(screen, qr->align.x, qr->align.y);
+
+	for (y = 0; y < qr->grid_size; y++) {
+		for (x = 0; x < qr->grid_size; x++) {
+			double u = x + 0.5;
+			double v = y + 0.5;
+			struct quirc_point p;
+
+			perspective_map(qr->c, u, v, &p);
+			draw_mark(screen, p.x, p.y);
+		}
+	}
+}
+
+static int sdl_examine(struct quirc *q)
+{
+	SDL_Surface *screen;
+	SDL_Event ev;
+
+	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+		fprintf(stderr, "couldn't init SDL: %s\n", SDL_GetError());
+		return -1;
+	}
+
+	screen = SDL_SetVideoMode(q->w, q->h, 32, SDL_SWSURFACE);
+	if (!screen) {
+		fprintf(stderr, "couldn't init video mode: %s\n",
+			SDL_GetError());
+		return -1;
+	}
+
+	while (SDL_WaitEvent(&ev) >= 0) {
+		int i;
+
+		if (ev.type == SDL_QUIT)
+			break;
+
+		if (ev.type == SDL_KEYDOWN &&
+		    ev.key.keysym.sym == 'q')
+			break;
+
+		draw_frame(screen, q);
+		for (i = 0; i < q->num_capstones; i++)
+			draw_capstone(screen, q, i);
+		for (i = 0; i < q->num_grids; i++)
+			draw_grid(screen, q, i);
+		SDL_Flip(screen);
+	}
+
+	SDL_Quit();
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	struct quirc *q;
+
+	printf("quirc inspection program\n");
+	printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
+	printf("Library version: %s\n", quirc_version());
+	printf("\n");
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s <testfile.jpg|testfile.png>\n", argv[0]);
+		return -1;
+	}
+
+	q = quirc_new();
+	if (!q) {
+		perror("can't create quirc object");
+		return -1;
+	}
+
+	int status = -1;
+	if (check_if_png(argv[1])) {
+		status = load_png(q, argv[1]);
+	} else {
+		status = load_jpeg(q, argv[1]);
+	}
+	if (status < 0) {
+		quirc_destroy(q);
+		return -1;
+	}
+
+	quirc_end(q);
+	dump_info(q);
+
+	if (sdl_examine(q) < 0) {
+		quirc_destroy(q);
+		return -1;
+	}
+
+	quirc_destroy(q);
+	return 0;
+}
diff --git a/src/third_party/quirc/tests/qrtest.c b/src/third_party/quirc/tests/qrtest.c
new file mode 100644
index 0000000..2e50698
--- /dev/null
+++ b/src/third_party/quirc/tests/qrtest.c
@@ -0,0 +1,300 @@
+/* quirc -- QR-code recognition library
+ * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <quirc.h>
+#include <jpeglib.h>
+#include <setjmp.h>
+#include <time.h>
+#include "dbgutil.h"
+
+static int want_verbose = 0;
+static int want_cell_dump = 0;
+
+#define MS(ts) (unsigned int)((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000))
+
+static struct quirc *decoder;
+
+struct result_info {
+	int		file_count;
+	int		id_count;
+	int		decode_count;
+
+	unsigned int	load_time;
+	unsigned int	identify_time;
+	unsigned int	total_time;
+};
+
+static void print_result(const char *name, struct result_info *info)
+{
+	puts("----------------------------------------"
+	     "---------------------------------------");
+	printf("%s: %d files, %d codes, %d decoded (%d failures)",
+	       name, info->file_count, info->id_count, info->decode_count,
+	       (info->id_count - info->decode_count));
+	if (info->id_count)
+		printf(", %d%% success rate",
+		       (info->decode_count * 100 + info->id_count / 2) /
+			info->id_count);
+	printf("\n");
+	printf("Total time [load: %u, identify: %u, total: %u]\n",
+	       info->load_time,
+	       info->identify_time,
+	       info->total_time);
+	if (info->file_count)
+		printf("Average time [load: %u, identify: %u, total: %u]\n",
+		       info->load_time / info->file_count,
+		       info->identify_time / info->file_count,
+		       info->total_time / info->file_count);
+}
+
+static void add_result(struct result_info *sum, struct result_info *inf)
+{
+	sum->file_count += inf->file_count;
+	sum->id_count += inf->id_count;
+	sum->decode_count += inf->decode_count;
+
+	sum->load_time += inf->load_time;
+	sum->identify_time += inf->identify_time;
+	sum->total_time += inf->total_time;
+}
+
+static int scan_file(const char *path, const char *filename,
+		     struct result_info *info)
+{
+	int (*loader)(struct quirc *, const char *);
+	int len = strlen(filename);
+	const char *ext;
+	struct timespec tp;
+	unsigned int start;
+	unsigned int total_start;
+	int ret;
+	int i;
+
+	while (len >= 0 && filename[len] != '.')
+		len--;
+	ext = filename + len + 1;
+	if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0)
+		loader = load_jpeg;
+	else if (strcasecmp(ext, "png") == 0)
+		loader = load_png;
+	else
+		return 0;
+
+	(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
+	total_start = start = MS(tp);
+	ret = loader(decoder, path);
+	(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
+	info->load_time = MS(tp) - start;
+
+	if (ret < 0) {
+		fprintf(stderr, "%s: load failed\n", filename);
+		return -1;
+	}
+
+	(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
+	start = MS(tp);
+	quirc_end(decoder);
+	(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
+	info->identify_time = MS(tp) - start;
+
+	info->id_count = quirc_count(decoder);
+	for (i = 0; i < info->id_count; i++) {
+		struct quirc_code code;
+		struct quirc_data data;
+
+		quirc_extract(decoder, i, &code);
+
+		if (!quirc_decode(&code, &data))
+			info->decode_count++;
+	}
+
+	(void)clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
+	info->total_time += MS(tp) - total_start;
+
+	printf("  %-30s: %5u %5u %5u %5d %5d\n", filename,
+	       info->load_time,
+	       info->identify_time,
+	       info->total_time,
+	       info->id_count, info->decode_count);
+
+	if (want_cell_dump || want_verbose) {
+		for (i = 0; i < info->id_count; i++) {
+			struct quirc_code code;
+
+			quirc_extract(decoder, i, &code);
+			if (want_cell_dump) {
+				dump_cells(&code);
+				printf("\n");
+			}
+
+			if (want_verbose) {
+				struct quirc_data data;
+				quirc_decode_error_t err =
+					quirc_decode(&code, &data);
+
+				if (err) {
+					printf("  ERROR: %s\n\n",
+					       quirc_strerror(err));
+				} else {
+					printf("  Decode successful:\n");
+					dump_data(&data);
+					printf("\n");
+				}
+			}
+		}
+	}
+
+	info->file_count = 1;
+	return 1;
+}
+
+static int test_scan(const char *path, struct result_info *info);
+
+static int scan_dir(const char *path, const char *filename,
+		    struct result_info *info)
+{
+	DIR *d = opendir(path);
+	struct dirent *ent;
+	int count = 0;
+
+	if (!d) {
+		fprintf(stderr, "%s: opendir: %s\n", path, strerror(errno));
+		return -1;
+	}
+
+	printf("%s:\n", path);
+
+	while ((ent = readdir(d))) {
+		if (ent->d_name[0] != '.') {
+			char fullpath[1024];
+			struct result_info sub;
+
+			snprintf(fullpath, sizeof(fullpath), "%s/%s",
+				 path, ent->d_name);
+			if (test_scan(fullpath, &sub) > 0) {
+				add_result(info, &sub);
+				count++;
+			}
+		}
+	}
+
+	closedir(d);
+
+	if (count > 1) {
+		print_result(filename, info);
+		puts("");
+	}
+
+	return count > 0;
+}
+
+static int test_scan(const char *path, struct result_info *info)
+{
+	int len = strlen(path);
+	struct stat st;
+	const char *filename;
+
+	memset(info, 0, sizeof(*info));
+
+	while (len >= 0 && path[len] != '/')
+		len--;
+	filename = path + len + 1;
+
+	if (lstat(path, &st) < 0) {
+		fprintf(stderr, "%s: lstat: %s\n", path, strerror(errno));
+		return -1;
+	}
+
+	if (S_ISREG(st.st_mode))
+		return scan_file(path, filename, info);
+
+	if (S_ISDIR(st.st_mode))
+		return scan_dir(path, filename, info);
+
+	return 0;
+}
+
+static int run_tests(int argc, char **argv)
+{
+	struct result_info sum;
+	int count = 0;
+	int i;
+
+	decoder = quirc_new();
+	if (!decoder) {
+		perror("quirc_new");
+		return -1;
+	}
+
+	printf("  %-30s  %17s %11s\n", "", "Time (ms)", "Count");
+	printf("  %-30s  %5s %5s %5s %5s %5s\n",
+	       "Filename", "Load", "ID", "Total", "ID", "Dec");
+	puts("----------------------------------------"
+	     "---------------------------------------");
+
+	memset(&sum, 0, sizeof(sum));
+	for (i = 0; i < argc; i++) {
+		struct result_info info;
+
+		if (test_scan(argv[i], &info) > 0) {
+			add_result(&sum, &info);
+			count++;
+		}
+	}
+
+	if (count > 1)
+		print_result("TOTAL", &sum);
+
+	quirc_destroy(decoder);
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	int opt;
+
+	printf("quirc test program\n");
+	printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
+	printf("Library version: %s\n", quirc_version());
+	printf("\n");
+
+	while ((opt = getopt(argc, argv, "vd")) >= 0)
+		switch (opt) {
+		case 'v':
+			want_verbose = 1;
+			break;
+
+		case 'd':
+			want_cell_dump = 1;
+			break;
+
+		case '?':
+			return -1;
+		}
+
+	argv += optind;
+	argc -= optind;
+
+	return run_tests(argc, argv);;
+}
diff --git a/src/third_party/skia/src/gpu/gl/GrGLCaps.cpp b/src/third_party/skia/src/gpu/gl/GrGLCaps.cpp
index fe879dc..0656cc7 100644
--- a/src/third_party/skia/src/gpu/gl/GrGLCaps.cpp
+++ b/src/third_party/skia/src/gpu/gl/GrGLCaps.cpp
@@ -1639,10 +1639,19 @@
         // We require some form of FBO support and all GLs with FBO support can render to RGBA8
         fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= allRenderFlags;
     } else {
+#if defined(STARBOARD)
+        // Starboard code in GrGLUtil.cpp may override the driver version from
+        // GLES 3.0 to 2.0 if the config specifies that only GLES 2.0 features
+        // should be used. This will confuse the regular capabilities check.
+        // Since all Starboard GLES platforms must support rendering to RGBA8
+        // buffers, no check is needed here.
+        fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= allRenderFlags;
+#else
         if (version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_OES_rgb8_rgba8") ||
             ctxInfo.hasExtension("GL_ARM_rgba8")) {
             fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= allRenderFlags;
         }
+#endif
     }
     if (texStorageSupported) {
         fConfigTable[kRGBA_8888_GrPixelConfig].fFlags |= ConfigInfo::kCanUseTexStorage_Flag;
diff --git a/src/third_party/skia/src/gpu/gl/GrGLUtil.cpp b/src/third_party/skia/src/gpu/gl/GrGLUtil.cpp
index 314fcda7..a2ee4ce 100644
--- a/src/third_party/skia/src/gpu/gl/GrGLUtil.cpp
+++ b/src/third_party/skia/src/gpu/gl/GrGLUtil.cpp
@@ -218,6 +218,12 @@
     // This is useful when a OpenGL 2.0 context is requested and received, but
     // the version string still shows version 3.0
     if (strstr(versionString, "OpenGL ES")) {
+#if defined(STARBOARD) && !defined(GLES3_SUPPORTED)
+        // If the platform has explicitly disabled GLES3, have Skia respect that
+        // it does not have GLES 3 support available.
+        return GR_GL_VER(2, 0);
+#endif
+
         EGLint client_type = -1;
         EGLBoolean success = false;
         do {
diff --git a/src/v8/include/v8config.h b/src/v8/include/v8config.h
index 243c705..fa2639d 100644
--- a/src/v8/include/v8config.h
+++ b/src/v8/include/v8config.h
@@ -417,4 +417,20 @@
 
 // clang-format on
 
+// Prior to mid-2018, Cobalt had its own array buffers / typed array
+// implementations, which were implemented the way other normal web IDL
+// platform objects are implemented.  When they are registered via
+// FunctionTemplates during bindings initialization, they cause many V8
+// internal DCHECKs to trigger, as V8 codes around the invariant of no object
+// properties being replaced by templates.  These specific DCHECKs are
+// temporarily disabled in order to allow all other DCHECKs to be turned on.
+// Once DOM array buffers are replaced with script array buffers, these
+// DCHECKs should be re-enabled (by removing this macro and all references to
+// it).
+//
+// tl;dr V8 doesn't like Cobalt's array buffers, and fights back with DCHECKs.
+#if defined(COBALT)
+#define COBALT_ARRAY_BUFFER_COLLISION_WORKAROUND 1
+#endif
+
 #endif  // V8CONFIG_H_
diff --git a/src/v8/src/base/platform/platform-starboard.cc b/src/v8/src/base/platform/platform-starboard.cc
index 24634a0..564d85d 100644
--- a/src/v8/src/base/platform/platform-starboard.cc
+++ b/src/v8/src/base/platform/platform-starboard.cc
@@ -355,7 +355,6 @@
 }
 
 Thread::~Thread() {
-  SB_NOTIMPLEMENTED();
   delete data_;
 }
 
diff --git a/src/v8/src/flag-definitions.h b/src/v8/src/flag-definitions.h
index f38ea77..dbf8a63 100644
--- a/src/v8/src/flag-definitions.h
+++ b/src/v8/src/flag-definitions.h
@@ -612,7 +612,11 @@
 DEFINE_INT(random_gc_interval, 0,
            "Collect garbage after random(0, X) allocations. It overrides "
            "gc_interval.")
+#if defined(COBALT_GC_ZEAL)
+DEFINE_INT(gc_interval, 1200, "garbage collect after <n> allocations")
+#else
 DEFINE_INT(gc_interval, -1, "garbage collect after <n> allocations")
+#endif
 DEFINE_INT(retain_maps_for_n_gc, 2,
            "keeps maps alive for <n> old space garbage collections")
 DEFINE_BOOL(trace_gc, false,
diff --git a/src/v8/src/inspector/inspector.gyp b/src/v8/src/inspector/inspector.gyp
index 3d59cc0..94bac0d 100644
--- a/src/v8/src/inspector/inspector.gyp
+++ b/src/v8/src/inspector/inspector.gyp
@@ -76,7 +76,8 @@
           'action': [
             'python',
             '<(protocol_path)/CodeGenerator.py',
-            '--jinja_dir', '../../third_party',
+            # Redirect V8 to use Cobalt's jinja.
+            '--jinja_dir', '../../../third_party',
             '--output_base', '<(SHARED_INTERMEDIATE_DIR)/src/inspector',
             '--config', 'inspector_protocol_config.json',
           ],
diff --git a/src/v8/src/lookup.cc b/src/v8/src/lookup.cc
index 71902df..bb35938 100644
--- a/src/v8/src/lookup.cc
+++ b/src/v8/src/lookup.cc
@@ -440,7 +440,9 @@
          (GetAccessors()->IsAccessorInfo() &&
           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
+#if !defined(COBALT_ARRAY_BUFFER_COLLISION_WORKAROUND)
   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
+#endif
 
   Handle<Map> map(receiver->map(), isolate_);
 
@@ -455,7 +457,9 @@
           global, name(), PropertyCellType::kUninitialized, &entry);
       Handle<GlobalDictionary> dictionary(global->global_dictionary(),
                                           isolate_);
+#if !defined(COBALT_ARRAY_BUFFER_COLLISION_WORKAROUND)
       DCHECK(cell->value()->IsTheHole(isolate_));
+#endif
       DCHECK(!value->IsTheHole(isolate_));
       transition_ = cell;
       // Assign an enumeration index to the property and update
diff --git a/src/v8/src/objects.cc b/src/v8/src/objects.cc
index f8c55e5..e650e0f 100644
--- a/src/v8/src/objects.cc
+++ b/src/v8/src/objects.cc
@@ -6004,7 +6004,9 @@
   DCHECK(!name->AsArrayIndex(&index));
   Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it);
   DCHECK(maybe.IsJust());
+#if !defined(COBALT_ARRAY_BUFFER_COLLISION_WORKAROUND)
   DCHECK(!it.IsFound());
+#endif
   DCHECK(object->map()->is_extensible() || name->IsPrivate());
 #endif
   CHECK(AddDataProperty(&it, value, attributes, kThrowOnError,
@@ -16775,7 +16777,9 @@
     PropertyCellType original_cell_type = cell->property_details().cell_type();
     DCHECK(original_cell_type == PropertyCellType::kInvalidated ||
            original_cell_type == PropertyCellType::kUninitialized);
+#if !defined(COBALT_ARRAY_BUFFER_COLLISION_WORKAROUND)
     DCHECK(cell->value()->IsTheHole(isolate));
+#endif
     if (original_cell_type == PropertyCellType::kInvalidated) {
       cell = PropertyCell::InvalidateEntry(dictionary, entry);
     }
diff --git a/src/v8/src/v8.gyp b/src/v8/src/v8.gyp
index 9f11840..ab75a5e 100644
--- a/src/v8/src/v8.gyp
+++ b/src/v8/src/v8.gyp
@@ -47,6 +47,20 @@
     'defines': [
       'V8_OS_STARBOARD=1',
     ],
+    'conditions': [
+      ['cobalt_config == "debug"', {
+        'defines': [
+          'DEBUG=1',
+          'OBJECT_PRINT=1',
+          'V8_ENABLE_ALLOCATION_TIMEOUT=1',
+        ],
+      }],
+      ['cobalt_gc_zeal == 1', {
+        'defines': [
+          'COBALT_GC_ZEAL=1',
+        ],
+      }],
+    ],
    },
   'includes': ['../gypfiles/toolchain.gypi', '../gypfiles/features.gypi', 'inspector/inspector.gypi'],
   'targets': [
@@ -1977,7 +1991,6 @@
             ],
           },
         }],
-        # TODO: Remove after starboardization.
         ['OS=="linux"', {
             'link_settings': {
               'libraries': [