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