Import Cobalt 20.master.0.234144

Includes the following patches:
  https://cobalt-review.googlesource.com/c/cobalt/+/5590
  by n1214.hwang@samsung.com

  https://cobalt-review.googlesource.com/c/cobalt/+/5530
  by errong.leng@samsung.com

  https://cobalt-review.googlesource.com/c/cobalt/+/5570
  by devin.cai@mediatek.com
diff --git a/src/base/base.gyp b/src/base/base.gyp
index 9cbd82f..5eccf13 100644
--- a/src/base/base.gyp
+++ b/src/base/base.gyp
@@ -142,6 +142,8 @@
         'files/file_util.cc',
         'files/file_util.h',
         'files/file_util_starboard.cc',
+        'files/important_file_writer.cc',
+        'files/important_file_writer.h',
         'files/platform_file.h',
         'files/scoped_file.cc',
         'files/scoped_file.h',
@@ -773,6 +775,7 @@
         'files/file_proxy_unittest.cc',
         'files/file_unittest.cc',
         'files/file_util_unittest.cc',
+        'files/important_file_writer_unittest.cc',
         'files/scoped_temp_dir_unittest.cc',
         'gmock_unittest.cc',
         'guid_unittest.cc',
diff --git a/src/base/cpp14oncpp11.h b/src/base/cpp14oncpp11.h
index bc791ea..ae23cd3 100644
--- a/src/base/cpp14oncpp11.h
+++ b/src/base/cpp14oncpp11.h
@@ -162,45 +162,55 @@
 template<typename T>
 using add_volatile_t = typename add_volatile<T>::type;
 
-template< class C > 
+template< class C >
 auto rbegin( C& c ) -> decltype(c.rbegin()) {
   return c.rbegin();
 }
 
-template< class C > 
+template< class C >
 auto rbegin( const C& c ) -> decltype(c.rbegin()) {
   return c.rbegin();
 }
 
-template< class T, size_t N > 
+template< class T, size_t N >
 reverse_iterator<T*> rbegin( T (&array)[N] ) {
   return reverse_iterator<T*>(array + N);
 }
 
-template< class C > 
+template <class C>
+constexpr auto cbegin(const C& c) -> decltype(std::begin(c)) {
+  return std::begin(c);
+}
+
+template< class C >
 auto crbegin( const C& c ) -> decltype(std::rbegin(c)) {
   return std::rbegin(c);
 }
 
-template< class C > 
+template< class C >
 auto rend( C& c ) -> decltype(c.rend()) {
   return c.rend();
 }
 
-template< class C > 
+template< class C >
 auto rend( const C& c ) -> decltype(c.rend()) {
   return c.rend();
 }
 
-template< class T, size_t N > 
+template< class T, size_t N >
 reverse_iterator<T*> rend( T (&array)[N] ) {
   return reverse_iterator<T*>(array);
 }
 
-template< class C > 
+template< class C >
 auto crend( const C& c ) -> decltype(std::rend(c)) {
   return std::rend(c);
 }
+
+template <class C>
+constexpr auto cend(const C& c) -> decltype(std::end(c)) {
+  return std::end(c);
+}
 #endif
 
 }  // namespace std
diff --git a/src/base/files/important_file_writer.cc b/src/base/files/important_file_writer.cc
index c5a120f..968c753 100644
--- a/src/base/files/important_file_writer.cc
+++ b/src/base/files/important_file_writer.cc
@@ -27,6 +27,7 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "starboard/file.h"
 #include "starboard/types.h"
 
 namespace base {
@@ -130,6 +131,22 @@
 
 }  // namespace
 
+#if defined(OS_STARBOARD)
+// static
+bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
+                                              StringPiece data,
+                                              StringPiece histogram_suffix) {
+  SB_UNREFERENCED_PARAMETER(histogram_suffix);
+#if SB_API_VERSION >= SB_FILE_ATOMIC_REPLACE_VERSION
+  return SbFileAtomicReplace(path.value().c_str(), data.data(), data.size());
+#else
+  SB_NOTREACHED()
+      << "SbFileAtomicReplace is not available before starboard version "
+      << SB_FILE_ATOMIC_REPLACE_VERSION;
+  return false;
+#endif
+}
+#else
 // static
 bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
                                               StringPiece data,
@@ -210,6 +227,7 @@
 
   return true;
 }
+#endif
 
 ImportantFileWriter::ImportantFileWriter(
     const FilePath& path,
diff --git a/src/base/files/important_file_writer.h b/src/base/files/important_file_writer.h
index 386811a..f0cbfd2 100644
--- a/src/base/files/important_file_writer.h
+++ b/src/base/files/important_file_writer.h
@@ -17,8 +17,6 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 
-#if !defined(STARBOARD)
-
 namespace base {
 
 class SequencedTaskRunner;
@@ -160,6 +158,4 @@
 
 }  // namespace base
 
-#endif  // #if !defined(STARBOARD)
-
 #endif  // BASE_FILES_IMPORTANT_FILE_WRITER_H_
diff --git a/src/base/files/important_file_writer_unittest.cc b/src/base/files/important_file_writer_unittest.cc
index 5dddc71..680a2f8 100644
--- a/src/base/files/important_file_writer_unittest.cc
+++ b/src/base/files/important_file_writer_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/timer/mock_timer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if SB_API_VERSION >= SB_FILE_ATOMIC_REPLACE_VERSION
 namespace base {
 
 namespace {
@@ -179,6 +180,9 @@
   EXPECT_EQ("baz", GetFileContent(writer.path()));
 }
 
+// Disable the test as win32 SbFileOpen doesn't fail on relative path
+// like bad/../path.tmp
+#if !defined(OS_STARBOARD)
 TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) {
   // Use an invalid file path (relative paths are invalid) to get a
   // FILE_ERROR_ACCESS_DENIED error when trying to write the file.
@@ -196,6 +200,7 @@
             write_callback_observer_.GetAndResetObservationState());
   EXPECT_FALSE(PathExists(writer.path()));
 }
+#endif
 
 TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) {
   base::Thread file_writer_thread("ImportantFileWriter test thread");
@@ -327,6 +332,7 @@
   EXPECT_FALSE(PathExists(writer.path()));
 }
 
+#if !defined(OS_STARBOARD)
 TEST_F(ImportantFileWriterTest, WriteFileAtomicallyHistogramSuffixTest) {
   base::HistogramTester histogram_tester;
   EXPECT_FALSE(PathExists(file_));
@@ -347,5 +353,8 @@
   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError", 1);
   histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError.test", 1);
 }
+#endif
 
 }  // namespace base
+
+#endif  // SB_API_VERSION >= SB_FILE_ATOMIC_REPLACE_VERSION
diff --git a/src/base/time/time.cc b/src/base/time/time.cc
index 90e22cc..b975053 100644
--- a/src/base/time/time.cc
+++ b/src/base/time/time.cc
@@ -30,7 +30,8 @@
 TimeTicksNowFunction g_time_ticks_now_function =
     &subtle::TimeTicksNowIgnoringOverride;
 
-#if SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION || \
+    SB_HAS(TIME_THREAD_NOW)
 ThreadTicksNowFunction g_thread_ticks_now_function =
     &subtle::ThreadTicksNowIgnoringOverride;
 #endif
@@ -323,11 +324,14 @@
 
 // static
 ThreadTicks ThreadTicks::Now() {
-#if SB_HAS(TIME_THREAD_NOW)
-  return internal::g_thread_ticks_now_function();
-#else
-  return ThreadTicks();
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION || \
+    SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION
+  if (SbTimeIsTimeThreadNowSupported())
 #endif
+    return internal::g_thread_ticks_now_function();
+#endif
+  return ThreadTicks();
 }
 
 std::ostream& operator<<(std::ostream& os, ThreadTicks thread_ticks) {
diff --git a/src/base/time/time.h b/src/base/time/time.h
index fba813a..8ee1b6d 100644
--- a/src/base/time/time.h
+++ b/src/base/time/time.h
@@ -997,7 +997,9 @@
   // Returns true if ThreadTicks::Now() is supported on this system.
   static bool IsSupported() WARN_UNUSED_RESULT {
 #if defined(STARBOARD)
-#if SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION
+    return SbTimeIsTimeThreadNowSupported();
+#elif SB_HAS(TIME_THREAD_NOW)
     return true;
 #else
     return false;
diff --git a/src/base/time/time_now_starboard.cc b/src/base/time/time_now_starboard.cc
index f876e9c..e80ffaf 100644
--- a/src/base/time/time_now_starboard.cc
+++ b/src/base/time/time_now_starboard.cc
@@ -62,12 +62,15 @@
 
 namespace subtle {
 ThreadTicks ThreadTicksNowIgnoringOverride() {
-#if SB_HAS(TIME_THREAD_NOW)
-  return ThreadTicks() +
-         TimeDelta::FromMicroseconds(SbTimeGetMonotonicThreadNow());
-#else
-  return ThreadTicks();
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION || \
+    SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION
+  if (SbTimeIsTimeThreadNowSupported())
 #endif
+    return ThreadTicks() +
+           TimeDelta::FromMicroseconds(SbTimeGetMonotonicThreadNow());
+#endif
+  return ThreadTicks();
 }
 }  // namespace subtle
 
diff --git a/src/base/time/time_override.cc b/src/base/time/time_override.cc
index 6232bfe..63f4d8e 100644
--- a/src/base/time/time_override.cc
+++ b/src/base/time/time_override.cc
@@ -26,7 +26,8 @@
   }
   if (time_ticks_override)
     internal::g_time_ticks_now_function = time_ticks_override;
-#if SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION || \
+    SB_HAS(TIME_THREAD_NOW)
   if (thread_ticks_override)
     internal::g_thread_ticks_now_function = thread_ticks_override;
 #endif
@@ -37,7 +38,8 @@
   internal::g_time_now_from_system_time_function =
       &TimeNowFromSystemTimeIgnoringOverride;
   internal::g_time_ticks_now_function = &TimeTicksNowIgnoringOverride;
-#if SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= SB_TIME_THREAD_NOW_REQUIRED_VERSION || \
+    SB_HAS(TIME_THREAD_NOW)
   internal::g_thread_ticks_now_function = &ThreadTicksNowIgnoringOverride;
 #endif
 #if DCHECK_IS_ON()
diff --git a/src/base/trace_event/memory_usage_estimator.h b/src/base/trace_event/memory_usage_estimator.h
index 53ce8f7..24f144f 100644
--- a/src/base/trace_event/memory_usage_estimator.h
+++ b/src/base/trace_event/memory_usage_estimator.h
@@ -211,10 +211,12 @@
 struct EMUCaller {
   // std::is_same<> below makes static_assert depend on T, in order to
   // prevent it from asserting regardless instantiation.
+#if !defined(_GLIBCXX_DEBUG) && !defined(_LIBCPP_DEBUG)
   static_assert(std::is_same<T, std::false_type>::value,
                 "Neither global function 'size_t EstimateMemoryUsage(T)' "
                 "nor member function 'size_t T::EstimateMemoryUsage() const' "
                 "is defined for the type.");
+#endif
 
   static size_t Call(const T&) { return 0; }
 };
diff --git a/src/base/util/values/values_util.cc b/src/base/util/values/values_util.cc
new file mode 100644
index 0000000..43b317b
--- /dev/null
+++ b/src/base/util/values/values_util.cc
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/util/values/values_util.h"
+
+#include "base/strings/string_number_conversions.h"
+
+namespace util {
+
+base::Value Int64ToValue(int64_t integer) {
+  return base::Value(base::NumberToString(integer));
+}
+
+base::Optional<int64_t> ValueToInt64(const base::Value* value) {
+  return value ? ValueToInt64(*value) : base::nullopt;
+}
+
+base::Optional<int64_t> ValueToInt64(const base::Value& value) {
+  if (!value.is_string())
+    return base::nullopt;
+
+  int64_t integer;
+  if (!base::StringToInt64(value.GetString(), &integer))
+    return base::nullopt;
+
+  return integer;
+}
+
+base::Value TimeDeltaToValue(base::TimeDelta time_delta) {
+  return Int64ToValue(time_delta.InMicroseconds());
+}
+
+base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value* value) {
+  return value ? ValueToTimeDelta(*value) : base::nullopt;
+}
+
+base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value& value) {
+  base::Optional<int64_t> integer = ValueToInt64(value);
+  if (!integer)
+    return base::nullopt;
+  return base::TimeDelta::FromMicroseconds(*integer);
+}
+
+base::Value TimeToValue(base::Time time) {
+  return TimeDeltaToValue(time.ToDeltaSinceWindowsEpoch());
+}
+
+base::Optional<base::Time> ValueToTime(const base::Value* value) {
+  return value ? ValueToTime(*value) : base::nullopt;
+}
+
+base::Optional<base::Time> ValueToTime(const base::Value& value) {
+  base::Optional<base::TimeDelta> time_delta = ValueToTimeDelta(value);
+  if (!time_delta)
+    return base::nullopt;
+  return base::Time::FromDeltaSinceWindowsEpoch(*time_delta);
+}
+
+}  // namespace util
diff --git a/src/base/util/values/values_util.gyp b/src/base/util/values/values_util.gyp
new file mode 100644
index 0000000..0c537c0
--- /dev/null
+++ b/src/base/util/values/values_util.gyp
@@ -0,0 +1,29 @@
+# Copyright 2019 The Cobalt Authors. 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': 'values_util',
+      'type': 'static_library',
+      'sources': [
+        'values_util.cc',
+        'values_util.h',
+      ],
+      'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
+      ],
+    },
+  ]
+}
diff --git a/src/base/util/values/values_util.h b/src/base/util/values/values_util.h
new file mode 100644
index 0000000..de9fd1b
--- /dev/null
+++ b/src/base/util/values/values_util.h
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_UTIL_VALUES_VALUES_UTIL_H_
+#define BASE_UTIL_VALUES_VALUES_UTIL_H_
+
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "base/values.h"
+
+namespace util {
+
+// Simple helper functions for converting int64_t, base::TimeDelta and
+// base::Time to numeric string base::Values.
+// Because base::TimeDelta and base::Time share the same internal representation
+// as int64_t they are stored using the exact same numeric string format.
+
+// Stores the int64_t as a string.
+base::Value Int64ToValue(int64_t integer);
+base::Optional<int64_t> ValueToInt64(const base::Value* value);
+base::Optional<int64_t> ValueToInt64(const base::Value& value);
+
+// Converts the TimeDelta to an int64_t of microseconds.
+base::Value TimeDeltaToValue(base::TimeDelta time_delta);
+base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value* value);
+base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value& value);
+
+// Converts the Time to a TimeDelta from the Windows epoch.
+base::Value TimeToValue(base::Time time);
+base::Optional<base::Time> ValueToTime(const base::Value* value);
+base::Optional<base::Time> ValueToTime(const base::Value& value);
+
+}  // namespace util
+
+#endif  // BASE_UTIL_VALUES_VALUES_UTIL_H_
diff --git a/src/build/common.gypi b/src/build/common.gypi
index 7078e81..da3d978 100644
--- a/src/build/common.gypi
+++ b/src/build/common.gypi
@@ -331,6 +331,7 @@
           ['(OS == "win" or target_arch=="xb1") and component=="shared_library"', {
             'msvs_disabled_warnings': [
               4251,  # class 'std::xx' needs to have dll-interface.
+              4715,  # Not all control paths return a value.
             ],
           }],
         ],
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index 7f3a40e..06fe9c7 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -4,6 +4,13 @@
 
 ## Version 21
 
+ - **DevTools and WebDriver listen to ANY interface, except on Linux.**
+
+  DevTools and WebDriver servers listen to connections on any network interface
+  by default, except on Linux where they listen only to loopback (localhost) by
+  default. A new "--dev_servers_listen_ip" command line parameter can be used to
+  specify a different interface for both of them to listen to.
+
  - **DevTools shows asynchronous stack traces.**
 
   When stopped at a breakpoint within the handler function for an asynchronous
diff --git a/src/cobalt/audio/audio_buffer_source_node.cc b/src/cobalt/audio/audio_buffer_source_node.cc
index 8481b6b..654e6a8 100644
--- a/src/cobalt/audio/audio_buffer_source_node.cc
+++ b/src/cobalt/audio/audio_buffer_source_node.cc
@@ -31,8 +31,9 @@
 
 // numberOfInputs  : 0
 // numberOfOutputs : 1
-AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context)
-    : AudioNode(context),
+AudioBufferSourceNode::AudioBufferSourceNode(
+    script::EnvironmentSettings* settings, AudioContext* context)
+    : AudioNode(settings, context),
       task_runner_(base::MessageLoop::current()->task_runner()),
       state_(kNone),
       read_index_(0),
diff --git a/src/cobalt/audio/audio_buffer_source_node.h b/src/cobalt/audio/audio_buffer_source_node.h
index a3623e0..1731aef 100644
--- a/src/cobalt/audio/audio_buffer_source_node.h
+++ b/src/cobalt/audio/audio_buffer_source_node.h
@@ -24,6 +24,7 @@
 #include "cobalt/base/tokens.h"
 #include "cobalt/media/base/interleaved_sinc_resampler.h"
 #include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
 namespace audio {
@@ -37,7 +38,8 @@
   typedef media::ShellAudioBus ShellAudioBus;
 
  public:
-  explicit AudioBufferSourceNode(AudioContext* context);
+  AudioBufferSourceNode(script::EnvironmentSettings* settings,
+                        AudioContext* context);
 
   // Web API: AudioBufferSourceNode
   //
diff --git a/src/cobalt/audio/audio_context.cc b/src/cobalt/audio/audio_context.cc
index 7ba55a5..e909b72 100644
--- a/src/cobalt/audio/audio_context.cc
+++ b/src/cobalt/audio/audio_context.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/audio/audio_context.h"
 
+#include <memory>
+
 #include "base/callback.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_settings.h"
@@ -24,7 +24,8 @@
 namespace audio {
 
 AudioContext::AudioContext(script::EnvironmentSettings* settings)
-    : global_environment_(
+    : dom::EventTarget(settings),
+      global_environment_(
           base::polymorphic_downcast<dom::DOMSettings*>(settings)
               ->global_environment()),
       ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
@@ -36,7 +37,7 @@
       current_time_(0.0f),
       audio_lock_(new AudioLock()),
       ALLOW_THIS_IN_INITIALIZER_LIST(
-          destination_(new AudioDestinationNode(this))),
+          destination_(new AudioDestinationNode(settings, this))),
       next_callback_id_(0),
       main_message_loop_(base::MessageLoop::current()->task_runner()) {
   DCHECK(main_message_loop_);
@@ -68,10 +69,12 @@
                        kStorageTypeInterleaved))));
 }
 
-scoped_refptr<AudioBufferSourceNode> AudioContext::CreateBufferSource() {
+scoped_refptr<AudioBufferSourceNode> AudioContext::CreateBufferSource(
+    script::EnvironmentSettings* settings) {
   DCHECK(main_message_loop_->BelongsToCurrentThread());
 
-  return scoped_refptr<AudioBufferSourceNode>(new AudioBufferSourceNode(this));
+  return scoped_refptr<AudioBufferSourceNode>(
+      new AudioBufferSourceNode(settings, this));
 }
 
 void AudioContext::PreventGarbageCollection() {
diff --git a/src/cobalt/audio/audio_context.h b/src/cobalt/audio/audio_context.h
index 26f8994..a4667bf 100644
--- a/src/cobalt/audio/audio_context.h
+++ b/src/cobalt/audio/audio_context.h
@@ -83,7 +83,8 @@
   //
   // The AudioBuffer is representing the decoded PCM audio data.
   typedef script::CallbackFunction<void(
-      const scoped_refptr<AudioBuffer>& decoded_data)> DecodeSuccessCallback;
+      const scoped_refptr<AudioBuffer>& decoded_data)>
+      DecodeSuccessCallback;
   typedef script::ScriptValue<DecodeSuccessCallback> DecodeSuccessCallbackArg;
   typedef DecodeSuccessCallbackArg::Reference DecodeSuccessCallbackReference;
 
@@ -132,7 +133,8 @@
                        const DecodeErrorCallbackArg& error_handler);
 
   // Creates an AudioBufferSourceNode.
-  scoped_refptr<AudioBufferSourceNode> CreateBufferSource();
+  scoped_refptr<AudioBufferSourceNode> CreateBufferSource(
+      script::EnvironmentSettings* settings);
 
   // Creates a new, empty AudioBuffer object.
   scoped_refptr<AudioBuffer> CreateBuffer(uint32 num_of_channels, uint32 length,
diff --git a/src/cobalt/audio/audio_context.idl b/src/cobalt/audio/audio_context.idl
index eda8c5b..f62770a 100644
--- a/src/cobalt/audio/audio_context.idl
+++ b/src/cobalt/audio/audio_context.idl
@@ -28,7 +28,7 @@
       optional DecodeErrorCallback errorCallback);
 
   // AudioNode creation
-  AudioBufferSourceNode createBufferSource();
+  [CallWith=EnvironmentSettings] AudioBufferSourceNode createBufferSource();
 
   // AudioBuffer creation
   AudioBuffer createBuffer(unsigned long numOfChannels, unsigned long length,
diff --git a/src/cobalt/audio/audio_destination_node.cc b/src/cobalt/audio/audio_destination_node.cc
index b728fbe..4107070 100644
--- a/src/cobalt/audio/audio_destination_node.cc
+++ b/src/cobalt/audio/audio_destination_node.cc
@@ -32,8 +32,9 @@
 
 // numberOfInputs  : 1
 // numberOfOutputs : 0
-AudioDestinationNode::AudioDestinationNode(AudioContext* context)
-    : AudioNode(context),
+AudioDestinationNode::AudioDestinationNode(
+    script::EnvironmentSettings* settings, AudioContext* context)
+    : AudioNode(settings, context),
       message_loop_(base::MessageLoop::current()),
       max_channel_count_(kMaxChannelCount) {
   AudioLock::AutoLock lock(audio_lock());
diff --git a/src/cobalt/audio/audio_destination_node.h b/src/cobalt/audio/audio_destination_node.h
index 9713e44..fd090c8 100644
--- a/src/cobalt/audio/audio_destination_node.h
+++ b/src/cobalt/audio/audio_destination_node.h
@@ -23,6 +23,7 @@
 #include "cobalt/audio/audio_helpers.h"
 #include "cobalt/audio/audio_node.h"
 #include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
 namespace audio {
@@ -39,7 +40,8 @@
   typedef media::ShellAudioBus ShellAudioBus;
 
  public:
-  explicit AudioDestinationNode(AudioContext* context);
+  AudioDestinationNode(script::EnvironmentSettings* settings,
+                       AudioContext* context);
 
   // Web API: AudioDestinationNode
   //
diff --git a/src/cobalt/audio/audio_node.cc b/src/cobalt/audio/audio_node.cc
index 756f0ec..ed2cc42 100644
--- a/src/cobalt/audio/audio_node.cc
+++ b/src/cobalt/audio/audio_node.cc
@@ -20,8 +20,10 @@
 namespace cobalt {
 namespace audio {
 
-AudioNode::AudioNode(AudioContext* context)
-    : audio_context_(context),
+AudioNode::AudioNode(script::EnvironmentSettings* settings,
+                     AudioContext* context)
+    : EventTarget(settings),
+      audio_context_(context),
       audio_lock_(context->audio_lock()),
       channel_count_(2),
       channel_count_mode_(kAudioNodeChannelCountModeMax),
diff --git a/src/cobalt/audio/audio_node.h b/src/cobalt/audio/audio_node.h
index 4374112..38d67cf 100644
--- a/src/cobalt/audio/audio_node.h
+++ b/src/cobalt/audio/audio_node.h
@@ -28,6 +28,7 @@
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/event_target.h"
 #include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
 namespace audio {
@@ -51,7 +52,7 @@
   typedef media::ShellAudioBus ShellAudioBus;
 
  public:
-  explicit AudioNode(AudioContext* context);
+  AudioNode(script::EnvironmentSettings* settings, AudioContext* context);
 
   // Web API: AudioNode
   //
diff --git a/src/cobalt/audio/audio_node_input_output_test.cc b/src/cobalt/audio/audio_node_input_output_test.cc
index 19cec61..c4bf0bb 100644
--- a/src/cobalt/audio/audio_node_input_output_test.cc
+++ b/src/cobalt/audio/audio_node_input_output_test.cc
@@ -13,12 +13,14 @@
 // limitations under the License.
 
 #include <math.h>
+
 #include <memory>
 
 #include "cobalt/audio/audio_buffer_source_node.h"
 #include "cobalt/audio/audio_context.h"
 #include "cobalt/audio/audio_helpers.h"
 #include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
@@ -39,8 +41,9 @@
   typedef media::ShellAudioBus ShellAudioBus;
 
  public:
-  explicit AudioDestinationNodeMock(AudioContext* context)
-      : AudioNode(context) {
+  AudioDestinationNodeMock(script::EnvironmentSettings* settings,
+                           AudioContext* context)
+      : AudioNode(settings, context) {
     AudioLock::AutoLock lock(audio_lock());
 
     AddInput(new AudioNodeInput(this));
@@ -71,20 +74,18 @@
     std::unique_ptr<ShellAudioBus> src_data,
     const AudioNodeChannelInterpretation& interpretation,
     ShellAudioBus* audio_bus, bool* silence) {
-  std::unique_ptr<script::EnvironmentSettings> environment_settings_ =
-      std::unique_ptr<script::EnvironmentSettings>(new dom::DOMSettings(
-          0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
+  dom::testing::StubEnvironmentSettings environment_settings;
 
   scoped_refptr<AudioContext> audio_context(
-      new AudioContext(environment_settings_.get()));
+      new AudioContext(&environment_settings));
   scoped_refptr<AudioBufferSourceNode> source(
-      audio_context->CreateBufferSource());
+      audio_context->CreateBufferSource(&environment_settings));
   scoped_refptr<AudioBuffer> buffer(
       new AudioBuffer(audio_context->sample_rate(), std::move(src_data)));
   source->set_buffer(buffer);
 
   scoped_refptr<AudioDestinationNodeMock> destination(
-      new AudioDestinationNodeMock(audio_context.get()));
+      new AudioDestinationNodeMock(&environment_settings, audio_context.get()));
   destination->set_channel_interpretation(interpretation);
   source->Connect(destination, 0, 0, NULL);
   source->Start(0, 0, NULL);
@@ -97,9 +98,8 @@
   AudioNodeInputOutputTest()
       : engine_(script::JavaScriptEngine::CreateEngine()),
         global_environment_(engine_->CreateGlobalEnvironment()) {
-    environment_settings_ =
-        std::unique_ptr<script::EnvironmentSettings>(new dom::DOMSettings(
-            0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
+    environment_settings_ = std::unique_ptr<script::EnvironmentSettings>(
+        new dom::testing::StubEnvironmentSettings);
     global_environment_->CreateGlobalObject();
   }
 
@@ -118,13 +118,13 @@
     return environment_settings_.get();
   }
 
+ protected:
+  base::MessageLoop message_loop_;
+
  private:
   std::unique_ptr<script::JavaScriptEngine> engine_;
   scoped_refptr<script::GlobalEnvironment> global_environment_;
   std::unique_ptr<script::EnvironmentSettings> environment_settings_;
-
- protected:
-  base::MessageLoop message_loop_;
 };
 
 TEST_F(AudioNodeInputOutputTest, StereoToStereoSpeakersLayoutTest) {
@@ -697,7 +697,7 @@
   std::unique_ptr<ShellAudioBus> src_data_1(new ShellAudioBus(
       kNumOfSrcChannels, kNumOfFrames_1, src_data_in_float_1));
   scoped_refptr<AudioBufferSourceNode> source_1(
-      audio_context->CreateBufferSource());
+      audio_context->CreateBufferSource(environment_settings()));
   scoped_refptr<AudioBuffer> buffer_1(
       new AudioBuffer(audio_context->sample_rate(), std::move(src_data_1)));
 
@@ -715,13 +715,14 @@
   std::unique_ptr<ShellAudioBus> src_data_2(new ShellAudioBus(
       kNumOfSrcChannels, kNumOfFrames_2, src_data_in_float_2));
   scoped_refptr<AudioBufferSourceNode> source_2(
-      audio_context->CreateBufferSource());
+      audio_context->CreateBufferSource(environment_settings()));
   scoped_refptr<AudioBuffer> buffer_2(
       new AudioBuffer(audio_context->sample_rate(), std::move(src_data_2)));
   source_2->set_buffer(buffer_2);
 
   scoped_refptr<AudioDestinationNodeMock> destination(
-      new AudioDestinationNodeMock(audio_context.get()));
+      new AudioDestinationNodeMock(environment_settings(),
+                                   audio_context.get()));
   destination->set_channel_interpretation(kInterpretation);
   source_1->Connect(destination, 0, 0, NULL);
   source_2->Connect(destination, 0, 0, NULL);
@@ -980,11 +981,12 @@
       scoped_refptr<AudioContext> audio_context(
           new AudioContext(environment_settings()));
       scoped_refptr<AudioBufferSourceNode> source(
-          audio_context->CreateBufferSource());
+          audio_context->CreateBufferSource(environment_settings()));
       source->set_buffer(buffer);
 
       scoped_refptr<AudioDestinationNodeMock> destination(
-          new AudioDestinationNodeMock(audio_context.get()));
+          new AudioDestinationNodeMock(environment_settings(),
+                                       audio_context.get()));
       destination->set_channel_interpretation(kInterpretation);
       source->Connect(destination, 0, 0, NULL);
       source->Start(0, 0, NULL);
diff --git a/src/cobalt/base/circular_buffer_shell_unittest.cc b/src/cobalt/base/circular_buffer_shell_unittest.cc
index ea1c0cf..9c90eaf 100644
--- a/src/cobalt/base/circular_buffer_shell_unittest.cc
+++ b/src/cobalt/base/circular_buffer_shell_unittest.cc
@@ -251,7 +251,7 @@
 
     EXPECT_EQ(10, bytes_peeked);
     IsSame(UNSET_DATA, destination, 9);
-    IsSame(UNSET_DATA + 9 + bytes_peeked, destination + 9 + bytes_peeked,
+    IsSame(&UNSET_DATA[9] + bytes_peeked, destination + 9 + bytes_peeked,
            sizeof(UNSET_DATA) - 9 - bytes_peeked);
     IsSame(kTestData, destination + 9, 10);
     peek_offset += bytes_peeked;
@@ -264,7 +264,7 @@
 
     EXPECT_EQ(7, bytes_peeked);
     IsSame(UNSET_DATA, destination, 9);
-    IsSame(UNSET_DATA + 9 + bytes_peeked, destination + 9 + bytes_peeked,
+    IsSame(&UNSET_DATA[9] + bytes_peeked, destination + 9 + bytes_peeked,
            sizeof(UNSET_DATA) - 9 - bytes_peeked);
     IsSame(kTestData + peek_offset, destination + 9, bytes_peeked);
     peek_offset += bytes_peeked;
@@ -277,7 +277,7 @@
 
     EXPECT_EQ(3, bytes_peeked);
     IsSame(UNSET_DATA, destination, 9);
-    IsSame(UNSET_DATA + 9 + bytes_peeked, destination + 9 + bytes_peeked,
+    IsSame(&UNSET_DATA[9] + bytes_peeked, destination + 9 + bytes_peeked,
            sizeof(UNSET_DATA) - 9 - bytes_peeked);
     IsSame(kTestData + peek_offset, destination + 9, bytes_peeked);
     peek_offset += bytes_peeked;
diff --git a/src/cobalt/base/debugger_hooks.h b/src/cobalt/base/debugger_hooks.h
index b0ac836..f555dd1 100644
--- a/src/cobalt/base/debugger_hooks.h
+++ b/src/cobalt/base/debugger_hooks.h
@@ -23,6 +23,13 @@
 // directly access the DebugModule.
 class DebuggerHooks {
  public:
+  // Indicates whether an asynchronous task will run at most once or if it might
+  // run multiple times.
+  enum class AsyncTaskFrequency {
+    kOneshot,
+    kRecurring,
+  };
+
   // Record the JavaScript stack on the WebModule thread at the point a task is
   // initiated that will run at a later time (on the same thread), allowing it
   // to be seen as the originator when breaking in the asynchronous task.
@@ -34,43 +41,45 @@
   // |name| is a user-visible label shown in the debugger to identify what the
   // asynchronous stack trace is.
   //
-  // |recurring| is true if the task may be run more than once.
-  virtual void AsyncTaskScheduled(void* task, const std::string& name,
-                                  bool recurring = false) const = 0;
+  // |frequency| whether the task runs at most once or might run multiple times.
+  // If kOneshot then the task will be implicitly canceled after it is finished,
+  // and if kRecurring then it must be explicitly canceled.
+  virtual void AsyncTaskScheduled(const void* task, const std::string& name,
+                                  AsyncTaskFrequency frequency) const = 0;
 
   // Inform the debugger that a scheduled task is starting to run.
-  virtual void AsyncTaskStarted(void* task) const = 0;
+  virtual void AsyncTaskStarted(const void* task) const = 0;
 
   // Inform the debugger that a scheduled task has finished running.
-  virtual void AsyncTaskFinished(void* task) const = 0;
+  virtual void AsyncTaskFinished(const void* task) const = 0;
 
   // Inform the debugger that a scheduled task will no longer be run, and that
   // it may free any resources associated with it.
-  virtual void AsyncTaskCanceled(void* task) const = 0;
+  virtual void AsyncTaskCanceled(const void* task) const = 0;
 };
 
 // Helper to start & finish async tasks using RAII.
 class ScopedAsyncTask {
  public:
-  ScopedAsyncTask(const DebuggerHooks& debugger_hooks, void* task)
+  ScopedAsyncTask(DebuggerHooks* debugger_hooks, const void* task)
       : debugger_hooks_(debugger_hooks), task_(task) {
-    debugger_hooks_.AsyncTaskStarted(task_);
+    debugger_hooks_->AsyncTaskStarted(task_);
   }
-  ~ScopedAsyncTask() { debugger_hooks_.AsyncTaskFinished(task_); }
+  ~ScopedAsyncTask() { debugger_hooks_->AsyncTaskFinished(task_); }
 
  private:
-  const DebuggerHooks& debugger_hooks_;
-  void* const task_;
+  DebuggerHooks* debugger_hooks_;
+  const void* const task_;
 };
 
 // Null implementation for gold builds and tests where there is no debugger.
 class NullDebuggerHooks : public DebuggerHooks {
  public:
-  void AsyncTaskScheduled(void* task, const std::string& name,
-                          bool recurring) const override {}
-  void AsyncTaskStarted(void* task) const override {}
-  void AsyncTaskFinished(void* task) const override {}
-  void AsyncTaskCanceled(void* task) const override {}
+  void AsyncTaskScheduled(const void* task, const std::string& name,
+                          AsyncTaskFrequency frequency) const override {}
+  void AsyncTaskStarted(const void* task) const override {}
+  void AsyncTaskFinished(const void* task) const override {}
+  void AsyncTaskCanceled(const void* task) const override {}
 };
 
 }  // namespace base
diff --git a/src/cobalt/base/instance_counter.h b/src/cobalt/base/instance_counter.h
new file mode 100644
index 0000000..ccf343e
--- /dev/null
+++ b/src/cobalt/base/instance_counter.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Cobalt Authors. 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_BASE_INSTANCE_COUNTER_H_
+#define COBALT_BASE_INSTANCE_COUNTER_H_
+
+#include "base/logging.h"
+
+#if defined(COBALT_BUILD_TYPE_GOLD)
+
+#define DECLARE_INSTANCE_COUNTER(class_name)
+#define ON_INSTANCE_CREATED(class_name)
+#define ON_INSTANCE_RELEASED(class_name)
+
+#else  // defined(COBALT_BUILD_TYPE_GOLD)
+
+#define DECLARE_INSTANCE_COUNTER(class_name)      \
+  namespace {                                     \
+  SbAtomic32 s_##class_name##_instance_count = 0; \
+  }
+
+#define ON_INSTANCE_CREATED(class_name)                                       \
+  {                                                                           \
+    LOG(INFO) << "New instance of " << #class_name << " is created. We have " \
+              << (SbAtomicNoBarrier_Increment(                                \
+                     &s_##class_name##_instance_count, 1))                    \
+              << " instances in total.";                                      \
+  }
+
+#define ON_INSTANCE_RELEASED(class_name)                                   \
+  {                                                                        \
+    LOG(INFO) << "Instance of " << #class_name << " is released. We have " \
+              << (SbAtomicNoBarrier_Increment(                             \
+                     &s_##class_name##_instance_count, -1))                \
+              << " instances in total.";                                   \
+  }
+#endif  // defined(COBALT_BUILD_TYPE_GOLD)
+
+#endif  // COBALT_BASE_INSTANCE_COUNTER_H_
diff --git a/src/cobalt/base/wrap_main_starboard.h b/src/cobalt/base/wrap_main_starboard.h
index 7ff43f1..49e6c0d 100644
--- a/src/cobalt/base/wrap_main_starboard.h
+++ b/src/cobalt/base/wrap_main_starboard.h
@@ -115,7 +115,8 @@
 #if SB_API_VERSION >= 8
     case kSbEventTypeWindowSizeChanged:
 #endif  // SB_API_VERSION >= 8
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
     case kSbEventTypeOnScreenKeyboardShown:
     case kSbEventTypeOnScreenKeyboardHidden:
     case kSbEventTypeOnScreenKeyboardFocused:
@@ -123,10 +124,11 @@
 #if SB_API_VERSION >= 11
     case kSbEventTypeOnScreenKeyboardSuggestionsUpdated:
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
-#if SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
     case kSbEventTypeAccessibilityCaptionSettingsChanged:
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
       event_function(event);
       break;
   }
diff --git a/src/cobalt/bindings/shared/idl_conditional_macros.h b/src/cobalt/bindings/shared/idl_conditional_macros.h
index c4ee247..a20612b 100644
--- a/src/cobalt/bindings/shared/idl_conditional_macros.h
+++ b/src/cobalt/bindings/shared/idl_conditional_macros.h
@@ -23,16 +23,18 @@
 // Conditionals that are dependent on Starboard feature macros that get defined
 // in header files.
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 // This is used to conditionally define the On Screen Keyboard interface and
 // attribute.
 #define COBALT_ENABLE_ON_SCREEN_KEYBOARD
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 #if SB_API_VERSION >= 11
 // This is used to conditionally define setMaxVideoCapabilities() in
 // HTMLVideoElement.
 #define COBALT_ENABLE_SET_MAX_VIDEO_CAPABILITIES
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= 11
 
 #endif  // COBALT_BINDINGS_SHARED_IDL_CONDITIONAL_MACROS_H_
diff --git a/src/cobalt/bindings/testing/bindings_test_base.h b/src/cobalt/bindings/testing/bindings_test_base.h
index eedc6ba..f8a0ba8 100644
--- a/src/cobalt/bindings/testing/bindings_test_base.h
+++ b/src/cobalt/bindings/testing/bindings_test_base.h
@@ -19,6 +19,7 @@
 #include <string>
 
 #include "base/memory/ref_counted.h"
+#include "base/test/scoped_task_environment.h"
 #include "cobalt/bindings/testing/window.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/global_environment.h"
@@ -87,6 +88,7 @@
   Window* window() { return window_.get(); }
 
  protected:
+  base::test::ScopedTaskEnvironment task_env_;
   const std::unique_ptr<script::EnvironmentSettings> environment_settings_;
   const std::unique_ptr<script::JavaScriptEngine> engine_;
   const scoped_refptr<script::GlobalEnvironment> global_environment_;
diff --git a/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template
index 037df6f..5c5741f 100644
--- a/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template
+++ b/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template
@@ -70,7 +70,8 @@
   // JSValue -> IDL enum algorithm described here:
   // http://heycam.github.io/webidl/#es-enumeration
   // 1. Let S be the result of calling ToString(V).
-  v8::MaybeLocal<v8::String> maybe_string = value->ToString(isolate->GetCurrentContext());
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  v8::MaybeLocal<v8::String> maybe_string = value->ToString(context);
   v8::Local<v8::String> string;
   if (!maybe_string.ToLocal(&string)) {
     exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
@@ -81,7 +82,7 @@
 // 3. Return the enumeration value of type E that is equal to S.
 {% for value, idl_value in value_pairs %}
   {{-" else " if not loop.first}} if (
-      NewInternalString(isolate, "{{idl_value}}")->Equals(value))
+      NewInternalString(isolate, "{{idl_value}}")->Equals(context, value).ToChecked())
   {
     *out_enum = {{namespace}}::{{value}};
   }
diff --git a/src/cobalt/bindings/v8c/templates/interface.cc.template b/src/cobalt/bindings/v8c/templates/interface.cc.template
index 2b29c85..29db60a 100644
--- a/src/cobalt/bindings/v8c/templates/interface.cc.template
+++ b/src/cobalt/bindings/v8c/templates/interface.cc.template
@@ -129,7 +129,8 @@
 void IndexedPropertyGetterCallback(
     uint32_t index,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString();
+  v8::Local<v8::String> as_string = (v8::Integer::New(info.GetIsolate(), index)->ToString(
+                                     info.GetIsolate()->GetCurrentContext())).ToLocalChecked();
   NamedPropertyGetterCallback(as_string, info);
 }
 {% endif %}
@@ -218,7 +219,8 @@
     uint32_t index,
     v8::Local<v8::Value> value,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString();
+  v8::Local<v8::String> as_string = (v8::Integer::New(info.GetIsolate(), index)->ToString(
+                                     info.GetIsolate()->GetCurrentContext())).ToLocalChecked();
   NamedPropertySetterCallback(as_string, value, info);
 }
 {% endif %}
@@ -251,7 +253,8 @@
     uint32_t index,
     const v8::PropertyCallbackInfo<v8::Boolean>& info) {
   v8::Isolate* isolate = info.GetIsolate();
-  v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString();
+  v8::Local<v8::String> as_string = (v8::Integer::New(info.GetIsolate(), index)->ToString(
+                                     info.GetIsolate()->GetCurrentContext())).ToLocalChecked();
   NamedPropertyDeleterCallback(as_string, info);
 }
 {% endif %}
@@ -287,12 +290,13 @@
 void IndexedPropertyEnumeratorCallback(
     const v8::PropertyCallbackInfo<v8::Array>& info) {
   v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
   v8::Local<v8::Object> object = info.Holder();
 {{ get_impl_class_instance(impl_class) }}
   const uint32_t length = impl->length();
   v8::Local<v8::Array> array = v8::Array::New(isolate, length);
   for (uint32_t i = 0; i < length; ++i) {
-    array->Set(i, v8::Integer::New(isolate, i));
+    array->Set(context, i, v8::Integer::New(isolate, i)).Check();
   }
   info.GetReturnValue().Set(array);
 }
@@ -828,7 +832,8 @@
   // Intentionally not an |EntryScope|, since the context doesn't exist yet.
   v8::Isolate::Scope isolate_scope(isolate_);
   v8::HandleScope handle_scope(isolate_);
-  v8::Local<v8::ObjectTemplate> global_object_template = {{binding_class}}::GetTemplate(isolate_)->InstanceTemplate();
+  v8::Local<v8::ObjectTemplate> global_object_template =
+                {{binding_class}}::GetTemplate(isolate_)->InstanceTemplate();
 
   v8::Local<v8::Context> context =
       v8::Context::New(isolate_, nullptr, global_object_template);
@@ -845,7 +850,8 @@
   v8::Local<v8::Object> global_object = context->Global();
   new WrapperPrivate(isolate_, global_interface, global_object);
 
-  auto actual_global_object = global_object->GetPrototype()->ToObject();
+  auto actual_global_object = global_object->GetPrototype()->
+                                          ToObject(context).ToLocalChecked();
   new WrapperPrivate(isolate_, global_interface, actual_global_object);
 
 {% for interface in all_interfaces %}
@@ -869,7 +875,8 @@
 void GlobalEnvironment::CreateGlobalObject<{{impl_class}}>(
     const scoped_refptr<{{impl_class}}>& global_interface,
     EnvironmentSettings* environment_settings) {
-  base::polymorphic_downcast<v8c::V8cGlobalEnvironment*>(this)->CreateGlobalObject(global_interface, environment_settings);
+  base::polymorphic_downcast<v8c::V8cGlobalEnvironment*>(this)->
+                  CreateGlobalObject(global_interface, environment_settings);
 }
 
 }  // namespace script
diff --git a/src/cobalt/bindings/v8c/templates/macros.cc.template b/src/cobalt/bindings/v8c/templates/macros.cc.template
index 67b53cf..ce11720 100644
--- a/src/cobalt/bindings/v8c/templates/macros.cc.template
+++ b/src/cobalt/bindings/v8c/templates/macros.cc.template
@@ -386,6 +386,7 @@
 {% macro overload_resolution_implementation(
       overload_context, bound_function_prefix) %}
   v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   switch(info.Length()) {
 {% for length, distinguishing_argument_index, resolution_tests in
@@ -399,7 +400,7 @@
       WrapperFactory* wrapper_factory = V8cGlobalEnvironment::GetFromIsolate(isolate)->wrapper_factory();
       v8::Local<v8::Object> object;
       if (arg->IsObject()) {
-        object = arg->ToObject();
+        object = arg->ToObject(context).ToLocalChecked();
       }
 {% endif %}
 {% for test, overload in resolution_tests %}
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py
index 108972e..c209f91 100644
--- a/src/cobalt/black_box_tests/black_box_tests.py
+++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -60,14 +60,8 @@
 _device_params = None
 # Binding address used to create the test server.
 _binding_address = None
-
-
-def GetDeviceParams():
-
-  global _device_params
-  _device_params = cobalt_runner.GetDeviceParamsFromCommandLine()
-  # Keep other modules from seeing these args
-  sys.argv = sys.argv[:1]
+# Port used to create the web platform test http server.
+_wpt_http_port = None
 
 
 class BlackBoxTestCase(unittest.TestCase):
@@ -100,9 +94,11 @@
   def GetBindingAddress(self):
     return _binding_address
 
+  def GetWptHttpPort(self):
+    return _wpt_http_port
+
 
 def LoadTests(platform, config, device_id, out_directory):
-
   launcher = abstract_launcher.LauncherFactory(
       platform,
       'cobalt',
@@ -127,19 +123,38 @@
 class BlackBoxTests(object):
   """Helper class to run all black box tests and return results."""
 
-  def __init__(self, test_name=None, proxy_port_number=None):
+  def __init__(self, server_binding_address, proxy_address=None,
+               proxy_port=None, test_name=None, wpt_http_port=None):
     logging.basicConfig(level=logging.DEBUG)
-    GetDeviceParams()
 
-    # Port number used to create the proxy server. If the --proxy target param
-    # is not set, a random free port is used.
-    if proxy_port_number is None:
-      proxy_port_number = str(self.GetUnusedPort(_binding_address))
-      _device_params.target_params.append('--proxy=%s:%s' % (_binding_address,
-                                                             proxy_port_number))
+    # Setup global variables used by test cases
+    global _device_params
+    _device_params = cobalt_runner.GetDeviceParamsFromCommandLine()
+    # Keep other modules from seeing these args
+    sys.argv = sys.argv[:1]
+    global _binding_address
+    _binding_address = server_binding_address
+    # Port used to create the web platform test http server. If not specified,
+    # a random free port is used.
+    if wpt_http_port is None:
+      wpt_http_port = str(self.GetUnusedPort([server_binding_address]))
+    global _wpt_http_port
+    _wpt_http_port = wpt_http_port
+    _device_params.target_params.append(
+        '--web-platform-test-server=http://web-platform.test:%s' %
+        wpt_http_port)
 
+    # Port used to create the proxy server. If not specified, a random free
+    # port is used.
+    if proxy_port is None:
+      proxy_port = str(self.GetUnusedPort([server_binding_address]))
+    if proxy_address is None:
+      proxy_address = server_binding_address
+    _device_params.target_params.append('--proxy=%s:%s' %
+                                        (proxy_address, proxy_port))
+
+    self.proxy_port = proxy_port
     self.test_name = test_name
-    self.proxy_port_number = proxy_port_number
 
     # Test domains used in web platform tests to be resolved to the server
     # binding address.
@@ -151,14 +166,14 @@
         'xn--n8j6ds53lwwkrqhv28a.web-platform.test',
         'xn--lve-6lad.web-platform.test'
     ]
-    self.host_resolve_map = dict([(host, _binding_address) for host in hosts])
+    self.host_resolve_map = dict([(host, server_binding_address) for host in hosts])
 
   def Run(self):
-    if self.proxy_port_number == '-1':
+    if self.proxy_port == '-1':
       return 1
-    logging.info('Using proxy port number: %s', self.proxy_port_number)
+    logging.info('Using proxy port: %s', self.proxy_port)
 
-    with ProxyServer(port=self.proxy_port_number,
+    with ProxyServer(port=self.proxy_port,
                      host_resolve_map=self.host_resolve_map):
       if self.test_name:
         suite = unittest.TestLoader().loadTestsFromModule(
@@ -171,42 +186,66 @@
           verbosity=0, stream=sys.stdout).run(suite).wasSuccessful()
       return return_code
 
-  def GetUnusedPort(self, machine_address):
-    """Find a free port on the machine address by pinging with socket."""
-    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  def GetUnusedPort(self, addresses):
+    """Find a free port on the list of addresses by pinging with sockets."""
+    SOCKET_SUCCESS = 0
+
+    if not addresses:
+      logging.error('Can not find unused port on invalid addresses.')
+      return -1
+
+    socks = []
+    for address in addresses:
+      socks.append((address, socket.socket(socket.AF_INET, socket.SOCK_STREAM)))
     try:
-      for i in range(1, _PORT_SELECTION_RETRY_LIMIT):
-        port_number = random.randint(_PORT_SELECTION_RANGE[0],
-                                     _PORT_SELECTION_RANGE[1])
-        result = sock.connect_ex((machine_address, port_number))
-        if result != 0:
-          return port_number
-        if i == _PORT_SELECTION_RETRY_LIMIT - 1:
-          logging.error(
-              'Can not find unused port on target machine within %s attempts.',
-              _PORT_SELECTION_RETRY_LIMIT)
-          return -1
+      for _ in range(_PORT_SELECTION_RETRY_LIMIT):
+        port = random.randint(_PORT_SELECTION_RANGE[0], _PORT_SELECTION_RANGE[1])
+        unused = True
+        for sock in socks:
+          result = sock[1].connect_ex((sock[0], port))
+          if result == SOCKET_SUCCESS:
+            ununsed = False
+            break
+        if unused:
+          return port
+      logging.error(
+          'Can not find unused port on addresses within %s attempts.' %
+          _PORT_SELECTION_RETRY_LIMIT)
+      return -1
     finally:
-      sock.close()
+      for sock in socks:
+        sock[1].close()
 
 
 def main():
   parser = argparse.ArgumentParser()
-  parser.add_argument('--test_name',
-                      help=('Name of test to be run. If not specified, all '
-                            'tests are run.'))
   parser.add_argument('--server_binding_address',
                       default='127.0.0.1',
                       help='Binding address used to create the test server.')
-  parser.add_argument('--proxy_port_number',
-                      help=('Port number used to create the proxy http server'
-                            'that all black box tests are run through. If not'
+  parser.add_argument('--proxy_address',
+                      default=None,
+                      help=('Address to the proxy server that all black box'
+                            'tests are run through. If not specified, the'
+                            'server binding address is used.'))
+  parser.add_argument('--proxy_port',
+                      default=None,
+                      help=('Port used to create the proxy server that all'
+                            'black box tests are run through. If not'
                             'specified, a random free port is used.'))
+  parser.add_argument('--test_name',
+                      default=None,
+                      help=('Name of test to be run. If not specified, all '
+                            'tests are run.'))
+  parser.add_argument('--wpt_http_port',
+                      default=None,
+                       help=('Port used to create the web platform test http'
+                             'server. If not specified, a random free port is'
+                             'used.'))
   args, _ = parser.parse_known_args()
 
-  global _binding_address
-  _binding_address = args.server_binding_address
-  test_object = BlackBoxTests(args.test_name, args.proxy_port_number)
+  test_object = BlackBoxTests(args.server_binding_address, args.proxy_address,
+                              args.proxy_port, args.test_name,
+                              args.wpt_http_port)
   sys.exit(test_object.Run())
 
 
diff --git a/src/cobalt/black_box_tests/testdata/web_debugger.html b/src/cobalt/black_box_tests/testdata/web_debugger.html
index 2ef6a09..aee0e79 100644
--- a/src/cobalt/black_box_tests/testdata/web_debugger.html
+++ b/src/cobalt/black_box_tests/testdata/web_debugger.html
@@ -3,6 +3,7 @@
 <head>
   <title>Connect to web debugger</title>
   <script src='black_box_js_test_utils.js'></script>
+  <script src='web_debugger_test_utils.js'></script>
 </head>
 
 <body>
diff --git a/src/cobalt/black_box_tests/testdata/web_debugger_test_utils.js b/src/cobalt/black_box_tests/testdata/web_debugger_test_utils.js
new file mode 100644
index 0000000..2b70459
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/web_debugger_test_utils.js
@@ -0,0 +1,129 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// This is the function we set the breakpoint on.
+function asyncBreak() {
+  foo = "bar";
+}
+
+// Tests AsyncTask reporting in WindowTimers.
+function testSetTimeout() {
+  asyncA(asyncBreak);
+}
+
+function asyncA(f) {
+  setTimeout(function timeoutA() { asyncB(f) }, 1);
+}
+
+function asyncB(f) {
+  setTimeout(function timeoutB() { asyncC(f) }, 1);
+}
+
+function asyncC(f) {
+  f();
+}
+
+// Tests AsyncTask reporting in a Promise using its 'then' method.
+function testPromise() {
+  let p = makePromise();
+  waitPromise(p);
+}
+
+function makePromise() {
+  return new Promise(function promiseExecutor(resolve, reject) {
+        setTimeout(function promiseTimeout() {
+          resolve();
+        }, 1)
+      });
+}
+
+function waitPromise(p) {
+  p.then(promiseThen);
+}
+
+function promiseThen() {
+  asyncBreak();
+}
+
+// Tests AsyncTask reporting in a JS async function that awaits a promise.
+function testAsyncFunction() {
+  let p = makePromise();
+  asyncAwait(p);
+}
+
+async function asyncAwait(p) {
+  await p;
+  asyncBreak();
+}
+
+// Tests AsyncTask reporting in EventTarget.
+function testXHR(url) {
+  doXHR(url);
+}
+
+function doXHR(url) {
+  let xhr = new XMLHttpRequest();
+  xhr.onload = function fileLoaded() {
+    asyncBreak();
+  }
+  xhr.open('GET', url);
+  xhr.send();
+}
+
+// Tests AsyncTask reporting in a MutationObserver.
+function testMutate() {
+  let target = document.getElementById('test');
+  let config = {attributes: true, childList: true, subtree: true};
+  let observer = new MutationObserver(mutationCallback);
+  observer.observe(target, config);
+  doSetAttribute(target, 'foo', 'bar');
+}
+
+function mutationCallback(mutationsList, observer) {
+  asyncBreak();
+}
+
+function doSetAttribute(node, attr, value) {
+  node.setAttribute(attr, value);
+}
+
+function testAnimationFrame() {
+  doRequestAnimationFrame();
+}
+
+function doRequestAnimationFrame() {
+  window.requestAnimationFrame(function animationFrameCallback() {
+    asyncBreak();
+  });
+}
+
+// Tests AsyncTask reporting in a MediaSource (that uses an EventQueue).
+function testMediaSource(){
+  let elem = document.createElement('video');
+  let ms = new MediaSource;
+  setSourceListener(ms);
+  attachMediaSource(elem, ms);
+}
+
+function setSourceListener(source) {
+  source.addEventListener('sourceopen', function sourceOpenCallback() {
+    asyncBreak();
+  });
+}
+
+function attachMediaSource(elem, ms) {
+  let url = window.URL.createObjectURL(ms);
+  elem.src = url;
+}
diff --git a/src/cobalt/black_box_tests/tests/web_debugger.py b/src/cobalt/black_box_tests/tests/web_debugger.py
index e170032..68553c2 100644
--- a/src/cobalt/black_box_tests/tests/web_debugger.py
+++ b/src/cobalt/black_box_tests/tests/web_debugger.py
@@ -49,6 +49,17 @@
     super(DebuggerCommandError, self).__init__(code + error['message'])
 
 
+class JavaScriptError(Exception):
+  """Exception when a JavaScript exception occurs in an evaluation."""
+
+  def __init__(self, exception_details):
+    # All the fields we care about are optional, so gracefully fallback.
+    ex = exception_details.get('exception', {})
+    fallback = ex.get('className', 'Unknown error') + ' (No description)'
+    msg = ex.get('description', fallback)
+    super(JavaScriptError, self).__init__(msg)
+
+
 class DebuggerConnection(object):
   """Connection to debugger over a WebSocket.
 
@@ -164,15 +175,23 @@
 
   def evaluate_js(self, expression):
     """Helper for the 'Runtime.evaluate' command to run some JavaScript."""
-    return self.run_command('Runtime.evaluate', {
+    response = self.run_command('Runtime.evaluate', {
         'contextId': self.context_id,
         'expression': expression,
     })
+    if 'exceptionDetails' in response['result']:
+      raise JavaScriptError(response['result']['exceptionDetails'])
+    return response['result']
 
 
 class WebDebuggerTest(black_box_tests.BlackBoxTestCase):
   """Test interaction with the web debugger over a WebSocket."""
 
+  def setUpWith(self, cm):
+    val = cm.__enter__()
+    self.addCleanup(cm.__exit__, None, None, None)
+    return val
+
   def setUp(self):
     platform_vars = self.platform_config.GetVariables(self.device_params.config)
     if platform_vars['javascript_engine'] != 'v8':
@@ -182,8 +201,16 @@
     if not cobalt_vars['enable_debugger']:
       self.skipTest('DevTools is disabled on this platform')
 
-  def create_debugger_connection(self, runner):
-    devtools_url = runner.GetCval('Cobalt.Server.DevTools')
+    self.server = self.setUpWith(
+        ThreadedWebServer(binding_address=self.GetBindingAddress()))
+    url = self.server.GetURL(file_name='testdata/web_debugger.html')
+    self.runner = self.setUpWith(self.CreateCobaltRunner(url=url))
+    self.debugger = self.setUpWith(self.create_debugger_connection())
+    self.runner.WaitForJSTestsSetup()
+    self.debugger.enable_runtime()
+
+  def create_debugger_connection(self):
+    devtools_url = self.runner.GetCval('Cobalt.Server.DevTools')
     parts = list(urlparse.urlsplit(devtools_url))
     parts[0] = 'ws'  # scheme
     parts[2] = '/devtools/page/cobalt'  # path
@@ -191,170 +218,323 @@
     return DebuggerConnection(ws_url)
 
   def test_runtime(self):
-    with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
-      url = server.GetURL(file_name='testdata/web_debugger.html')
-      with self.CreateCobaltRunner(url=url) as runner:
-        with self.create_debugger_connection(runner) as debugger:
-          runner.WaitForJSTestsSetup()
-          debugger.enable_runtime()
+    # Evaluate a simple expression.
+    eval_result = self.debugger.evaluate_js('6 * 7')
+    self.assertEqual(42, eval_result['result']['value'])
 
-          # Evaluate a simple expression.
-          eval_response = debugger.evaluate_js('6 * 7')
-          self.assertEqual(42, eval_response['result']['result']['value'])
+    # Set an attribute and read it back w/ WebDriver.
+    self.debugger.evaluate_js(
+        'document.body.setAttribute("web_debugger", "tested")')
+    self.assertEqual(
+        'tested',
+        self.runner.UniqueFind('body').get_attribute('web_debugger'))
 
-          # Set an attribute and read it back w/ WebDriver.
-          debugger.evaluate_js(
-              'document.body.setAttribute("web_debugger", "tested")')
-          self.assertEqual(
-              'tested',
-              runner.UniqueFind('body').get_attribute('web_debugger'))
+    # Log to console, and check we get the console event.
+    self.debugger.evaluate_js('console.log("hello")')
+    console_event = self.debugger.wait_event('Runtime.consoleAPICalled')
+    self.assertEqual('hello', console_event['params']['args'][0]['value'])
 
-          # Log to console, and check we get the console event.
-          debugger.evaluate_js('console.log("hello")')
-          console_event = debugger.wait_event('Runtime.consoleAPICalled')
-          self.assertEqual('hello', console_event['params']['args'][0]['value'])
-
-          # End the test.
-          debugger.evaluate_js('onEndTest()')
-          self.assertTrue(runner.JSTestsSucceeded())
+    # End the test.
+    self.debugger.evaluate_js('onEndTest()')
+    self.assertTrue(self.runner.JSTestsSucceeded())
 
   def test_dom(self):
-    with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
-      url = server.GetURL(file_name='testdata/web_debugger.html')
-      with self.CreateCobaltRunner(url=url) as runner:
-        with self.create_debugger_connection(runner) as debugger:
-          runner.WaitForJSTestsSetup()
-          debugger.enable_runtime()
-          debugger.run_command('DOM.enable')
+    self.debugger.run_command('DOM.enable')
 
-          doc_response = debugger.run_command('DOM.getDocument')
-          doc_root = doc_response['result']['root']
-          self.assertEqual('#document', doc_root['nodeName'])
+    doc_response = self.debugger.run_command('DOM.getDocument')
+    doc_root = doc_response['result']['root']
+    self.assertEqual('#document', doc_root['nodeName'])
 
-          doc_url = doc_root['documentURL']
-          # remove query params (cert_scope, etc.)
-          doc_url = doc_url.split('?')[0]
-          self.assertEqual(url, doc_url)
+    doc_url = doc_root['documentURL']
+    # remove query params (cert_scope, etc.)
+    doc_url = doc_url.split('?')[0]
+    self.assertEqual(self.runner.url, doc_url)
 
-          # document: <html><head></head><body></body></html>
-          html_node = doc_root['children'][0]
-          body_node = html_node['children'][1]
-          self.assertEqual('BODY', body_node['nodeName'])
+    # document: <html><head></head><body></body></html>
+    html_node = doc_root['children'][0]
+    body_node = html_node['children'][1]
+    self.assertEqual('BODY', body_node['nodeName'])
 
-          # body:
-          #   <h1><span>Web debugger</span></h1>
-          #   <div#test>
-          #     <div#A><div#A1/><div#A2/></div#A>
-          #     <div#B/>
-          #   </div#test>
-          debugger.run_command('DOM.requestChildNodes', {
-              'nodeId': body_node['nodeId'],
-              'depth': -1,  # entire subtree
-          })
-          child_nodes_event = debugger.wait_event('DOM.setChildNodes')
+    # body:
+    #   <h1><span>Web debugger</span></h1>
+    #   <div#test>
+    #     <div#A><div#A1/><div#A2/></div#A>
+    #     <div#B/>
+    #   </div#test>
+    self.debugger.run_command('DOM.requestChildNodes', {
+        'nodeId': body_node['nodeId'],
+        'depth': -1,  # entire subtree
+    })
+    child_nodes_event = self.debugger.wait_event('DOM.setChildNodes')
 
-          h1 = child_nodes_event['params']['nodes'][0]
-          span = h1['children'][0]
-          text = span['children'][0]
-          self.assertEqual('H1', h1['nodeName'])
-          self.assertEqual('SPAN', span['nodeName'])
-          self.assertEqual('#text', text['nodeName'])
-          self.assertEqual('Web debugger', text['nodeValue'])
+    h1 = child_nodes_event['params']['nodes'][0]
+    span = h1['children'][0]
+    text = span['children'][0]
+    self.assertEqual('H1', h1['nodeName'])
+    self.assertEqual('SPAN', span['nodeName'])
+    self.assertEqual('#text', text['nodeName'])
+    self.assertEqual('Web debugger', text['nodeValue'])
 
-          test_div = child_nodes_event['params']['nodes'][1]
-          child_a = test_div['children'][0]
-          child_a1 = child_a['children'][0]
-          child_a2 = child_a['children'][1]
-          child_b = test_div['children'][1]
-          self.assertEqual(2, test_div['childNodeCount'])
-          self.assertEqual(2, child_a['childNodeCount'])
-          self.assertEqual(0, child_b['childNodeCount'])
-          self.assertEqual(['id', 'test'], test_div['attributes'])
-          self.assertEqual(['id', 'A'], child_a['attributes'])
-          self.assertEqual(['id', 'A1'], child_a1['attributes'])
-          self.assertEqual(['id', 'A2'], child_a2['attributes'])
-          self.assertEqual(['id', 'B'], child_b['attributes'])
-          self.assertEqual([], child_b['children'])
+    test_div = child_nodes_event['params']['nodes'][1]
+    child_a = test_div['children'][0]
+    child_a1 = child_a['children'][0]
+    child_a2 = child_a['children'][1]
+    child_b = test_div['children'][1]
+    self.assertEqual(2, test_div['childNodeCount'])
+    self.assertEqual(2, child_a['childNodeCount'])
+    self.assertEqual(0, child_b['childNodeCount'])
+    self.assertEqual(['id', 'test'], test_div['attributes'])
+    self.assertEqual(['id', 'A'], child_a['attributes'])
+    self.assertEqual(['id', 'A1'], child_a1['attributes'])
+    self.assertEqual(['id', 'A2'], child_a2['attributes'])
+    self.assertEqual(['id', 'B'], child_b['attributes'])
+    self.assertEqual([], child_b['children'])
 
-          # Repeat, but only to depth 2 - not reporting children of A & B.
-          debugger.run_command('DOM.requestChildNodes', {
-              'nodeId': body_node['nodeId'],
-              'depth': 2,
-          })
-          child_nodes_event = debugger.wait_event('DOM.setChildNodes')
+    # Repeat, but only to depth 2 - not reporting children of A & B.
+    self.debugger.run_command('DOM.requestChildNodes', {
+        'nodeId': body_node['nodeId'],
+        'depth': 2,
+    })
+    child_nodes_event = self.debugger.wait_event('DOM.setChildNodes')
 
-          test_div = child_nodes_event['params']['nodes'][1]
-          child_a = test_div['children'][0]
-          child_b = test_div['children'][1]
-          self.assertFalse('children' in child_a)
-          self.assertFalse('children' in child_b)
-          self.assertEqual(2, test_div['childNodeCount'])
-          self.assertEqual(2, child_a['childNodeCount'])
-          self.assertEqual(0, child_b['childNodeCount'])
-          self.assertEqual(['id', 'test'], test_div['attributes'])
-          self.assertEqual(['id', 'A'], child_a['attributes'])
-          self.assertEqual(['id', 'B'], child_b['attributes'])
+    test_div = child_nodes_event['params']['nodes'][1]
+    child_a = test_div['children'][0]
+    child_b = test_div['children'][1]
+    self.assertFalse('children' in child_a)
+    self.assertFalse('children' in child_b)
+    self.assertEqual(2, test_div['childNodeCount'])
+    self.assertEqual(2, child_a['childNodeCount'])
+    self.assertEqual(0, child_b['childNodeCount'])
+    self.assertEqual(['id', 'test'], test_div['attributes'])
+    self.assertEqual(['id', 'A'], child_a['attributes'])
+    self.assertEqual(['id', 'B'], child_b['attributes'])
 
-          # Repeat, to default depth of 1 - not reporting children of "#test".
-          debugger.run_command('DOM.requestChildNodes', {
-              'nodeId': body_node['nodeId'],
-          })
-          child_nodes_event = debugger.wait_event('DOM.setChildNodes')
+    # Repeat, to default depth of 1 - not reporting children of "#test".
+    self.debugger.run_command('DOM.requestChildNodes', {
+        'nodeId': body_node['nodeId'],
+    })
+    child_nodes_event = self.debugger.wait_event('DOM.setChildNodes')
 
-          test_div = child_nodes_event['params']['nodes'][1]
-          self.assertFalse('children' in test_div)
-          self.assertEqual(2, test_div['childNodeCount'])
-          self.assertEqual(['id', 'test'], test_div['attributes'])
+    test_div = child_nodes_event['params']['nodes'][1]
+    self.assertFalse('children' in test_div)
+    self.assertEqual(2, test_div['childNodeCount'])
+    self.assertEqual(['id', 'test'], test_div['attributes'])
 
-          # Get the test div as a remote object, and request it as a node.
-          # This sends a 'DOM.setChildNodes' event for each node up to the root.
-          eval_result = debugger.evaluate_js('document.getElementById("test")')
-          node_response = debugger.run_command('DOM.requestNode', {
-              'objectId': eval_result['result']['result']['objectId'],
-          })
-          self.assertEqual(test_div['nodeId'],
-                           node_response['result']['nodeId'])
+    # Get the test div as a remote object, and request it as a node.
+    # This sends a 'DOM.setChildNodes' event for each node up to the root.
+    eval_result = self.debugger.evaluate_js('document.getElementById("test")')
+    node_response = self.debugger.run_command('DOM.requestNode', {
+        'objectId': eval_result['result']['objectId'],
+    })
+    self.assertEqual(test_div['nodeId'],
+                     node_response['result']['nodeId'])
 
-          # Event reporting the requested <div#test>
-          node_event = debugger.wait_event('DOM.setChildNodes')
-          self.assertEqual(test_div['nodeId'],
-                           node_event['params']['nodes'][0]['nodeId'])
-          self.assertEqual(body_node['nodeId'],
-                           node_event['params']['parentId'])
+    # Event reporting the requested <div#test>
+    node_event = self.debugger.wait_event('DOM.setChildNodes')
+    self.assertEqual(test_div['nodeId'],
+                     node_event['params']['nodes'][0]['nodeId'])
+    self.assertEqual(body_node['nodeId'],
+                     node_event['params']['parentId'])
 
-          # Event reporting the parent <body>
-          node_event = debugger.wait_event('DOM.setChildNodes')
-          self.assertEqual(body_node['nodeId'],
-                           node_event['params']['nodes'][0]['nodeId'])
-          self.assertEqual(html_node['nodeId'],
-                           node_event['params']['parentId'])
+    # Event reporting the parent <body>
+    node_event = self.debugger.wait_event('DOM.setChildNodes')
+    self.assertEqual(body_node['nodeId'],
+                     node_event['params']['nodes'][0]['nodeId'])
+    self.assertEqual(html_node['nodeId'],
+                     node_event['params']['parentId'])
 
-          # Event reporting the parent <html>
-          node_event = debugger.wait_event('DOM.setChildNodes')
-          self.assertEqual(html_node['nodeId'],
-                           node_event['params']['nodes'][0]['nodeId'])
-          self.assertEqual(doc_root['nodeId'], node_event['params']['parentId'])
+    # Event reporting the parent <html>
+    node_event = self.debugger.wait_event('DOM.setChildNodes')
+    self.assertEqual(html_node['nodeId'],
+                     node_event['params']['nodes'][0]['nodeId'])
+    self.assertEqual(doc_root['nodeId'], node_event['params']['parentId'])
 
-          # Round trip resolving test div to an object, then back to a node.
-          resolve_response = debugger.run_command('DOM.resolveNode', {
-              'nodeId': test_div['nodeId'],
-          })
-          node_response = debugger.run_command('DOM.requestNode', {
-              'objectId': resolve_response['result']['object']['objectId'],
-          })
-          self.assertEqual(test_div['nodeId'],
-                           node_response['result']['nodeId'])
+    # Round trip resolving test div to an object, then back to a node.
+    resolve_response = self.debugger.run_command('DOM.resolveNode', {
+        'nodeId': test_div['nodeId'],
+    })
+    node_response = self.debugger.run_command('DOM.requestNode', {
+        'objectId': resolve_response['result']['object']['objectId'],
+    })
+    self.assertEqual(test_div['nodeId'],
+                     node_response['result']['nodeId'])
 
-          # Event reporting the requested <div#test>
-          node_event = debugger.wait_event('DOM.setChildNodes')
-          self.assertEqual(test_div['nodeId'],
-                           node_event['params']['nodes'][0]['nodeId'])
-          self.assertEqual(body_node['nodeId'],
-                           node_event['params']['parentId'])
-          # Ignore the other two events reporting the parents.
-          node_event = debugger.wait_event('DOM.setChildNodes')
-          node_event = debugger.wait_event('DOM.setChildNodes')
+    # Event reporting the requested <div#test>
+    node_event = self.debugger.wait_event('DOM.setChildNodes')
+    self.assertEqual(test_div['nodeId'],
+                     node_event['params']['nodes'][0]['nodeId'])
+    self.assertEqual(body_node['nodeId'],
+                     node_event['params']['parentId'])
+    # Ignore the other two events reporting the parents.
+    node_event = self.debugger.wait_event('DOM.setChildNodes')
+    node_event = self.debugger.wait_event('DOM.setChildNodes')
 
-          # End the test.
-          debugger.evaluate_js('onEndTest()')
-          self.assertTrue(runner.JSTestsSucceeded())
+    # End the test.
+    self.debugger.evaluate_js('onEndTest()')
+    self.assertTrue(self.runner.JSTestsSucceeded())
+
+  def assert_paused(self, expected_stacks):
+    """Checks that the debugger is paused at a breakpoint.
+
+    Waits for the expected |Debugger.paused| event from hitting the breakpoint
+    and then asserts that the expected_stacks match the call stacks in that
+    event. Execution is always resumed before returning so that more JS can be
+    evaluated, as needed to continue or end the test.
+
+    Args:
+      expected_stacks: A list of lists of strings with the expected function
+        names in the call stacks in a series of asynchronous executions.
+    """
+    paused_event = self.debugger.wait_event('Debugger.paused')
+    try:
+      call_stacks = []
+      # First the main stack where the breakpoint was hit.
+      call_frames = paused_event['params']['callFrames']
+      call_stack = [frame['functionName'] for frame in call_frames]
+      call_stacks.append(call_stack)
+      # Then asynchronous stacks that preceeded the main stack.
+      async_trace = paused_event['params'].get('asyncStackTrace')
+      while async_trace:
+        call_frames = async_trace['callFrames']
+        call_stack = [frame['functionName'] for frame in call_frames]
+        call_stacks.append(call_stack)
+        async_trace = async_trace.get('parent')
+      self.assertEqual(expected_stacks, call_stacks)
+    finally:
+      # We must resume in order to avoid hanging if something goes wrong.
+      self.debugger.run_command('Debugger.resume')
+
+  def test_debugger_breakpoint(self):
+    self.debugger.run_command('Debugger.enable')
+
+    # Get the ID and source of our JavaScript test utils.
+    script_id = ''
+    while not script_id:
+      script_event = self.debugger.wait_event('Debugger.scriptParsed')
+      script_url = script_event['params']['url']
+      if script_url.endswith('web_debugger_test_utils.js'):
+        script_id = script_event['params']['scriptId']
+    source_response = self.debugger.run_command('Debugger.getScriptSource', {
+        'scriptId': script_id,
+    })
+    script_source = source_response['result']['scriptSource'].splitlines()
+
+    # Set a breakpoint on the asyncBreak() function.
+    line_number = next(n for n, l in enumerate(script_source)
+                       if l.startswith('function asyncBreak'))
+    self.debugger.run_command('Debugger.setBreakpoint', {
+        'location': {
+            'scriptId': script_id,
+            'lineNumber': line_number,
+        },
+    })
+    self.debugger.run_command('Debugger.setAsyncCallStackDepth', {
+        'maxDepth': 99,
+    })
+
+    # Check the breakpoint within a SetTimeout() callback.
+    self.debugger.evaluate_js('testSetTimeout()')
+    self.assert_paused([
+        [
+            'asyncBreak',
+            'asyncC',
+            'timeoutB',
+        ],
+        [
+            'asyncB',
+            'timeoutA',
+        ],
+        [
+            'asyncA',
+            'testSetTimeout',
+            '',  # Anonymous function for the 'Runtime.evaluate' command.
+        ]
+    ])
+
+    # Check the breakpoint within a promise "then" after being resolved.
+    self.debugger.evaluate_js('testPromise()')
+    self.assert_paused([
+        [
+            'asyncBreak',
+            'promiseThen',
+        ],
+        [
+            'waitPromise',
+            'testPromise',
+            '',  # Anonymous function for the 'Runtime.evaluate' command.
+        ]
+    ])
+
+    # Check the breakpoint after async await for a promise to resolve.
+    self.debugger.evaluate_js('testAsyncFunction()')
+    self.assert_paused([
+        [
+            'asyncBreak',
+            'asyncAwait',
+        ],
+        [
+            'asyncAwait',
+            'testAsyncFunction',
+            '',  # Anonymous function for the 'Runtime.evaluate' command.
+        ]
+    ])
+
+    # Check the breakpoint within an XHR event handler.
+    self.debugger.evaluate_js('testXHR(window.location.href)')
+    self.assert_paused([
+        [
+            'asyncBreak',
+            'fileLoaded',
+        ],
+        [
+            'doXHR',
+            'testXHR',
+            '',  # Anonymous function for the 'Runtime.evaluate' command.
+        ]
+    ])
+
+    # Check the breakpoint within a MutationObserver.
+    self.debugger.evaluate_js('testMutate()')
+    self.assert_paused([
+        [
+            'asyncBreak',
+            'mutationCallback',
+        ],
+        [
+            'doSetAttribute',
+            'testMutate',
+            '',  # Anonymous function for the 'Runtime.evaluate' command.
+        ],
+    ])
+
+    # Check the breakpoint within an animation callback.
+    self.debugger.evaluate_js('testAnimationFrame()')
+    self.assert_paused([
+        [
+            'asyncBreak',
+            'animationFrameCallback',
+        ],
+        [
+            'doRequestAnimationFrame',
+            'testAnimationFrame',
+            '',  # Anonymous function for the 'Runtime.evaluate' command.
+        ],
+    ])
+
+    # Check the breakpoint on a media callback going through EventQueue.
+    self.debugger.evaluate_js('testMediaSource()')
+    self.assert_paused([
+        [
+            'asyncBreak',
+            'sourceOpenCallback',
+        ],
+        [
+            'setSourceListener',
+            'testMediaSource',
+            '',  # Anonymous function for the 'Runtime.evaluate' command.
+        ],
+    ])
+
+    # End the test.
+    self.debugger.evaluate_js('onEndTest()')
+    self.assertTrue(self.runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/web_platform_tests.py b/src/cobalt/black_box_tests/tests/web_platform_tests.py
index 12adcd0..283b5d6 100644
--- a/src/cobalt/black_box_tests/tests/web_platform_tests.py
+++ b/src/cobalt/black_box_tests/tests/web_platform_tests.py
@@ -33,20 +33,28 @@
       self.skipTest('Can only run web platform tests on debug or devel config.')
 
   def test_simple(self):
-    with WebPlatformTestServer(binding_address=self.GetBindingAddress()):
+    with WebPlatformTestServer(binding_address=self.GetBindingAddress(),
+                               wpt_http_port=self.GetWptHttpPort()):
       target_params = []
 
       filters = self.cobalt_config.GetWebPlatformTestFilters()
+      used_filters = []
 
-      if test_filter.DISABLE_TESTING in filters:
-        return
+      for filter in filters:
+        if filter == test_filter.DISABLE_TESTING:
+          return
+        if filter == test_filter.FILTER_ALL:
+          return
+        if isinstance(filter, test_filter.TestFilter):
+          if filter.config and filter.config != self.device_params.config:
+            continue
+          used_filters.append(filter.test_name)
+        else:
+          used_filters.append(filter)
 
-      if test_filter.FILTER_ALL in filters:
-        return
-
-      if filters:
-        target_params.append('--gtest_filter=-{}'.format(':'.join(
-            filters)))
+      if used_filters:
+        target_params.append('--gtest_filter=-{}'.format(
+            ':'.join(used_filters)))
 
       if self.device_params.target_params:
         target_params += self.device_params.target_params
@@ -58,6 +66,7 @@
           device_id=self.device_params.device_id,
           target_params=target_params,
           output_file=None,
-          out_directory=self.device_params.out_directory)
+          out_directory=self.device_params.out_directory,
+          env_variables={'ASAN_OPTIONS': 'intercept_tls_get_addr=0'})
       status = launcher.Run()
       self.assertEqual(status, 0)
diff --git a/src/cobalt/black_box_tests/web_platform_test_server.py b/src/cobalt/black_box_tests/web_platform_test_server.py
index 5199151..d3a9d96 100644
--- a/src/cobalt/black_box_tests/web_platform_test_server.py
+++ b/src/cobalt/black_box_tests/web_platform_test_server.py
@@ -31,12 +31,16 @@
 class WebPlatformTestServer(object):
   """Runs a WPT StashServer on its own thread in a Python context manager."""
 
-  def __init__(self, binding_address=None):
+  def __init__(self, binding_address=None, wpt_http_port=None):
     # IP config['host'] should map to either through a dns or the hosts file.
     if binding_address:
       self._binding_address = binding_address
     else:
       self._binding_address = '127.0.0.1'
+    if wpt_http_port:
+      self._wpt_http_port = wpt_http_port
+    else:
+      self._wpt_http_port = '8000'
 
   def main(self):
     kwargs = vars(serve.get_parser().parse_args())
@@ -45,6 +49,7 @@
     config = serve.load_config(os.path.join(WPT_DIR, 'config.default.json'),
                                os.path.join(WPT_DIR, 'config.json'),
                                **kwargs)
+    config['ports']['http'][0] = int(self._wpt_http_port)
 
     serve.setup_logger(config['log_level'])
 
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 045fcdf..9b76975 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -86,6 +86,30 @@
   return !base::strcasecmp(str.c_str(), "none");
 }
 
+#if defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
+std::string GetDevServersListenIp() {
+  bool ip_v6;
+#if SB_API_VERSION >= SB_IPV6_REQUIRED_VERSION
+  ip_v6 = SbSocketIsIpv6Supported();
+#elif SB_HAS(IPV6)
+  ip_v6 = true;
+#else
+  ip_v6 = false;
+#endif
+  std::string listen_ip(ip_v6 ? "::" : "0.0.0.0");
+
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kDevServersListenIp)) {
+    listen_ip =
+        command_line->GetSwitchValueASCII(switches::kDevServersListenIp);
+  }
+#endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+
+  return listen_ip;
+}
+#endif  // defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
+
 #if defined(ENABLE_DEBUGGER)
 int GetRemoteDebuggingPort() {
 #if defined(SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT)
@@ -145,11 +169,13 @@
 std::string GetWebDriverListenIp() {
   // The default IP on which the webdriver server should listen for incoming
   // connections.
-  std::string webdriver_listen_ip =
-      webdriver::WebDriverModule::kDefaultListenIp;
+  std::string webdriver_listen_ip = GetDevServersListenIp();
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kWebDriverListenIp)) {
+    DLOG(WARNING) << "The \"--" << switches::kWebDriverListenIp
+                  << "\" switch is deprecated; please use \"--"
+                  << switches::kDevServersListenIp << "\" instead.";
     webdriver_listen_ip =
         command_line->GetSwitchValueASCII(switches::kWebDriverListenIp);
   }
@@ -183,20 +209,23 @@
   }
 
 #if SB_API_VERSION >= 11
-  // Append the device authentication query parameters based on the platform's
-  // certification secret to the initial URL.
-  std::string query = initial_url.query();
-  std::string device_authentication_query_string =
-      GetDeviceAuthenticationSignedURLQueryString();
-  if (!query.empty() && !device_authentication_query_string.empty()) {
-    query += "&";
-  }
-  query += device_authentication_query_string;
+  if (!command_line->HasSwitch(
+          switches::kOmitDeviceAuthenticationQueryParameters)) {
+    // Append the device authentication query parameters based on the platform's
+    // certification secret to the initial URL.
+    std::string query = initial_url.query();
+    std::string device_authentication_query_string =
+        GetDeviceAuthenticationSignedURLQueryString();
+    if (!query.empty() && !device_authentication_query_string.empty()) {
+      query += "&";
+    }
+    query += device_authentication_query_string;
 
-  if (!query.empty()) {
-    GURL::Replacements replacements;
-    replacements.SetQueryStr(query);
-    initial_url = initial_url.ReplaceComponents(replacements);
+    if (!query.empty()) {
+      GURL::Replacements replacements;
+      replacements.SetQueryStr(query);
+      initial_url = initial_url.ReplaceComponents(replacements);
+    }
   }
 #endif  // SB_API_VERSION >= 11
 
@@ -524,11 +553,13 @@
     options.storage_manager_options.savegame_options.factory =
         &storage::SavegameFake::Create;
   }
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   if (command_line->HasSwitch(browser::switches::kDisableOnScreenKeyboard)) {
     options.enable_on_screen_keyboard = false;
   }
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 #endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
 
@@ -668,7 +699,8 @@
   event_dispatcher_.AddEventCallback(base::WindowSizeChangedEvent::TypeId(),
                                      window_size_change_event_callback_);
 #endif  // SB_API_VERSION >= 8
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   on_screen_keyboard_shown_event_callback_ = base::Bind(
       &Application::OnOnScreenKeyboardShownEvent, base::Unretained(this));
   event_dispatcher_.AddEventCallback(base::OnScreenKeyboardShownEvent::TypeId(),
@@ -696,15 +728,16 @@
       base::OnScreenKeyboardSuggestionsUpdatedEvent::TypeId(),
       on_screen_keyboard_suggestions_updated_event_callback_);
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   on_caption_settings_changed_event_callback_ = base::Bind(
       &Application::OnCaptionSettingsChangedEvent, base::Unretained(this));
   event_dispatcher_.AddEventCallback(
       base::AccessibilityCaptionSettingsChangedEvent::TypeId(),
       on_caption_settings_changed_event_callback_);
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 #if defined(ENABLE_WEBDRIVER)
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   bool create_webdriver_module =
@@ -732,7 +765,7 @@
                << switches::kRemoteDebuggingPort << " is 0.";
   } else {
     debug_web_server_.reset(new debug::remote::DebugWebServer(
-        remote_debugging_port,
+        remote_debugging_port, GetDevServersListenIp(),
         base::Bind(&BrowserModule::CreateDebugClient,
                    base::Unretained(browser_module_.get()))));
   }
@@ -770,7 +803,8 @@
   event_dispatcher_.RemoveEventCallback(base::WindowSizeChangedEvent::TypeId(),
                                         window_size_change_event_callback_);
 #endif  // SB_API_VERSION >= 8
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   event_dispatcher_.RemoveEventCallback(
       base::OnScreenKeyboardShownEvent::TypeId(),
       on_screen_keyboard_shown_event_callback_);
@@ -788,12 +822,13 @@
       base::OnScreenKeyboardSuggestionsUpdatedEvent::TypeId(),
       on_screen_keyboard_suggestions_updated_event_callback_);
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
-#if SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   event_dispatcher_.RemoveEventCallback(
       base::AccessibilityCaptionSettingsChangedEvent::TypeId(),
       on_caption_settings_changed_event_callback_);
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 
   app_status_ = kShutDownAppStatus;
 }
@@ -851,7 +886,8 @@
               ->size));
       break;
 #endif  // SB_API_VERSION >= 8
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
     case kSbEventTypeOnScreenKeyboardShown:
       DCHECK(starboard_event->data);
       DispatchEventInternal(new base::OnScreenKeyboardShownEvent(
@@ -876,7 +912,8 @@
           *static_cast<int*>(starboard_event->data)));
       break;
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
     case kSbEventTypeLink: {
       const char* link = static_cast<const char*>(starboard_event->data);
       DispatchEventInternal(new base::DeepLinkEvent(link));
@@ -885,12 +922,12 @@
     case kSbEventTypeAccessiblitySettingsChanged:
       DispatchEventInternal(new base::AccessibilitySettingsChangedEvent());
       break;
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
     case kSbEventTypeAccessibilityCaptionSettingsChanged:
       DispatchEventInternal(
           new base::AccessibilityCaptionSettingsChangedEvent());
       break;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
     // Explicitly list unhandled cases here so that the compiler can give a
     // warning when a value is added, but not handled.
     case kSbEventTypeInput:
@@ -966,10 +1003,11 @@
 #if SB_API_VERSION >= 8
     case kSbEventTypeWindowSizeChanged:
 #endif
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
     case kSbEventTypeAccessibilityCaptionSettingsChanged:
-#endif  // SB_HAS(CAPTIONS)
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
     case kSbEventTypeOnScreenKeyboardBlurred:
     case kSbEventTypeOnScreenKeyboardFocused:
     case kSbEventTypeOnScreenKeyboardHidden:
@@ -977,7 +1015,8 @@
 #if SB_API_VERSION >= 11
     case kSbEventTypeOnScreenKeyboardSuggestionsUpdated:
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
     case kSbEventTypeAccessiblitySettingsChanged:
     case kSbEventTypeInput:
     case kSbEventTypeLink:
@@ -1021,7 +1060,8 @@
 }
 #endif  // SB_API_VERSION >= 8
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 void Application::OnOnScreenKeyboardShownEvent(const base::Event* event) {
   TRACE_EVENT0("cobalt::browser",
                "Application::OnOnScreenKeyboardShownEvent()");
@@ -1064,9 +1104,10 @@
           const base::OnScreenKeyboardSuggestionsUpdatedEvent*>(event));
 }
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 void Application::OnCaptionSettingsChangedEvent(const base::Event* event) {
   TRACE_EVENT0("cobalt::browser",
                "Application::OnCaptionSettingsChangedEvent()");
@@ -1074,7 +1115,7 @@
       base::polymorphic_downcast<
           const base::AccessibilityCaptionSettingsChangedEvent*>(event));
 }
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 
 void Application::WebModuleRecreated() {
   TRACE_EVENT0("cobalt::browser", "Application::WebModuleRecreated()");
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 66ff3c7..db55875 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -73,7 +73,8 @@
   void OnWindowSizeChangedEvent(const base::Event* event);
 #endif  // SB_API_VERSION >= 8
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   void OnOnScreenKeyboardShownEvent(const base::Event* event);
   void OnOnScreenKeyboardHiddenEvent(const base::Event* event);
   void OnOnScreenKeyboardFocusedEvent(const base::Event* event);
@@ -81,11 +82,12 @@
 #if SB_API_VERSION >= 11
   void OnOnScreenKeyboardSuggestionsUpdatedEvent(const base::Event* event);
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   void OnCaptionSettingsChangedEvent(const base::Event* event);
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 
   // Called when a navigation occurs in the BrowserModule.
   void WebModuleRecreated();
@@ -105,7 +107,8 @@
 #if SB_API_VERSION >= 8
   base::EventCallback window_size_change_event_callback_;
 #endif  // SB_API_VERSION >= 8
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   base::EventCallback on_screen_keyboard_shown_event_callback_;
   base::EventCallback on_screen_keyboard_hidden_event_callback_;
   base::EventCallback on_screen_keyboard_focused_event_callback_;
@@ -113,10 +116,11 @@
 #if SB_API_VERSION >= 11
   base::EventCallback on_screen_keyboard_suggestions_updated_event_callback_;
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
-#if SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   base::EventCallback on_caption_settings_changed_event_callback_;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 
   // Thread checkers to ensure that callbacks for network and application events
   // always occur on the same thread.
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 18aeeca..6052b23 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -252,13 +252,16 @@
           &storage_manager_, event_dispatcher_,
           options_.network_module_options),
       splash_screen_cache_(new SplashScreenCache()),
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
       on_screen_keyboard_bridge_(
-          options.enable_on_screen_keyboard
+          OnScreenKeyboardStarboardBridge::IsSupported() &&
+                  options.enable_on_screen_keyboard
               ? new OnScreenKeyboardStarboardBridge(base::Bind(
                     &BrowserModule::GetSbWindow, base::Unretained(this)))
               : NULL),
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
       web_module_loaded_(base::WaitableEvent::ResetPolicy::MANUAL,
                          base::WaitableEvent::InitialState::NOT_SIGNALED),
       web_module_recreated_callback_(options_.web_module_recreated_callback),
@@ -570,9 +573,7 @@
       base::Bind(&BrowserModule::OnLoad, base::Unretained(this)));
 #if defined(ENABLE_FAKE_MICROPHONE)
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kFakeMicrophone) ||
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kInputFuzzer)) {
+          switches::kFakeMicrophone)) {
     options.dom_settings_options.microphone_options.enable_fake_microphone =
         true;
   }
@@ -931,7 +932,8 @@
 }
 #endif  // SB_API_VERSION >= 8
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 void BrowserModule::OnOnScreenKeyboardShown(
     const base::OnScreenKeyboardShownEvent* event) {
   DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
@@ -982,16 +984,17 @@
   }
 }
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 void BrowserModule::OnCaptionSettingsChanged(
     const base::AccessibilityCaptionSettingsChangedEvent* /*event*/) {
   if (web_module_) {
     web_module_->InjectCaptionSettingsChangedEvent();
   }
 }
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 
 #if defined(ENABLE_DEBUGGER)
 void BrowserModule::OnFuzzerToggle(const std::string& message) {
@@ -1063,7 +1066,7 @@
     return;
   }
 
-  if (debug_console_->GetMode() == debug::console::DebugHub::kDebugConsoleOff) {
+  if (!debug_console_->IsVisible()) {
     // If the layer already has no render tree then simply return. In that case
     // nothing is changing.
     if (!debug_console_layer_->HasRenderTree()) {
@@ -1080,7 +1083,8 @@
 
 #endif  // defined(ENABLE_DEBUGGER)
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 void BrowserModule::OnOnScreenKeyboardInputEventProduced(
     base::Token type, const dom::InputEventInit& event) {
   TRACE_EVENT0("cobalt::browser",
@@ -1094,18 +1098,15 @@
   }
 
 #if defined(ENABLE_DEBUGGER)
-  // If the debug console is fully visible, it gets the next chance to handle
-  // input events.
-  if (debug_console_->GetMode() >= debug::console::DebugHub::kDebugConsoleOn) {
-    if (!debug_console_->InjectOnScreenKeyboardInputEvent(type, event)) {
-      return;
-    }
+  if (!debug_console_->FilterOnScreenKeyboardInputEvent(type, event)) {
+    return;
   }
 #endif  // defined(ENABLE_DEBUGGER)
 
   InjectOnScreenKeyboardInputEventToMainWebModule(type, event);
 }
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 void BrowserModule::OnKeyEventProduced(base::Token type,
                                        const dom::KeyboardEventInit& event) {
@@ -1136,14 +1137,9 @@
   }
 
 #if defined(ENABLE_DEBUGGER)
-  // If the debug console is fully visible, it gets the next chance to handle
-  // pointer events.
-  if (debug_console_->GetMode() >= debug::console::DebugHub::kDebugConsoleOn) {
-    if (!debug_console_->FilterPointerEvent(type, event)) {
-      return;
-    }
+  if (!debug_console_->FilterPointerEvent(type, event)) {
+    return;
   }
-
 #endif  // defined(ENABLE_DEBUGGER)
 
   DCHECK(web_module_);
@@ -1161,14 +1157,9 @@
   }
 
 #if defined(ENABLE_DEBUGGER)
-  // If the debug console is fully visible, it gets the next chance to handle
-  // wheel events.
-  if (debug_console_->GetMode() >= debug::console::DebugHub::kDebugConsoleOn) {
-    if (!debug_console_->FilterWheelEvent(type, event)) {
-      return;
-    }
+  if (!debug_console_->FilterWheelEvent(type, event)) {
+    return;
   }
-
 #endif  // defined(ENABLE_DEBUGGER)
 
   DCHECK(web_module_);
@@ -1190,7 +1181,8 @@
   web_module_->InjectKeyboardEvent(type, event);
 }
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 void BrowserModule::InjectOnScreenKeyboardInputEventToMainWebModule(
     base::Token type, const dom::InputEventInit& event) {
   TRACE_EVENT0(
@@ -1208,7 +1200,8 @@
   DCHECK(web_module_);
   web_module_->InjectOnScreenKeyboardInputEvent(type, event);
 }
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 void BrowserModule::OnError(const GURL& url, const std::string& error) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnError()");
@@ -1291,12 +1284,8 @@
   }
 
 #if defined(ENABLE_DEBUGGER)
-  // If the debug console is fully visible, it gets the next chance to handle
-  // key events.
-  if (debug_console_->GetMode() >= debug::console::DebugHub::kDebugConsoleOn) {
-    if (!debug_console_->FilterKeyEvent(type, event)) {
-      return false;
-    }
+  if (!debug_console_->FilterKeyEvent(type, event)) {
+    return false;
   }
 #endif  // defined(ENABLE_DEBUGGER)
 
@@ -1310,7 +1299,7 @@
   if (event.key_code() == dom::keycode::kF1 ||
       (event.ctrl_key() && event.key_code() == dom::keycode::kO)) {
     if (type == base::Tokens::keydown()) {
-      // Ctrl+O toggles the debug console display.
+      // F1 or Ctrl+O cycles the debug console display.
       debug_console_->CycleMode();
     }
     return false;
@@ -1319,6 +1308,12 @@
       // F5 reloads the page.
       Reload();
     }
+  } else if (event.ctrl_key() && event.key_code() == dom::keycode::kS) {
+    if (type == base::Tokens::keydown()) {
+      // Ctrl+S suspends Cobalt.
+      SbSystemRequestSuspend();
+    }
+    return false;
   }
 #endif  // defined(ENABLE_DEBUGGER)
 
@@ -1609,10 +1604,12 @@
       base::Bind(&BrowserModule::OnPointerEventProduced,
                  base::Unretained(this)),
       base::Bind(&BrowserModule::OnWheelEventProduced, base::Unretained(this)),
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
       base::Bind(&BrowserModule::OnOnScreenKeyboardInputEventProduced,
                  base::Unretained(this)),
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
       system_window_.get());
   InstantiateRendererModule();
 
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 0bc7d1a..b44418a 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -190,7 +190,8 @@
                            float video_pixel_ratio);
 #endif  // SB_API_VERSION >= 8
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   void OnOnScreenKeyboardShown(const base::OnScreenKeyboardShownEvent* event);
   void OnOnScreenKeyboardHidden(const base::OnScreenKeyboardHiddenEvent* event);
   void OnOnScreenKeyboardFocused(
@@ -201,12 +202,13 @@
   void OnOnScreenKeyboardSuggestionsUpdated(
       const base::OnScreenKeyboardSuggestionsUpdatedEvent* event);
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   void OnCaptionSettingsChanged(
       const base::AccessibilityCaptionSettingsChangedEvent* event);
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 
  private:
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
@@ -253,13 +255,15 @@
   // persist the user's preference.
   void SaveDebugConsoleMode();
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   // Glue function to deal with the production of an input event from an on
   // screen keyboard input device, and manage handing it off to the web module
   // for interpretation.
   void OnOnScreenKeyboardInputEventProduced(base::Token type,
                                             const dom::InputEventInit& event);
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
   // Glue function to deal with the production of a keyboard input event from a
   // keyboard input device, and manage handing it off to the web module for
@@ -278,12 +282,14 @@
   // interpretation.
   void OnWheelEventProduced(base::Token type, const dom::WheelEventInit& event);
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   // Injects an on screen keyboard input event directly into the main web
   // module.
   void InjectOnScreenKeyboardInputEventToMainWebModule(
       base::Token type, const dom::InputEventInit& event);
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
   // Injects a key event directly into the main web module, useful for setting
   // up an input fuzzer whose input should be sent directly to the main
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp
index ec30b50..8320f9b 100644
--- a/src/cobalt/browser/cobalt.gyp
+++ b/src/cobalt/browser/cobalt.gyp
@@ -15,6 +15,7 @@
 {
   'variables': {
     'sb_pedantic_warnings': 1,
+    'has_updater%' : '<!(python ../../build/file_exists.py <(DEPTH)/cobalt/updater/updater.gyp)',
   },
   'targets': [
     {
@@ -33,6 +34,11 @@
             '<(DEPTH)/cobalt/browser/splash_screen/splash_screen.gyp:copy_splash_screen',
           ],
         }],
+        ['sb_evergreen == 1 and has_updater == "True"', {
+          'dependencies': [
+            '<(DEPTH)/cobalt/updater/updater.gyp:updater',
+          ],
+        }],
       ],
     },
     {
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc
index b769196..56223d9 100644
--- a/src/cobalt/browser/debug_console.cc
+++ b/src/cobalt/browser/debug_console.cc
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "cobalt/browser/debug_console.h"
+
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
@@ -144,44 +145,55 @@
 
 DebugConsole::~DebugConsole() {}
 
+bool DebugConsole::ShouldInjectInputEvents() {
+  switch (GetMode()) {
+    case debug::console::DebugHub::kDebugConsoleOff:
+    case debug::console::DebugHub::kDebugConsoleHud:
+      return false;
+    default:
+      return true;
+  }
+}
+
 bool DebugConsole::FilterKeyEvent(base::Token type,
                                   const dom::KeyboardEventInit& event) {
-  // Assume here the full debug console is visible - pass all events to its
-  // web module, and return false to indicate the event has been consumed.
+  // Return true to indicate the event should still be handled.
+  if (!ShouldInjectInputEvents()) return true;
+
   web_module_->InjectKeyboardEvent(type, event);
   return false;
 }
 
 bool DebugConsole::FilterWheelEvent(base::Token type,
                                     const dom::WheelEventInit& event) {
-  // Assume here the full debug console is visible - pass all events to its
-  // web module, and return false to indicate the event has been consumed.
+  // Return true to indicate the event should still be handled.
+  if (!ShouldInjectInputEvents()) return true;
+
   web_module_->InjectWheelEvent(type, event);
   return false;
 }
 
 bool DebugConsole::FilterPointerEvent(base::Token type,
                                       const dom::PointerEventInit& event) {
-  // Assume here the full debug console is visible - pass all events to its
-  // web module, and return false to indicate the event has been consumed.
+  // Return true to indicate the event should still be handled.
+  if (!ShouldInjectInputEvents()) return true;
+
   web_module_->InjectPointerEvent(type, event);
   return false;
 }
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
-bool DebugConsole::InjectOnScreenKeyboardInputEvent(
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
+bool DebugConsole::FilterOnScreenKeyboardInputEvent(
     base::Token type, const dom::InputEventInit& event) {
-  // Assume here the full debug console is visible - pass all events to its
-  // web module, and return false to indicate the event has been consumed.
+  // Return true to indicate the event should still be handled.
+  if (!ShouldInjectInputEvents()) return true;
+
   web_module_->InjectOnScreenKeyboardInputEvent(type, event);
   return false;
 }
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
-
-void DebugConsole::SetMode(int mode) {
-  base::AutoLock lock(mode_mutex_);
-  mode_ = mode;
-}
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 void DebugConsole::CycleMode() {
   base::AutoLock lock(mode_mutex_);
diff --git a/src/cobalt/browser/debug_console.h b/src/cobalt/browser/debug_console.h
index a027803..bb1fe25 100644
--- a/src/cobalt/browser/debug_console.h
+++ b/src/cobalt/browser/debug_console.h
@@ -68,23 +68,26 @@
   // false if it was consumed within this function.
   bool FilterWheelEvent(base::Token type, const dom::WheelEventInit& event);
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
-  // Inject an on screen keyboard input event.
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
+  // Filters an on screen keyboard input event.
   // Returns true if the event should be passed on to other handlers,
   // false if it was consumed within this function.
-  bool InjectOnScreenKeyboardInputEvent(base::Token type,
+  bool FilterOnScreenKeyboardInputEvent(base::Token type,
                                         const dom::InputEventInit& event);
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
   const WebModule& web_module() const { return *web_module_; }
   WebModule& web_module() { return *web_module_; }
 
-  // Sets the debug console's visibility mode.
-  void SetMode(int mode);
   // Cycles through each different possible debug console visibility mode.
   void CycleMode();
-  // Returns the currently set debug console visibility mode.
-  int GetMode();
+
+  // Returns true iff the console is in a mode that is visible.
+  bool IsVisible() {
+    return (GetMode() != debug::console::DebugHub::kDebugConsoleOff);
+  }
 
   void SetSize(const cssom::ViewportSize& window_dimensions,
                float video_pixel_ratio) {
@@ -110,6 +113,13 @@
     LOG(ERROR) << error;
   }
 
+  // Returns the currently set debug console visibility mode.
+  int GetMode();
+
+  // Returns true iff the debug console is in a state where it should route
+  // input events to its web module.
+  bool ShouldInjectInputEvents();
+
   // The current console visibility mode.  The mutex is required since the debug
   // console's visibility mode may be accessed from both the WebModule thread
   // and the DebugConsole's host thread.
diff --git a/src/cobalt/browser/device_authentication.cc b/src/cobalt/browser/device_authentication.cc
index 7c8eeea..4a41510 100644
--- a/src/cobalt/browser/device_authentication.cc
+++ b/src/cobalt/browser/device_authentication.cc
@@ -146,6 +146,10 @@
   CHECK(!cert_scope.empty());
   CHECK(!start_time.empty());
 
+  if (base64_signature.empty()) {
+    return std::string();
+  }
+
   std::map<std::string, std::string> signed_query_components;
   signed_query_components["cert_scope"] = cert_scope;
   signed_query_components["start_time"] = start_time;
diff --git a/src/cobalt/browser/device_authentication_test.cc b/src/cobalt/browser/device_authentication_test.cc
index a820d94..5592d0c 100644
--- a/src/cobalt/browser/device_authentication_test.cc
+++ b/src/cobalt/browser/device_authentication_test.cc
@@ -140,6 +140,11 @@
           "yacs", "11111111", "11111111111111111111111111111111111111111111"));
 }
 
+TEST(DeviceAuthenticationTest, NoCertSignatureImpliesNoQueryParameters) {
+  EXPECT_EQ("", GetDeviceAuthenticationSignedURLQueryStringFromComponents(
+                    "my_cert_scope", "1234", ""));
+}
+
 #endif  // SB_API_VERSION >= 11
 
 }  // namespace browser
diff --git a/src/cobalt/browser/memory_settings/auto_mem_settings.cc b/src/cobalt/browser/memory_settings/auto_mem_settings.cc
index c0df087..55e0a7c 100644
--- a/src/cobalt/browser/memory_settings/auto_mem_settings.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem_settings.cc
@@ -26,13 +26,16 @@
 #include "base/strings/string_util.h"
 #include "cobalt/browser/memory_settings/constants.h"
 #include "cobalt/browser/switches.h"
+#include "starboard/blitter.h"
 
 namespace cobalt {
 namespace browser {
 namespace memory_settings {
 namespace {
 bool HasBlitter() {
-#if SB_HAS(BLITTER)
+#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+  const bool has_blitter = SbBlitterIsBlitterSupported();
+#elif SB_HAS(BLITTER)
   const bool has_blitter = true;
 #else
   const bool has_blitter = false;
diff --git a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc
index 47c92ea..bc2f657 100644
--- a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc
+++ b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc
@@ -19,9 +19,19 @@
 
 #include "starboard/event.h"
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 namespace cobalt {
 namespace browser {
+// static
+bool OnScreenKeyboardStarboardBridge::IsSupported() {
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION
+  return SbWindowOnScreenKeyboardIsSupported();
+#else
+  return true;
+#endif
+}
+
 void OnScreenKeyboardStarboardBridge::Show(const char* input_text, int ticket) {
   // Delay providing the SbWindow until as late as possible.
   SbWindowShowOnScreenKeyboard(sb_window_provider_.Run(), input_text, ticket);
@@ -105,4 +115,5 @@
 }
 }  // namespace browser
 }  // namespace cobalt
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
diff --git a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h
index 2b6bda0..1186f7d 100644
--- a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h
+++ b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h
@@ -21,7 +21,8 @@
 #include "cobalt/dom/on_screen_keyboard_bridge.h"
 #include "starboard/window.h"
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 namespace cobalt {
 namespace browser {
 
@@ -36,6 +37,8 @@
     DCHECK(!sb_window_provider_.is_null());
   }
 
+  static bool IsSupported();
+
   void Show(const char* input_text, int ticket) override;
 
   void Hide(int ticket) override;
@@ -63,5 +66,6 @@
 
 }  // namespace browser
 }  // namespace cobalt
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 #endif  // COBALT_BROWSER_ON_SCREEN_KEYBOARD_STARBOARD_BRIDGE_H_
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 0465c29..edb849b 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "cobalt/browser/switches.h"
+
 #include <map>
 
 namespace cobalt {
@@ -27,13 +28,20 @@
 const char kDebugConsoleModeHelp[] =
     "Switches different debug console modes: on | hud | off";
 
+const char kDevServersListenIp[] = "dev_servers_listen_ip";
+const char kDevServersListenIpHelp[] =
+    "IP address of the interface that internal development servers (remote web "
+    "debugger and WebDriver) listen on. If unspecified, INADDR_ANY (on most "
+    "platforms). Tip: To listen to ANY interface use \"::\" (\"0.0.0.0\" for "
+    "IPv4), and to listen to LOOPBACK use \"::1\" (\"127.0.0.1\" for IPv4)";
+
 #if defined(ENABLE_DEBUGGER)
 const char kRemoteDebuggingPort[] = "remote_debugging_port";
 const char kRemoteDebuggingPortHelp[] =
     "Remote web debugger is served from the specified port. If 0, then the "
     "remote web debugger is disabled.";
 
-    const char kWaitForWebDebugger[] = "wait_for_web_debugger";
+const char kWaitForWebDebugger[] = "wait_for_web_debugger";
 const char kWaitForWebDebuggerHelp[] =
     "Waits for remote web debugger to connect before loading the page.  A "
     "number may optionally be specified to indicate which in a sequence of "
@@ -184,17 +192,20 @@
 const char kWebDriverListenIp[] = "webdriver_listen_ip";
 const char kWebDriverListenIpHelp[] =
     "IP that the WebDriver server should be listening on. (INADDR_ANY if "
-    "unspecified).";
+    "unspecified). This is deprecated in favor of --dev_servers_listen_ip (if "
+    "both are specified, --webdriver_listen_ip is used).";
 
 const char kWebDriverPort[] = "webdriver_port";
 const char kWebDriverPortHelp[] =
     "Port that the WebDriver server should be listening on.";
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 const char kDisableOnScreenKeyboard[] = "disable_on_screen_keyboard";
 const char kDisableOnScreenKeyboardHelp[] =
     "Disable the on screen keyboard for testing.";
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
@@ -286,6 +297,12 @@
     "limit allows. It is recommended that enough memory be reserved for two "
     "RGBA atlases about a quarter of the frame size.";
 
+const char kOmitDeviceAuthenticationQueryParameters[] =
+    "omit_device_authentication_query_parameters";
+const char kOmitDeviceAuthenticationQueryParametersHelp[] =
+    "When set, no device authentication parameters will be appended to the"
+    "initial URL.";
+
 const char kProxy[] = "proxy";
 const char kProxyHelp[] =
     "Specifies a proxy to use for network connections. "
@@ -383,8 +400,10 @@
   std::map<const char*, const char*> help_map {
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
     {kDebugConsoleMode, kDebugConsoleModeHelp},
+        {kDevServersListenIp, kDevServersListenIpHelp},
 #if defined(ENABLE_DEBUGGER)
         {kWaitForWebDebugger, kWaitForWebDebuggerHelp},
+        {kRemoteDebuggingPort, kRemoteDebuggingPortHelp},
 #endif  // ENABLE_DEBUGGER
         {kDisableImageAnimations, kDisableImageAnimationsHelp},
         {kForceDeterministicRendering, kForceDeterministicRenderingHelp},
@@ -400,7 +419,6 @@
         {kMinCompatibilityVersion, kMinCompatibilityVersionHelp},
         {kMinLogLevel, kMinLogLevelHelp}, {kNullSavegame, kNullSavegameHelp},
         {kDisablePartialLayout, kDisablePartialLayoutHelp}, {kProd, kProdHelp},
-        {kRemoteDebuggingPort, kRemoteDebuggingPortHelp},
         {kRequireCSP, kRequireCSPHelp},
         {kRequireHTTPSLocation, kRequireHTTPSLocationHelp},
         {kShutdownAfter, kShutdownAfterHelp},
@@ -410,9 +428,11 @@
         {kUserAgentOsNameVersion, kUserAgentOsNameVersionHelp},
         {kUseTTS, kUseTTSHelp}, {kWebDriverListenIp, kWebDriverListenIpHelp},
         {kWebDriverPort, kWebDriverPortHelp},
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
         {kDisableOnScreenKeyboard, kDisableOnScreenKeyboardHelp},
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
         {kDisableJavaScriptJit, kDisableJavaScriptJitHelp},
@@ -431,6 +451,8 @@
         {kMaxCobaltGpuUsage, kMaxCobaltGpuUsageHelp},
         {kOffscreenTargetCacheSizeInBytes,
          kOffscreenTargetCacheSizeInBytesHelp},
+        {kOmitDeviceAuthenticationQueryParameters,
+         kOmitDeviceAuthenticationQueryParametersHelp},
         {kProxy, kProxyHelp}, {kQrCodeOverlay, kQrCodeOverlayHelp},
         {kReduceCpuMemoryBy, kReduceCpuMemoryByHelp},
         {kReduceGpuMemoryBy, kReduceGpuMemoryByHelp},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 6b3b163..9d400e7 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -26,6 +26,8 @@
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
 extern const char kDebugConsoleMode[];
 extern const char kDebugConsoleModeHelp[];
+extern const char kDevServersListenIp[];
+extern const char kDevServersListenIpHelp[];
 
 #if defined(ENABLE_DEBUGGER)
 extern const char kRemoteDebuggingPort[];
@@ -87,10 +89,12 @@
 extern const char kWebDriverPort[];
 extern const char kWebDriverPortHelp[];
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 extern const char kDisableOnScreenKeyboard[];
 extern const char kDisableOnScreenKeyboardHelp[];
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
 extern const char kDisableJavaScriptJit[];
@@ -121,6 +125,8 @@
 extern const char kMaxCobaltGpuUsageHelp[];
 extern const char kOffscreenTargetCacheSizeInBytes[];
 extern const char kOffscreenTargetCacheSizeInBytesHelp[];
+extern const char kOmitDeviceAuthenticationQueryParameters[];
+extern const char kOmitDeviceAuthenticationQueryParametersHelp[];
 extern const char kProxy[];
 extern const char kProxyHelp[];
 extern const char kQrCodeOverlay[];
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 43468e0..3b10308 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -29,6 +29,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/c_val.h"
+#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/base/language.h"
 #include "cobalt/base/startup_timer.h"
 #include "cobalt/base/tokens.h"
@@ -67,6 +68,7 @@
 #include "cobalt/storage/storage_manager.h"
 #include "starboard/accessibility.h"
 #include "starboard/common/log.h"
+#include "starboard/gles.h"
 
 #if defined(ENABLE_DEBUGGER)
 #include "cobalt/debug/backend/debug_module.h"
@@ -121,7 +123,8 @@
   }
 #endif  // ENABLE_DEBUGGER
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   // Injects an on screen keyboard input event into the web module. Event is
   // directed at a specific element if the element is non-null. Otherwise, the
   // currently focused element receives the event. If element is specified, we
@@ -146,7 +149,8 @@
   // module. Event is directed at the on screen keyboard element.
   void InjectOnScreenKeyboardSuggestionsUpdatedEvent(int ticket);
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
   // Injects a keyboard event into the web module. Event is directed at a
   // specific element if the element is non-null. Otherwise, the currently
@@ -496,13 +500,22 @@
   // Note that it is important that we do not parse map-to-mesh filters if we
   // cannot render them, since web apps may check for map-to-mesh support by
   // testing whether it parses or not via the CSS.supports() Web API.
-  css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh =
-#if defined(ENABLE_MAP_TO_MESH)
-      data.options.enable_map_to_mesh_rectangular
-          ? css_parser::Parser::kSupportsMapToMeshRectangular
-          : css_parser::Parser::kSupportsMapToMesh;
+  css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh;
+#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+  if (SbGetGlesInterface()) {
+    supports_map_to_mesh =
+        data.options.enable_map_to_mesh_rectangular
+            ? css_parser::Parser::kSupportsMapToMeshRectangular
+            : css_parser::Parser::kSupportsMapToMesh;
+  } else {
+    supports_map_to_mesh = css_parser::Parser::kDoesNotSupportMapToMesh;
+  }
+#elif defined(ENABLE_MAP_TO_MESH)
+  supports_map_to_mesh = data.options.enable_map_to_mesh_rectangular
+                             ? css_parser::Parser::kSupportsMapToMeshRectangular
+                             : css_parser::Parser::kSupportsMapToMesh;
 #else
-      css_parser::Parser::kDoesNotSupportMapToMesh;
+  supports_map_to_mesh = css_parser::Parser::kDoesNotSupportMapToMesh;
 #endif
 
   css_parser_ = css_parser::Parser::Create(supports_map_to_mesh);
@@ -602,10 +615,19 @@
 
   media_source_registry_.reset(new dom::MediaSource::Registry);
 
+  environment_settings_.reset(new dom::DOMSettings(
+      kDOMMaxElementDepth, fetcher_factory_.get(), data.network_module,
+      media_source_registry_.get(), blob_registry_.get(),
+      data.can_play_type_handler, javascript_engine_.get(),
+      global_environment_.get(), &debugger_hooks_,
+      &mutation_observer_task_manager_, data.options.dom_settings_options));
+  DCHECK(environment_settings_);
+
   media_session_client_ = media_session::MediaSessionClient::Create();
   media_session_client_->SetMediaPlayerFactory(data.web_media_player_factory);
 
-  system_caption_settings_ = new cobalt::dom::captions::SystemCaptionSettings();
+  system_caption_settings_ = new cobalt::dom::captions::SystemCaptionSettings(
+      environment_settings_.get());
 
   dom::Window::CacheCallback splash_screen_cache_callback =
       CacheUrlContentCallback(data.options.splash_screen_cache);
@@ -636,10 +658,10 @@
 #endif
 
   window_ = new dom::Window(
-      data.window_dimensions, data.video_pixel_ratio,
-      data.initial_application_state, css_parser_.get(), dom_parser_.get(),
-      fetcher_factory_.get(), loader_factory_.get(), &resource_provider_,
-      animated_image_tracker_.get(), image_cache_.get(),
+      environment_settings_.get(), data.window_dimensions,
+      data.video_pixel_ratio, data.initial_application_state, css_parser_.get(),
+      dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(),
+      &resource_provider_, animated_image_tracker_.get(), image_cache_.get(),
       reduced_image_cache_capacity_manager_.get(), remote_typeface_cache_.get(),
       mesh_cache_.get(), local_storage_database_.get(),
       data.can_play_type_handler, data.web_media_player_factory,
@@ -665,9 +687,8 @@
                  base::Unretained(this)),
       base::Bind(&WebModule::Impl::OnStopDispatchEvent, base::Unretained(this)),
       data.options.provide_screenshot_function, &synchronous_loader_interrupt_,
-      debugger_hooks_, data.ui_nav_root,
-      data.options.csp_insecure_allowed_token, data.dom_max_element_depth,
-      data.options.video_playback_rate_multiplier,
+      data.ui_nav_root, data.options.csp_insecure_allowed_token,
+      data.dom_max_element_depth, data.options.video_playback_rate_multiplier,
 #if defined(ENABLE_TEST_RUNNER)
       data.options.layout_trigger == layout::LayoutManager::kTestRunnerMode
           ? dom::Window::kClockTypeTestRunner
@@ -683,15 +704,7 @@
   window_weak_ = base::AsWeakPtr(window_.get());
   DCHECK(window_weak_);
 
-  environment_settings_.reset(new dom::DOMSettings(
-      kDOMMaxElementDepth, fetcher_factory_.get(), data.network_module, window_,
-      media_source_registry_.get(), blob_registry_.get(),
-      data.can_play_type_handler, javascript_engine_.get(),
-      global_environment_.get(), &mutation_observer_task_manager_,
-      data.options.dom_settings_options));
-  DCHECK(environment_settings_);
-
-  window_->SetEnvironmentSettings(environment_settings_.get());
+  environment_settings_->set_window(window_);
 
   global_environment_->CreateGlobalObject(window_, environment_settings_.get());
 
@@ -823,7 +836,8 @@
   }
 }
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 void WebModule::Impl::InjectOnScreenKeyboardInputEvent(
     scoped_refptr<dom::Element> element, base::Token type,
     const dom::InputEventInit& event) {
@@ -879,7 +893,8 @@
   window_->on_screen_keyboard()->DispatchSuggestionsUpdatedEvent(ticket);
 }
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 void WebModule::Impl::InjectKeyboardEvent(scoped_refptr<dom::Element> element,
                                           base::Token type,
@@ -1385,7 +1400,8 @@
   impl_.reset(new Impl(data));
 }
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 
 void WebModule::InjectOnScreenKeyboardInputEvent(
     base::Token type, const dom::InputEventInit& event) {
@@ -1460,7 +1476,8 @@
           base::Unretained(impl_.get()), ticket));
 }
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 void WebModule::InjectKeyboardEvent(base::Token type,
                                     const dom::KeyboardEventInit& event) {
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 7f8ad01..8c33295 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -286,7 +286,8 @@
             float layout_refresh_rate, const Options& options);
   ~WebModule();
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   // Injects an on screen keyboard input event into the web module. The value
   // for type represents beforeinput or input.
   void InjectOnScreenKeyboardInputEvent(base::Token type,
@@ -304,7 +305,8 @@
   // module.
   void InjectOnScreenKeyboardSuggestionsUpdatedEvent(int ticket);
 #endif  // SB_API_VERSION >= 11
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
   // Injects a keyboard event into the web module. The value for type
   // represents the event name, for example 'keydown' or 'keyup'.
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 15b5dc5..e0d3b20 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -16,6 +16,10 @@
 # default.
 
 {
+  'variables': {
+    'has_elf_loader%' : '<!(python ../../build/file_exists.py <(DEPTH)/starboard/elf_loader/elf_loader.gyp)',
+    'has_loader_app%' : '<!(python ../../build/file_exists.py <(DEPTH)/starboard/loader_app/loader_app.gyp)',
+  },
   'targets': [
     {
       'target_name': 'Default',
@@ -30,7 +34,7 @@
       'target_name': 'All',
       'type': 'none',
       'dependencies': [
-        '<(DEPTH)/base/base.gyp:base_unittests',
+        '<(DEPTH)/base/base.gyp:base_unittests_deploy',
         '<(DEPTH)/cobalt/audio/audio.gyp:*',
         '<(DEPTH)/cobalt/audio/audio_test.gyp:*',
         '<(DEPTH)/cobalt/base/base.gyp:*',
@@ -84,20 +88,34 @@
         '<(DEPTH)/cobalt/webdriver/webdriver_test.gyp:*',
         '<(DEPTH)/cobalt/websocket/websocket.gyp:*',
         '<(DEPTH)/cobalt/xhr/xhr.gyp:*',
-        '<(DEPTH)/crypto/crypto.gyp:crypto_unittests',
+        '<(DEPTH)/crypto/crypto.gyp:crypto_unittests_deploy',
         '<(DEPTH)/third_party/boringssl/boringssl_tool.gyp:*',
-        '<(DEPTH)/net/net.gyp:net_unittests',
-        '<(DEPTH)/sql/sql.gyp:sql_unittests',
-        '<(DEPTH)/starboard/elf_loader/elf_loader.gyp:elf_loader_test',
+        '<(DEPTH)/net/net.gyp:net_unittests_deploy',
+        '<(DEPTH)/sql/sql.gyp:sql_unittests_deploy',
       ],
       'conditions': [
+        ['has_elf_loader == "True"', {
+          'dependencies': [
+            '<(DEPTH)/starboard/elf_loader/elf_loader.gyp:elf_loader_test_deploy',
+          ],
+        }],
+        ['has_loader_app == "True"', {
+          'dependencies': [
+            '<(DEPTH)/starboard/loader_app/loader_app.gyp:*',
+          ],
+        }],
         ['OS=="starboard"', {
           'dependencies': [
-            '<(DEPTH)/nb/nb_test.gyp:nb_test',
+            '<(DEPTH)/nb/nb_test.gyp:nb_test_deploy',
             '<(DEPTH)/nb/nb_test.gyp:reuse_allocator_benchmark',
             '<(DEPTH)/starboard/starboard_all.gyp:starboard_all',
           ],
         }],
+        ['sb_evergreen==1', {
+          'dependencies': [
+            '<(DEPTH)/third_party/musl/musl.gyp:musl_unittests',
+          ],
+        }],
       ],
     },
   ],
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 8a3eeb1..a86f8a5 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-224424
\ No newline at end of file
+234144
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_archive.py b/src/cobalt/build/cobalt_archive.py
deleted file mode 100644
index e91dfe7..0000000
--- a/src/cobalt/build/cobalt_archive.py
+++ /dev/null
@@ -1,533 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""Tools for creating and extracting a Cobalt Archive."""
-
-import argparse
-import fnmatch
-import hashlib
-import json
-import logging
-import os
-import random
-import stat
-import sys
-import time
-import zipfile
-
-import _env  # pylint: disable=relative-import,unused-import
-from cobalt.build import cobalt_archive_extract
-import starboard.build.filelist as filelist
-import starboard.build.port_symlink as port_symlink
-from starboard.tools.app_launcher_packager import CopyAppLauncherTools
-from starboard.tools.build import GetPlatformConfig
-from starboard.tools.config import GetAll as GetAllConfigs
-import starboard.tools.paths as paths
-from starboard.tools.platform import GetAll as GetAllPlatforms
-from starboard.tools.util import SetupDefaultLoggingConfig
-
-
-################################################################################
-#                                  API                                         #
-################################################################################
-
-
-def MakeCobaltArchiveFromFileList(output_archive_path,
-                                  input_file_list,  # class FileList
-                                  platform_name,
-                                  platform_sdk_version,
-                                  config,
-                                  additional_buildinfo_dict=None):
-  if additional_buildinfo_dict is None:
-    additional_buildinfo_dict = {}
-  archive = CobaltArchive(archive_zip_path=output_archive_path)
-  archive.MakeArchive(platform_name=platform_name,
-                      platform_sdk_version=platform_sdk_version,
-                      config=config,
-                      file_list=input_file_list,
-                      additional_buildinfo_dict=additional_buildinfo_dict)
-
-
-def MakeCobaltArchiveFromSource(output_archive_path,
-                                platform_name,
-                                config,
-                                platform_sdk_version,
-                                additional_buildinfo_dict=None,
-                                include_black_box_tests=False):
-  """Returns None, failure is signaled via exception."""
-  if additional_buildinfo_dict is None:
-    additional_buildinfo_dict = {}
-  _MakeCobaltArchiveFromSource(
-      output_archive_path=output_archive_path,
-      platform_name=platform_name,
-      config=config,
-      platform_sdk_version=platform_sdk_version,
-      additional_buildinfo_dict=additional_buildinfo_dict,
-      include_black_box_tests=include_black_box_tests)
-
-
-def ExtractCobaltArchive(input_zip_path,
-                         output_directory_path,
-                         outstream=None):
-  """Returns True if the extract operation was successfull."""
-  archive = CobaltArchive(archive_zip_path=input_zip_path)
-  return archive.ExtractTo(output_dir=output_directory_path,
-                           outstream=outstream)
-
-
-def ReadCobaltArchiveInfo(input_zip_path):
-  archive = CobaltArchive(archive_zip_path=input_zip_path)
-  return archive.ReadMetaData()
-
-
-################################################################################
-#                                 IMPL                                         #
-################################################################################
-
-
-# Source resource paths.
-_SELF_DIR = os.path.abspath(os.path.dirname(__file__))
-_SRC_CONTENT_PATH = os.path.join(_SELF_DIR, 'cobalt_archive_content')
-
-
-# Relative paths from the resulting archive root. The path seperator
-# is normalized to '/'.
-_OUT_ARCHIVE_ROOT = '__cobalt_archive'
-_OUT_FINALIZE_DECOMPRESSION_PATH = '%s/%s' % (_OUT_ARCHIVE_ROOT,
-                                              'finalize_decompression')
-_OUT_METADATA_PATH = '%s/%s' % (_OUT_ARCHIVE_ROOT, 'metadata.json')
-_OUT_DECOMP_JSON = '%s/%s' % (_OUT_FINALIZE_DECOMPRESSION_PATH,
-                              'decompress.json')
-
-
-class CobaltArchive(object):
-  """CobaltArchive is a utility generating archives."""
-
-  def __init__(self, archive_zip_path):
-    self.archive_zip_path = archive_zip_path
-
-  def ExtractTo(self, output_dir, outstream=None):
-    """Returns True if all files were extracted, False otherwise."""
-    return cobalt_archive_extract.ExtractTo(
-        self.archive_zip_path, output_dir, outstream)
-
-  def ReadMetaData(self):
-    json_str = self.ReadFile(_OUT_METADATA_PATH)
-    return json.loads(json_str)
-
-  def ReadFile(self, file_path):
-    with zipfile.ZipFile(self.archive_zip_path, 'r', allowZip64=True) as zf:
-      return zf.read(file_path)
-
-  def MakeArchive(self,
-                  platform_name,
-                  platform_sdk_version,
-                  config,
-                  file_list,  # class FileList
-                  additional_buildinfo_dict=None):
-    """Creates an archive for the given platform and config."""
-    logging.info('Making cobalt archive...')
-    is_windows = port_symlink.IsWindows()
-    if additional_buildinfo_dict is None:
-      additional_buildinfo_dict = {}
-    if config not in GetAllConfigs():
-      raise ValueError('Expected %s to be one of %s'
-                       % (config, GetAllConfigs()))
-    additional_buildinfo_dict = dict(additional_buildinfo_dict)  # Copy
-    build_info_str = _GenerateBuildInfoStr(
-        platform_name=platform_name,
-        platform_sdk_version=platform_sdk_version,
-        config=config,
-        additional_buildinfo_dict=additional_buildinfo_dict)
-    with zipfile.ZipFile(self.archive_zip_path, mode='w',
-                         compression=zipfile.ZIP_DEFLATED,
-                         allowZip64=True) as zf:
-      # Copy the cobalt_archive_content directory into the root of the archive.
-      content_file_list = filelist.FileList()
-      content_file_list.AddAllFilesInPath(root_dir=_SRC_CONTENT_PATH,
-                                          sub_path=_SRC_CONTENT_PATH)
-      for file_path, archive_path in content_file_list.file_list:
-        # Skip the fake metadata.json file because the real one
-        # is a generated in it's place.
-        if os.path.basename(file_path) == 'metadata.json':
-          continue
-        zf.write(file_path, arcname=archive_path)
-      # Write out the metadata.
-      zf.writestr(_OUT_METADATA_PATH, build_info_str)
-      if file_list.file_list:
-        logging.info('  Compressing %d files', len(file_list.file_list))
-
-      executable_files = []
-      n_file_list = len(file_list.file_list)
-      progress_set = set()
-      for i in range(n_file_list):
-        # Logging every 5% increment during compression step.
-        prog = int((float(i)/n_file_list) * 100)
-        if prog not in progress_set:
-          progress_set.add(prog)
-          logging.info('  Compressed %d%%...', prog)
-        file_path, archive_path = file_list.file_list[i]
-        if not is_windows:
-          perms = _GetFilePermissions(file_path)
-          if (stat.S_IXUSR) & perms:
-            executable_files.append(archive_path)
-        # TODO: Use and implement _FoldIdenticalFiles() to reduce
-        # duplicate files. This will help platforms like nxswitch which include
-        # a lot of duplicate files for the sdk.
-        try:
-          zf.write(file_path, arcname=archive_path)
-        except WindowsError:  # pylint: disable=undefined-variable
-          # Happens for long file path names.
-          zf.write(cobalt_archive_extract.ToWinUncPath(file_path),
-                   arcname=archive_path)
-
-      if file_list.symlink_dir_list:
-        logging.info('  Compressing %d symlinks',
-                     len(file_list.symlink_dir_list))
-      # Generate the decompress.json file used by decompress.py.
-      # Removes the first element which is the root directory, which is not
-      # important for symlink creation.
-      symlink_dir_list = [l[1:] for l in file_list.symlink_dir_list]
-      # Replace '\\' with '/'
-      symlink_dir_list = [_ToUnixPaths(l) for l in symlink_dir_list]
-      decompress_json_str = _JsonDumpPrettyPrint({
-          'symlink_dir': symlink_dir_list,
-          'symlink_dir_doc': '[link_dir_path, target_dir_path]',
-          'executable_files': executable_files,
-      })
-      zf.writestr(_OUT_DECOMP_JSON, decompress_json_str)
-      logging.info('Done...')
-
-
-def _ToUnixPaths(path_list):
-  out = []
-  for p in path_list:
-    out.append(p.replace('\\', '/'))
-  return out
-
-
-def _GetFilePermissions(path):
-  return stat.S_IMODE(os.stat(path).st_mode)
-
-
-def _JsonDumpPrettyPrint(data):
-  return json.dumps(data, sort_keys=True, indent=4, separators=(',', ': '))
-
-
-def _MakeDirs(path):
-  if not os.path.isdir(path):
-    os.makedirs(path)
-
-
-def _FindPossibleDeployPaths(build_root):
-  """Searches for folders that are likely required for archiving."""
-  out = []
-  # Ultimately, this function should not be needed as each platform should
-  # implement GetDeployPathPatterns(). This is stop-gap for platforms that do
-  # not have GetDeployPathPatterns() implemented yet.
-  root_paths = os.listdir(build_root)
-  for p in root_paths:
-    if p in ('gen', 'gypfiles', 'gyp-win-tool', 'obj', 'obj.host'):
-      continue
-    if p.endswith('.pdb'):
-      continue  # Skip pdb files for size (only applies to Windows).
-    p = os.path.join(build_root, p)
-    if os.path.isfile(p):
-      out.append(p)
-      continue
-    if port_symlink.IsSymLink(p):
-      continue
-    out.append(os.path.normpath(p))
-  return out
-
-
-def _PathMatchesPatterns(file_path, patterns):
-  for p in patterns:
-    if fnmatch.fnmatch(file_path, p):
-      logging.debug('pattern %s matched %s', p, file_path)
-      return True
-  logging.debug('Skipping %s', file_path)
-  return False
-
-
-def _GetDeployPaths(platform_name, config):
-  """Returns a list of paths that should be included in the archive."""
-  try:
-    gyp_config = GetPlatformConfig(platform_name)
-    patterns = gyp_config.GetDeployPathPatterns()
-    logging.info('Found platform include patterns: [%s]', ', '.join(patterns))
-    out_directory = paths.BuildOutputDirectory(platform_name, config)
-    out_paths = []
-    for root, _, files in port_symlink.OsWalk(out_directory):
-      for f in files:
-        full_path = os.path.join(root, f)
-        file_path = os.path.relpath(full_path, out_directory)
-        if _PathMatchesPatterns(file_path, patterns):
-          out_paths.append(file_path)
-    return out_paths
-  except NotImplementedError:  # Abstract class throws NotImplementedError.
-    logging.warning('** AUTO INCLUDE: ** Specific deploy paths were not found '
-                    'so including known possible deploy paths from the '
-                    'platform out directory.')
-    build_root = paths.BuildOutputDirectory(platform_name, config)
-    deploy_paths = _FindPossibleDeployPaths(build_root)
-    return deploy_paths
-
-
-def _MakeCobaltArchiveFromSource(output_archive_path,
-                                 platform_name,
-                                 config,
-                                 platform_sdk_version,
-                                 additional_buildinfo_dict,
-                                 include_black_box_tests):
-  """Finds necessary files and makes an archive."""
-  _MakeDirs(os.path.dirname(output_archive_path))
-  out_directory = paths.BuildOutputDirectory(platform_name, config)
-  root_dir = os.path.abspath(
-      os.path.normpath(os.path.join(out_directory, '..', '..')))
-  flist = filelist.FileList()
-  inc_paths = _GetDeployPaths(platform_name, config)
-  logging.info('Adding binary files to bundle...')
-  for path in inc_paths:
-    path = os.path.join(out_directory, path)
-    if not os.path.exists(path):
-      logging.info('Skipping deploy path %s because it does not exist.',
-                   path)
-      continue
-    logging.info('  adding %s', os.path.abspath(path))
-    flist.AddAllFilesInPath(root_dir=root_dir, sub_path=path)
-  logging.info('...done')
-  launcher_tools_path = os.path.join(
-      os.path.dirname(output_archive_path),
-      '____app_launcher')
-  if os.path.exists(launcher_tools_path):
-    port_symlink.Rmtree(launcher_tools_path)
-  logging.info('Adding app_launcher_files to bundle in %s',
-               os.path.abspath(launcher_tools_path))
-
-  try:
-    CopyAppLauncherTools(repo_root=paths.REPOSITORY_ROOT,
-                         dest_root=launcher_tools_path,
-                         additional_glob_patterns=[],
-                         include_black_box_tests=include_black_box_tests)
-    flist.AddAllFilesInPath(root_dir=launcher_tools_path,
-                            sub_path=launcher_tools_path)
-    logging.info('...done')
-
-    MakeCobaltArchiveFromFileList(
-        output_archive_path,
-        input_file_list=flist,
-        platform_name=platform_name,
-        platform_sdk_version=platform_sdk_version,
-        config=config,
-        additional_buildinfo_dict=additional_buildinfo_dict)
-    logging.info('...done')
-  finally:
-    port_symlink.Rmtree(launcher_tools_path)
-
-
-def _FoldIdenticalFiles(file_path_list):
-  """Takes input files and determines which are md5 identical and folds them.
-
-  TODO: Implement into Cobalt Archive.
-
-  Example:
-    files, copy_list = _FoldIdenticalFiles(['in0/test.txt', 'in1/test.txt'])
-    Output:
-      files => ['in0/test.txt']
-      copy_list => ['in0/test.txt', 'in1/test.txt']
-
-  Args:
-    file_path_list: A list of files that will be processed.
-
-  Returns:
-    A 2-tuple (files, copy_list) where files is a list of physical files and
-    copy_list is the list of files that are identical.
-  """
-  # Remove duplicates.
-  file_path_list = list(set(file_path_list))
-  file_path_list.sort()
-  def Md5File(fpath):
-    hash_md5 = hashlib.md5()
-    with open(fpath, 'rb') as f:
-      for chunk in iter(lambda: f.read(4096), b''):
-        hash_md5.update(chunk)
-    return hash_md5.hexdigest()
-  # Output
-  phy_file_list = []
-  copy_list = []
-  # Temp data structure.
-  file_map = {}
-  for file_path in file_path_list:
-    name = os.path.basename(file_path)
-    fsize = os.stat(file_path).st_size
-    entry = (name, fsize)
-    files = file_map.get(entry, [])
-    files.append(file_path)
-    file_map[entry] = files
-  for (fname, fsize), path_list in file_map.iteritems():  # pylint: disable=unused-variable
-    assert path_list
-    phy_file_list.append(path_list[0])
-    if len(path_list) == 1:
-      continue
-    else:
-      md5_dict = {Md5File(path_list[0]): path_list[0]}
-      for tail_file in path_list[1:]:
-        new_md5 = Md5File(tail_file)
-        matching_file = md5_dict.get(new_md5, None)
-        if matching_file is not None:
-          # Match found.
-          copy_list.append((matching_file, tail_file))
-        else:
-          phy_file_list.append(tail_file)
-          md5_dict[new_md5] = tail_file
-  return phy_file_list, copy_list
-
-
-def _GenerateBuildInfoStr(platform_name, platform_sdk_version,
-                          config, additional_buildinfo_dict):
-  """Generates a build info string (for the metadata file)."""
-  build_info = dict(additional_buildinfo_dict)  # Copy dict.
-  build_info['archive_time_RFC_2822'] = (
-      time.strftime('%a, %d %b %Y %H:%M:%S +0000', time.gmtime()))
-  build_info['archive_time_local'] = time.asctime()
-  build_info['platform'] = platform_name
-  build_info['config'] = config
-  build_info['sdk_version'] = platform_sdk_version
-  # Can be used by clients for caching reasons.
-  build_info['nonce'] = random.randint(0, 0xffffffffffffffff)
-  build_info_str = _JsonDumpPrettyPrint(build_info)
-  return build_info_str
-
-
-################################################################################
-#                                CMD LINE                                      #
-################################################################################
-
-
-def _MakeCobaltPlatformArchive(platform, config, output_zip,
-                               include_black_box_tests):
-  """Makes a Cobalt Archive, prompting for missing platform/config."""
-  if not platform:
-    platform = raw_input('platform: ')
-  if platform not in GetAllPlatforms():
-    raise ValueError('Platform "%s" not recognized, expected one of: \n%s'
-                     % (platform, GetAllPlatforms()))
-  if not config:
-    config = raw_input('config: ')
-  if not output_zip:
-    output_zip = os.path.normpath(raw_input('output_zip: '))
-  if not output_zip.endswith('.zip'):
-    output_zip += '.zip'
-  start_time = time.time()
-  MakeCobaltArchiveFromSource(
-      output_zip,
-      platform,
-      config,
-      platform_sdk_version='TEST',
-      additional_buildinfo_dict=None,
-      include_black_box_tests=include_black_box_tests)
-  time_delta = time.time() - start_time
-  if not os.path.isfile(output_zip):
-    raise ValueError('Expected zip file at ' + output_zip)
-  logging.info('\nGenerated: %s in %d seconds', output_zip, int(time_delta))
-
-
-# Returns True/False
-def _DecompressArchive(in_zip, out_path):
-  if not in_zip:
-    in_zip = raw_input('cobalt archive path: ')
-  if not out_path:
-    out_path = raw_input('output path: ')
-  return ExtractCobaltArchive(input_zip_path=in_zip,
-                              output_directory_path=out_path)
-
-
-def _CreateArgumentParser():
-  """Creates a parser that will print the full help on failure to parse."""
-
-  class MyParser(argparse.ArgumentParser):
-
-    def error(self, message):
-      sys.stderr.write('error: %s\n' % message)
-      self.print_help()
-      sys.exit(2)
-  help_msg = (
-      'Example 1:\n'
-      '  python cobalt_archive.py --create --platform nxswitch'
-      ' --config devel --out_path <OUT_ZIP>\n\n'
-      'Example 2:\n'
-      '  python cobalt_archive.py --extract --in_path <ARCHIVE_PATH.ZIP>'
-      ' --out_path <OUT_DIR>')
-  # Enables new lines in the description and epilog.
-  formatter_class = argparse.RawDescriptionHelpFormatter
-  parser = MyParser(epilog=help_msg, formatter_class=formatter_class)
-  group = parser.add_mutually_exclusive_group(required=True)
-  group.add_argument(
-      '-c',
-      '--create',
-      help='Creates an archive from source directory, optional arguments'
-           ' include --platform --config and --out_path',
-      action='store_true')
-  group.add_argument(
-      '-x',
-      '--extract',
-      help='Extract archive from IN_PATH to OUT_PATH, optional arguments '
-           'include --in_path and --out_path',
-      action='store_true')
-  parser.add_argument('--platform', type=str,
-                      help='Optional, used for --create',
-                      default=None)
-  parser.add_argument('--config', type=str,
-                      help='Optional, used for --create',
-                      choices=GetAllConfigs(),
-                      default=None)
-  parser.add_argument('--out_path', type=str,
-                      help='Optional, used for --create and --decompress',
-                      default=None)
-  parser.add_argument('--in_path', type=str,
-                      help='Optional, used for decompress',
-                      default=None)
-  parser.add_argument('--include_black_box_tests',
-                      help='Optional, used for --create to add blackbox tests',
-                      action='store_true')
-  return parser
-
-
-def main():
-  SetupDefaultLoggingConfig()
-  parser = _CreateArgumentParser()
-  args, unknown_args = parser.parse_known_args()
-  if unknown_args:
-    logging.warning('Unknown (ignored) args: %s', unknown_args)
-  if args.create:
-    _MakeCobaltPlatformArchive(
-        platform=args.platform,
-        config=args.config,
-        output_zip=os.path.normpath(args.out_path),
-        include_black_box_tests=args.include_black_box_tests)
-    sys.exit(0)
-  elif args.extract:
-    ok = _DecompressArchive(args.in_path, args.out_path)
-    rc = 0 if ok else 1
-    sys.exit(rc)
-  else:
-    parser.print_help()
-
-
-if __name__ == '__main__':
-  main()
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/finalize_decompression/decompress.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/finalize_decompression/decompress.py
deleted file mode 100644
index 3f34428..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/finalize_decompression/decompress.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""Finalizes decompression.
-
-This is meant to be run after the zip is decompressed into the temp directory
-and includes support for special file system operations not supported by the
-zip file.
-"""
-
-
-import json
-import logging
-import os
-import subprocess
-import sys
-
-
-_SELF_DIR = os.path.dirname(__file__)
-_ARCHIVE_ROOT = os.path.join(_SELF_DIR, os.pardir, os.pardir)
-_DATA_JSON_PATH = os.path.abspath(os.path.join(_SELF_DIR, 'decompress.json'))
-_FULL_PERMISSIONS = 0o777
-
-
-_IS_WINDOWS = sys.platform in ['win32', 'cygwin']
-
-
-def _CreateWin32Symlink(source, link_name):
-  rc = subprocess.call('mklink /D %s %s' % (link_name, source), shell=True)
-  if rc != 0:
-    # Some older versions of windows require admin permissions for /D style
-    # reparse points. In this case fallback to using /J.
-    cmd = 'mklink /J %s %s' % (link_name, source),
-    rc = subprocess.call(cmd, shell=True)
-    if rc != 0:
-      logging.critical('Error using %s during %s, cwd=%s', rc, cmd, os.getcwd())
-
-
-def _CreateSymlink(source, link_name):
-  if _IS_WINDOWS:
-    _CreateWin32Symlink(source, link_name)
-  else:
-    os.symlink(source, link_name)
-
-
-def _MakeDirs(path):
-  if _IS_WINDOWS:
-    # Necessary for long file name support
-    subprocess.check_call('mkdir %s' % path, shell=True)
-  else:
-    os.makedirs(path)
-
-
-def _ExtractSymlinks(archive_root, symlink_dir_list):
-  """Recreates symlinks on Windows and linux."""
-  archive_root = os.path.normpath(archive_root)
-  if _IS_WINDOWS:
-    archive_root = '\\\\?\\' + archive_root
-  for link_path, real_path in symlink_dir_list:
-    # link_path and real_path are assumed to be both relative paths.
-    real_path = os.path.normpath(real_path)
-    link_path = os.path.normpath(link_path)
-    target_path = os.path.relpath(real_path, os.path.dirname(link_path))
-    link_path = os.path.join(archive_root, link_path)
-    if not os.path.exists(os.path.dirname(link_path)):
-      _MakeDirs(os.path.dirname(link_path))
-    _CreateSymlink(target_path, link_path)
-  # Check that all the symlinks point to an existing directory.
-  all_ok = True
-  cwd = os.getcwd()
-  for link_path, _ in symlink_dir_list:
-    link_path = os.path.join(archive_root, link_path)
-    try:
-      # This will raise an error if the link points to an invalid directory.
-      os.chdir(link_path)
-    except:
-      all_ok = False
-    finally:
-      os.chdir(cwd)
-  if not all_ok:
-    logging.critical('\n*******************************************'
-                     '\nErrors happended during symlink extraction.'
-                     '\n*******************************************')
-
-
-def _SetExecutionBits(cwd, executable_files):
-  if not executable_files:
-    return
-  logging.info('Setting Permissions %s on %d files',
-               _FULL_PERMISSIONS, len(executable_files))
-  for f in executable_files:
-    full_path = os.path.abspath(os.path.join(cwd, f))
-    logging.info('  %s', full_path)
-    os.chmod(full_path, _FULL_PERMISSIONS)
-
-
-def main():
-  logging.basicConfig(level=logging.INFO,
-                      format='%(filename)s(%(lineno)s): %(message)s')
-  assert(os.path.exists(_DATA_JSON_PATH)), _DATA_JSON_PATH
-  with open(_DATA_JSON_PATH) as fd:
-    json_str = fd.read()
-  data = json.loads(json_str)
-  _ExtractSymlinks(_ARCHIVE_ROOT, data.get('symlink_dir', []))
-  _SetExecutionBits(_ARCHIVE_ROOT, data.get('executable_files', []))
-
-
-if __name__ == '__main__':
-  main()
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/metadata.json b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/metadata.json
deleted file mode 100644
index 632f04c..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/metadata.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-    "comment": "TEST",
-    "archive_time_RFC_2822": "Tue, 30 Apr 2019 00:14:18 +0000",
-    "archive_time_local": "Tue Apr 30 00:14:18 2019",
-    "config": "CONFIG_STRING",
-    "platform": "PLATFORM_STRING",
-    "random_uint64": 0,
-    "sdk_version": "any"
-}
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/__init__.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/__init__.py
deleted file mode 100644
index dbab2d5..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/__init__.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/__init__.py
deleted file mode 100644
index dbab2d5..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/run_test.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/run_test.py
deleted file mode 100644
index b1a300d..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/run_test.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""Unit tests the run.py trampoline."""
-
-import os
-import sys
-import unittest
-
-sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
-import run  # pylint: disable=relative-import,g-bad-import-order,g-import-not-at-top
-
-
-class RunGeneralTrampolineTest(unittest.TestCase):
-  """Tests trampoline substitutions for run_cobalt.py."""
-
-  def setUp(self):
-    """Change the trampoline internals for testing purposes."""
-    super(RunGeneralTrampolineTest, self).setUp()
-    run.trampoline.PLATFORM = 'MY_PLATFORM'
-    run.trampoline.CONFIG = 'MY_CONFIG'
-
-  def testOneTarget(self):
-    """Tests that one target gets resolved."""
-    expected_output = (
-        'python starboard/tools/example/app_launcher_client.py'
-        ' --platform MY_PLATFORM --config MY_CONFIG'
-        ' --target_name cobalt')
-    cmd_str = run._ResolveTrampoline(argv=['cobalt'])
-    self.assertEqual(expected_output, cmd_str)
-
-  def testTwoTargets(self):
-    """Tests that two targets gets resolved."""
-    expected_output = (
-        'python starboard/tools/example/app_launcher_client.py'
-        ' --platform MY_PLATFORM --config MY_CONFIG'
-        ' --target_name cobalt --target_name nplb')
-    cmd_str = run._ResolveTrampoline(argv=['cobalt', 'nplb'])
-    self.assertEqual(expected_output, cmd_str)
-
-  def testTargetParams(self):
-    """Tests that the the target_params gets correctly resolved."""
-    expected_output = (
-        'python starboard/tools/example/app_launcher_client.py'
-        ' --platform MY_PLATFORM --config MY_CONFIG'
-        ' --target_params="--url=http://my.server.test"')
-    argv = ['--target_params', '"--url=http://my.server.test"']
-    cmd_str = run._ResolveTrampoline(argv=argv)
-    self.assertEqual(expected_output, cmd_str)
-
-
-if __name__ == '__main__':
-  unittest.main(verbosity=2)
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/run_tests_test.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/run_tests_test.py
deleted file mode 100644
index cffc18d..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/run_tests_test.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""Unit tests the run.py trampoline."""
-
-import os
-import sys
-import unittest
-
-sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
-import run_tests  # pylint: disable=relative-import,g-bad-import-order,g-import-not-at-top
-
-
-class RunUnitTestsTrampolineTest(unittest.TestCase):
-  """Tests trampoline substitutions for run_unit_tests.py."""
-
-  def setUp(self):
-    """Change the trampoline internals for testing purposes."""
-    super(RunUnitTestsTrampolineTest, self).setUp()
-    run_tests.trampoline.PLATFORM = 'MY_PLATFORM'
-    run_tests.trampoline.CONFIG = 'MY_CONFIG'
-
-  def testOne(self):
-    """Tests that --target_name resolves to the expected value."""
-    expected_output = (
-        'python starboard/tools/testing/test_runner.py'
-        ' --target_name nplb --platform MY_PLATFORM --config MY_CONFIG')
-    cmd_str = run_tests._ResolveTrampoline(argv=['--target_name', 'nplb'])
-    self.assertEqual(expected_output, cmd_str)
-
-  def testTwo(self):
-    """Tests that --target_name used twice resolves to the expected value."""
-    expected_output = (
-        'python starboard/tools/testing/test_runner.py'
-        ' --platform MY_PLATFORM --config MY_CONFIG'
-        ' --target_name nplb --target_name nb_test')
-    argv=['nplb', 'nb_test']
-    cmd_str = run_tests._ResolveTrampoline(argv=argv)
-    self.assertEqual(expected_output, cmd_str)
-
-
-if __name__ == '__main__':
-  unittest.main(verbosity=2)
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/trampoline.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/trampoline.py
deleted file mode 100644
index 059c1e3..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/trampoline.py
+++ /dev/null
@@ -1,165 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""Trampoline resolves a trampoline to a command string."""
-
-
-import argparse
-import json
-import os
-import subprocess
-import sys
-
-
-################################################################################
-#                                  API                                         #
-################################################################################
-
-
-def RunTrampolineThenExit(trampoline, argv=None):
-  cmd_str = ResolveTrampoline(trampoline, argv)
-  sys.exit(_ShellCmd(cmd_str))
-
-
-def ResolveTrampoline(trampoline, argv=None):
-  r"""Resolves the trampoline list and returns a fully resolve strings.
-
-  The result of this function call is to return the cmd string with
-  the platform, config, device_id resolved to values.
-    Example input:
-      ['python starboard/tools/example/app_launcher_client.py -t cobalt',
-       '{platform_arg}', '{config_arg}', '{device_id_arg}']
-    Example Output:
-      'python starboard/tools/example/app_launcher_client.py -t cobalt ' + \
-      '--platform linux --config devel --device_id IP_ADDRESS'
-
-  Args:
-    trampoline: a list of commands mixed in with unresolved symobls.
-    argv: a list of known resolves symbols to use in the trampoline.
-
-  Returns:
-    A string representing the resolved shell command.
-  """
-  return _ResolveTrampoline(trampoline, argv)
-
-
-def RunThenExit(cmd_str, cwd=None):
-  if cwd == None:
-    cwd = _FindCwd()
-  sys.exit(_ShellCmd(cmd_str, cwd=cwd))
-
-
-################################################################################
-#                                 IMPL                                         #
-################################################################################
-
-
-_SELF_DIR = os.path.dirname(__file__)
-_META_FILE = os.path.normpath(
-    os.path.join(_SELF_DIR, '..', '..', 'metadata.json'))
-with open(_META_FILE) as fd:
-  data = fd.read()
-  _META_DATA = json.loads(data)
-
-
-# Tests can modify these values and alter the output of the trampoline
-# resolver.
-PLATFORM = _META_DATA['platform']
-CONFIG = _META_DATA['config']
-IS_TEST = _META_DATA.get('comment', None) == 'TEST'
-
-
-def _FindCwd():
-  """Gets the current working directory.
-
-  This is detected by whether the metadata.json is real (meaning we are in a
-  cobalt archive) or in the source directory.
-
-  Returns:
-    current working directory for execution.
-  """
-  if IS_TEST:
-    p = os.path.join(_SELF_DIR, '..', '..', '..', '..', '..', '..')
-  else:
-    p = os.path.join(_SELF_DIR, '..', '..', '..')
-  return os.path.normpath(p)
-
-
-def _ShellCmd(cmd_str, cwd):
-  sys.stdout.write('in:      %s\n' % os.path.abspath(cwd))
-  sys.stdout.write('Calling: %s\n\n' % cmd_str)
-  return subprocess.call(cmd_str, cwd=cwd, shell=True,
-                         universal_newlines=True)
-
-
-def _UnQuote(s):
-  if len(s) < 2:
-    return s
-  elif s[0] == '"' and s[-1] == '"':
-    return s[1:-1]
-  else:
-    return s
-
-
-def _ResolveTrampoline(trampoline, argv):
-  """Implemention, see ResolveTrampoline() above."""
-  if argv is None:
-    argv = sys.argv[2:]
-  known_args, unknown_args = _ParseArgs(argv)
-  placeholders = {
-      'platform_arg': '--platform %s' % PLATFORM,
-      'config_arg': '--config %s' % CONFIG
-  }
-  placeholders['device_id_arg'] = (
-      '' if not known_args.device_id
-      else '--device_id %s' % known_args.device_id)
-  placeholders['target_params_arg'] = (
-      '' if not known_args.target_params else
-      '--target_params="%s"' % _UnQuote(known_args.target_params))
-  if not known_args.target_name:
-    placeholders['target_names_arg'] = ''
-  else:
-    targets = ['--target_name ' + name for name in known_args.target_name]
-    placeholders['target_names_arg'] = ' '.join(targets)
-  trampoline = [part.format(**placeholders) for part in trampoline]
-  trampoline = [t for t in trampoline if t.strip()]
-  return ' '.join(trampoline), unknown_args
-
-
-def _ParseArgs(argv):
-  """Parses arguments."""
-
-  class MyParser(argparse.ArgumentParser):
-
-    def error(self, message):
-      sys.stderr.write('error: %s\n' % message)
-      self.print_help()
-
-  formatter_class = argparse.RawDescriptionHelpFormatter
-  parser = MyParser(formatter_class=formatter_class)
-  parser.add_argument('--device_id', type=str,
-                      help=('Choices are a device_id (usually an IP) '
-                            'passed to the launcher.'),
-                      default=None)
-  parser.add_argument('--target_params', type=str,
-                      help='Key=Value list of args to pass to the client.',
-                      default=None)
-  parser.add_argument('-t', '--target_name', action='append',
-                      help=('Name of executable target. Repeatable for '
-                            'multiple targets.'))
-  args = parser.parse_known_args(argv)
-  return args
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/trampoline_test.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/trampoline_test.py
deleted file mode 100644
index cc34e91..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/impl/trampoline_test.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-import unittest
-
-import trampoline  # pylint: disable=relative-import,g-bad-import-order,g-import-not-at-top
-
-
-class TrampolineTest(unittest.TestCase):
-  """Tests trampoline substitutions."""
-
-  def setUp(self):
-    super(TrampolineTest, self).setUp()
-    # Change the trampoline internals for testing purposes.
-    trampoline.PLATFORM = 'MY_PLATFORM'
-    trampoline.CONFIG ='MY_CONFIG'
-
-  def testResolvePlatformConfig(self):
-    """Tests that a device_id resolves to the expected value."""
-    tramp = [
-        'python dummy.py', '{platform_arg}', '{config_arg}', '{device_id_arg}',
-        '{target_params_arg}',
-    ]
-    expected_output = (
-        'python dummy.py --platform MY_PLATFORM --config MY_CONFIG')
-    cmd_str, _ = trampoline.ResolveTrampoline(tramp, argv=[])
-    self.assertEqual(expected_output, cmd_str)
-
-  def testResolveDeviceId(self):
-    """Tests that a device_id resolves to the expected value."""
-    tramp = [
-        'python dummy.py', '{platform_arg}', '{config_arg}', '{device_id_arg}',
-        '{target_params_arg}',
-    ]
-    expected_output = (
-        'python dummy.py --platform MY_PLATFORM --config MY_CONFIG'
-        ' --device_id 1234')
-    cmd_str, _ = trampoline.ResolveTrampoline(
-        tramp,
-        argv=['--device_id', '1234'])
-    self.assertEqual(expected_output, cmd_str)
-
-  def testTargetParams(self):
-    """Tests that target_params resolves to the expected value."""
-    tramp = [
-        'python dummy.py', '--target_name cobalt',
-        '{platform_arg}', '{config_arg}', '{device_id_arg}',
-        '{target_params_arg}',
-    ]
-    expected_output = (
-        'python dummy.py'
-        ' --target_name cobalt --platform MY_PLATFORM'
-        ' --config MY_CONFIG --target_params="--url=http://my.server.test"')
-    cmd_str, _ = trampoline.ResolveTrampoline(
-        tramp,
-        argv=['--target_params', '"--url=http://my.server.test"'])
-    self.assertEqual(expected_output, cmd_str)
-
-
-if __name__ == '__main__':
-  unittest.main(verbosity=2)
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/readme.md b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/readme.md
deleted file mode 100644
index df9ef75..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/readme.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## Trampoline Readme

-

-**Trampoline**:

-  An operation which will forward control to a delegate.

-

-*Maps f(device_id) -> g(platform, config, device_id)*

-

-Trampolines reduce the complexity for invoking cobalt/starboard binaries

-and tests by reading parameters from the `metadata.json` file of a Cobalt

-Archive. The delegate is then run with the full command-line argument

-set including platform and configuration data. The calling signature is

-therefore *stable* and any Cobalt archive can be run the same binaries/tests

-using the same shell command.

-

-Example:

-    python __cobalt_archive/run/run_cobalt.py --device_id IP

-

-  Could translate into:

-    python starboard/tools/example/app_launcher_client.py

-    -t cobalt --platform linux --config devel --device_id IP

diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run.py
deleted file mode 100644
index 896010a..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""A trampoline that launches starboard/tools/example/app_launcher.py.
-
-
-   Example: python __cobalt_archive/run/run.py cobalt
-"""
-
-
-import sys
-sys.path.insert(0, '.')
-from impl import trampoline # pylint: disable=relative-import,g-import-not-at-top
-
-
-TRAMPOLINE = [
-    'python starboard/tools/example/app_launcher_client.py',
-    '{platform_arg}',
-    '{config_arg}',
-    '{device_id_arg}',
-    '{target_params_arg}',
-]
-
-
-def _ResolveTrampoline(argv=None):
-  if argv == None:
-    argv = sys.argv[1:]
-  resolved_cmd, unresolve_args = trampoline.ResolveTrampoline(
-      TRAMPOLINE, argv=argv)
-  # Interpret all tail args as the target_name param.
-  tail_args = ['--target_name %s' % a for a in unresolve_args]
-  resolved_cmd = ' '.join([resolved_cmd] + tail_args)
-  return resolved_cmd
-
-
-if __name__ == '__main__':
-  trampoline.RunThenExit(_ResolveTrampoline())
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_black_box_tests.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_black_box_tests.py
deleted file mode 100644
index dda40e7..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_black_box_tests.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""A trampoline that launches cobalt/tools/buildbot/run_black_box_tests.py.
-
-
-   Example: python __cobalt_archive/run/run_black_box_tests.py
-"""
-
-
-import sys
-sys.path.insert(0, '.')
-from impl import trampoline # pylint: disable=relative-import,g-import-not-at-top
-
-
-TRAMPOLINE = [
-    'python cobalt/tools/buildbot/run_black_box_tests.py',
-    '--action', 'run',
-    '{platform_arg}',
-    '{config_arg}',
-    '{device_id_arg}',
-    '{target_params_arg}',
-]
-
-
-def _ResolveTrampoline(argv=None):
-  if argv == None:
-    argv = sys.argv[1:]
-  resolved_cmd, _ = trampoline.ResolveTrampoline(
-      TRAMPOLINE, argv=argv)
-  return resolved_cmd
-
-
-if __name__ == '__main__':
-  trampoline.RunThenExit(_ResolveTrampoline())
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_platform_tests.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_platform_tests.py
deleted file mode 100644
index f74a7ad..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_platform_tests.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""A trampoline for running the unit tests from a Cobalt Archive.
-
-   This will invoke starboard/tools/testing/test_runner.py with
-   --platform_tests_only, which only runs a subset of tests related to platform
-   include nplb, starboard_platform_tests and others.
-
-   Example: python __cobalt_archive/run/run_platform_tests.py
-"""
-
-
-import sys
-sys.path.insert(0, '.')
-from impl import trampoline # pylint: disable=relative-import,g-import-not-at-top
-
-
-TRAMPOLINE = [
-    'python starboard/tools/testing/test_runner.py',
-    '--platform_tests_only',
-    '{platform_arg}',
-    '{config_arg}',
-    '{device_id_arg}',
-    '{target_params_arg}',
-]
-
-
-def _ResolveTrampoline(argv=None):
-  if argv == None:
-    argv = sys.argv[1:]
-  resolved_cmd, unresolve_args = trampoline.ResolveTrampoline(
-      TRAMPOLINE, argv=argv)
-  return resolved_cmd
-
-
-if __name__ == '__main__':
-  trampoline.RunThenExit(_ResolveTrampoline())
diff --git a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_tests.py b/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_tests.py
deleted file mode 100644
index 041278e..0000000
--- a/src/cobalt/build/cobalt_archive_content/__cobalt_archive/run/run_tests.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-"""A trampoline for running the unit tests from a Cobalt Archive.
-
-   This will invoke starboard/tools/testing/test_runner.py, which runs the
-   specified test with a test filter list for the patform.
-
-   Note that this is different from run.py will invoke
-   starboard/tools/example/app_launcher_client.py which does NOT filter any
-   tests.
-
-   Example: python __cobalt_archive/run/run_tests.py nplb nb_test
-
-   Example: python __cobalt_archive/run/run_tests.py (runs all tests)
-"""
-
-
-import sys
-sys.path.insert(0, '.')
-from impl import trampoline # pylint: disable=relative-import,g-import-not-at-top
-
-
-TRAMPOLINE = [
-    'python starboard/tools/testing/test_runner.py',
-    '{target_names_arg}',
-    '{platform_arg}',
-    '{config_arg}',
-    '{device_id_arg}',
-    '{target_params_arg}',
-]
-
-
-def _ResolveTrampoline(argv=None):
-  if argv == None:
-    argv = sys.argv[1:]
-  resolved_cmd, unresolve_args = trampoline.ResolveTrampoline(
-      TRAMPOLINE, argv=argv)
-  # Interpret all tail args as the target_name param.
-  tail_args = ['--target_name %s' % a for a in unresolve_args]
-  resolved_cmd = ' '.join([resolved_cmd] + tail_args)
-  return resolved_cmd
-
-
-if __name__ == '__main__':
-  trampoline.RunThenExit(_ResolveTrampoline())
diff --git a/src/cobalt/build/cobalt_archive_extract.py b/src/cobalt/build/cobalt_archive_extract.py
deleted file mode 100644
index 617d1e8..0000000
--- a/src/cobalt/build/cobalt_archive_extract.py
+++ /dev/null
@@ -1,166 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-"""Tools for extracting a Cobalt Archive.
-
-This no-dependency tool will extract a Cobalt Archive across all platforms.
-
-This is slightly complicated because of issues on Windows where the poor
-support for pathnames longer than 255 characters is an issue.
-"""
-
-
-import argparse
-import logging
-import os
-import shutil
-import subprocess
-import sys
-import zipfile
-
-
-################################################################################
-#                                  API                                         #
-################################################################################
-
-
-def ExtractTo(archive_zip_path, output_dir, outstream=None):
-  return _ExtractTo(archive_zip_path=archive_zip_path,
-                    output_dir=output_dir,
-                    outstream=outstream)
-
-
-def ToWinUncPath(dos_path, encoding=None):
-  """Returns a windows UNC path which enables long path names in win32 apis."""
-  return _ToWinUncPath(dos_path, encoding)
-
-
-################################################################################
-#                                 IMPL                                         #
-################################################################################
-
-
-_OUT_ARCHIVE_ROOT = '__cobalt_archive'
-_OUT_FINALIZE_DECOMPRESSION_PATH = '%s/%s' % (_OUT_ARCHIVE_ROOT,
-                                              'finalize_decompression')
-_OUT_DECOMP_PY = '%s/%s' % (_OUT_FINALIZE_DECOMPRESSION_PATH,
-                            'decompress.py')
-_OUT_DECOMP_JSON = '%s/%s' % (_OUT_FINALIZE_DECOMPRESSION_PATH,
-                              'decompress.json')
-
-_IS_WINDOWS = sys.platform in ['win32', 'cygwin']
-
-
-def _ToWinUncPath(dos_path, encoding=None):
-  """Windows supports long file names when using a UNC path."""
-  assert _IS_WINDOWS
-  do_convert = (not isinstance(dos_path, unicode) and encoding is not None)
-  if do_convert:
-    dos_path = dos_path.decode(encoding)
-  path = os.path.abspath(dos_path)
-  if path.startswith(u'\\\\'):
-    return u'\\\\?\\UNC\\' + path[2:]
-  return u'\\\\?\\' + path
-
-
-def _GetZipFileClass():
-  """Get the ZipFile class for the platform"""
-  if not _IS_WINDOWS:
-    return zipfile.ZipFile
-  else:
-    class ZipFileLongPaths(zipfile.ZipFile):
-      """Handles extracting to paths longer than 255 characters."""
-
-      def _extract_member(self, member, targetpath, pwd):
-        targetpath = _ToWinUncPath(targetpath)
-        return zipfile.ZipFile._extract_member(self, member, targetpath, pwd)
-    return ZipFileLongPaths
-
-
-def _UnzipFiles(input_zip_path, output_dir, outstream):
-  """Returns True if all files were extracted, else False."""
-  if outstream is None:
-    outstream = sys.stdout
-  all_ok = True
-  zf_class = _GetZipFileClass()
-  with zf_class(input_zip_path, 'r', allowZip64=True) as zf:
-    for zinfo in zf.infolist():
-      try:
-        logging.debug('Extracting: %s -> %s', zinfo.filename, output_dir)
-        zf.extract(zinfo, path=output_dir)
-      except Exception as err:  # pylint: disable=broad-except
-        msg = 'Exception happend during bundle extraction: ' + str(err) + '\n'
-        outstream.write(msg)
-        all_ok = False
-  return all_ok
-
-
-def _ExtractTo(archive_zip_path, output_dir, outstream=None):
-  """Returns True if all files were extracted, False otherwise."""
-  outstream = outstream if outstream else sys.stdout
-  assert os.path.exists(archive_zip_path), 'No archive at %s' % archive_zip_path
-  logging.info('UNZIPPING %s -> %s', archive_zip_path, output_dir)
-  ok = _UnzipFiles(archive_zip_path, output_dir, outstream)
-  # Now that all files have been extracted, execute the final decompress
-  # step.
-  decomp_py = os.path.abspath(os.path.join(output_dir, _OUT_DECOMP_PY))
-  assert(os.path.isfile(decomp_py)), decomp_py
-  cmd_str = 'python ' + decomp_py
-  outstream.write('Executing: %s\n' % cmd_str)
-  rc = subprocess.call(cmd_str, shell=True, stdout=outstream,
-                       stderr=outstream)
-  ok = ok & (rc == 0)
-  return ok
-
-
-def _CreateArgumentParser():
-  """Creates a parser that will print the full help on failure to parse."""
-  parser = argparse.ArgumentParser()
-  parser.add_argument('archive_path', help='Archive to extract.')
-  parser.add_argument('output_path', help='Output path to extract the archive.')
-  parser.add_argument('--delete', action='store_true',
-                      help='Deletes output_path if it exists.')
-  parser.add_argument('--verbose', action='store_true')
-  return parser
-
-
-def main():
-  parser = _CreateArgumentParser()
-  # To make this future compatible parse_known_args() is used and any unknown
-  # args will generate a warning rather than be a fatal event. This allows
-  # any flags used in the future to be used on this tool.
-  args, unknown_args = parser.parse_known_args()
-  logging_lvl = logging.INFO
-  if args.verbose:
-    logging_lvl = logging.DEBUG
-  fmt = '[%(filename)s:%(lineno)s:%(levelname)s] %(message)s'
-  logging.basicConfig(format=fmt, level=logging_lvl)
-  if unknown_args:
-    logging.warning('Unknown (ignored) args: %s', unknown_args)
-  if args.delete:
-    logging.info('Removing previous folder at %s', args.output_path)
-    shutil.rmtree(args.output_path, ignore_errors=True)
-  all_ok = ExtractTo(args.archive_path, args.output_path)
-  if all_ok:
-    sys.exit(0)
-  else:
-    logging.critical('Errors happened.')
-    sys.exit(1)
-
-
-if __name__ == '__main__':
-  main()
-
diff --git a/src/cobalt/build/cobalt_archive_test.py b/src/cobalt/build/cobalt_archive_test.py
deleted file mode 100644
index fe232e5..0000000
--- a/src/cobalt/build/cobalt_archive_test.py
+++ /dev/null
@@ -1,215 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2019 The Cobalt Authors. 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.
-
-
-import json
-import os
-import shutil
-import stat
-import subprocess
-import unittest
-
-import _env  # pylint: disable=relative-import,unused-import
-from cobalt.build import cobalt_archive
-from starboard.build import filelist
-from starboard.build import filelist_test
-from starboard.build import port_symlink
-from starboard.tools import util
-
-
-class CobaltArchiveTest(unittest.TestCase):
-
-  def testFoldIdenticalFiles(self):
-    tf_root = filelist_test.TempFileSystem('bundler_fold')
-    tf_root.Clear()
-    tf1 = filelist_test.TempFileSystem(os.path.join('bundler_fold', '1'))
-    tf2 = filelist_test.TempFileSystem(os.path.join('bundler_fold', '2'))
-    tf1.Make()
-    tf2.Make()
-    flist = filelist.FileList()
-    subdirs = [tf1.root_in_tmp, tf2.root_in_tmp]
-    flist.AddAllFilesInPaths(tf_root.root_tmp, subdirs)
-    flist.Print()
-    identical_files = [tf1.test_txt, tf2.test_txt]
-    physical_files, copy_files = cobalt_archive._FoldIdenticalFiles(
-        identical_files)
-    self.assertEqual(tf1.test_txt, physical_files[0])
-    self.assertIn(tf1.test_txt, copy_files[0][0])
-    self.assertIn(tf2.test_txt, copy_files[0][1])
-
-  def testMakesDeployInfo(self):
-    flist = filelist.FileList()
-    tf = filelist_test.TempFileSystem()
-    tf.Clear()
-    tf.Make()
-    bundle_zip = os.path.join(tf.root_tmp, 'bundle.zip')
-    car = cobalt_archive.CobaltArchive(bundle_zip)
-    car.MakeArchive(platform_name='fake',
-                    platform_sdk_version='fake_sdk',
-                    config='devel',
-                    file_list=flist)
-    out_dir = os.path.join(tf.root_tmp, 'out')
-    car.ExtractTo(out_dir)
-    out_metadata_file = os.path.join(out_dir, cobalt_archive._OUT_METADATA_PATH)
-    self.assertEqual(filelist.GetFileType(out_metadata_file),
-                     filelist.TYPE_FILE)
-    with open(out_metadata_file) as fd:
-      text = fd.read()
-      js = json.loads(text)
-      self.assertTrue(js)
-      self.assertEqual(js['sdk_version'], 'fake_sdk')
-      self.assertEqual(js['platform'], 'fake')
-      self.assertEqual(js['config'], 'devel')
-
-  def testExtractTo(self):
-    flist = filelist.FileList()
-    tf = filelist_test.TempFileSystem()
-    tf.Clear()
-    tf.Make()
-    flist.AddFile(tf.root_in_tmp, tf.test_txt)
-    flist.AddSymLink(tf.root_in_tmp, tf.sym_dir)
-    bundle_zip = os.path.join(tf.root_tmp, 'bundle.zip')
-    car = cobalt_archive.CobaltArchive(bundle_zip)
-    car.MakeArchive(platform_name='fake',
-                    platform_sdk_version='fake_sdk',
-                    config='devel',
-                    file_list=flist)
-    out_dir = os.path.join(tf.root_tmp, 'out')
-    car.ExtractTo(out_dir)
-    out_from_dir = os.path.join(out_dir, 'from_dir')
-    out_from_dir_lnk = os.path.join(out_dir, 'from_dir_lnk')
-    self.assertEqual(filelist.GetFileType(out_from_dir),
-                     filelist.TYPE_DIRECTORY)
-    self.assertEqual(filelist.GetFileType(out_from_dir_lnk),
-                     filelist.TYPE_SYMLINK_DIR)
-    resolved_from_link_path = os.path.join(
-        out_dir, port_symlink.ReadSymLink(out_from_dir_lnk))
-    self.assertEqual(os.path.abspath(out_from_dir),
-                     os.path.abspath(resolved_from_link_path))
-
-  def testExtractFileWithLongFileName(self):
-    """Tests that a long file name can be archived and extracted."""
-    flist = filelist.FileList()
-    tf = filelist_test.TempFileSystem()
-    tf.Clear()
-    tf.Make()
-    self.assertTrue(os.path.exists(tf.root_in_tmp))
-    suffix_path = os.path.join(
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
-        'test.txt'
-    )
-    input_dst = os.path.join(tf.root_in_tmp, suffix_path)
-    out_dir = os.path.join(tf.root_tmp, 'out')
-    output_dst = os.path.join(out_dir, suffix_path)
-    _MoveFileWithLongPath(tf.test_txt, input_dst)
-    self.assertTrue(_LongPathExists(input_dst))
-    flist.AddFile(tf.root_in_tmp, input_dst)
-
-    bundle_zip = os.path.join(tf.root_tmp, 'bundle.zip')
-    car = cobalt_archive.CobaltArchive(bundle_zip)
-    car.MakeArchive(platform_name='fake',
-                    platform_sdk_version='fake_sdk',
-                    config='devel',
-                    file_list=flist)
-    car.ExtractTo(out_dir)
-    self.assertTrue(_LongPathExists(output_dst))
-
-  @unittest.skipIf(port_symlink.IsWindows(), 'Any platform but windows.')
-  def testExecutionAttribute(self):
-    flist = filelist.FileList()
-    tf = filelist_test.TempFileSystem()
-    tf.Make()
-    # Execution bit seems to turn off the read bit, so we just set all
-    # read/write/execute bit for the user.
-    write_flags = stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR
-    os.chmod(tf.test_txt, write_flags)
-    self.assertNotEqual(
-        0, write_flags & cobalt_archive._GetFilePermissions(tf.test_txt))
-    flist.AddFile(tf.root_tmp, tf.test_txt)
-    bundle_zip = os.path.join(tf.root_tmp, 'bundle.zip')
-    car = cobalt_archive.CobaltArchive(bundle_zip)
-    car.MakeArchive(platform_name='fake',
-                    platform_sdk_version='fake_sdk',
-                    config='devel',
-                    file_list=flist)
-    # Now grab the json file and check that the file appears in the
-    # executable_file list.
-    json_str = car.ReadFile(
-        '__cobalt_archive/finalize_decompression/decompress.json')
-    decompress_dict = json.loads(json_str)
-    executable_files = decompress_dict.get('executable_files')
-    # Expect that the executable file appears in the executable_files.
-    self.assertTrue(executable_files)
-    archive_path = os.path.relpath(tf.test_txt, tf.root_tmp)
-    self.assertIn(archive_path, executable_files)
-    out_dir = os.path.join(tf.root_tmp, 'out')
-    car.ExtractTo(output_dir=out_dir)
-    out_file = os.path.join(out_dir, tf.test_txt)
-    self.assertTrue(_LongPathExists(out_file))
-    perms = cobalt_archive._GetFilePermissions(out_file)
-    self.assertTrue(perms & stat.S_IXUSR)
-
-
-def _SilentCall(cmd_str):
-  proc = subprocess.Popen(cmd_str,
-                          shell=True,
-                          stdout=subprocess.PIPE,
-                          stderr=subprocess.STDOUT)
-  (_, _) = proc.communicate()
-  return proc.returncode
-
-
-def _LongPathExists(p):
-  if port_symlink.IsWindows():
-    rc = _SilentCall('dir /s /b "%s"' % p)
-    return rc == 0
-  else:
-    return os.path.isfile(p)
-
-
-def _MoveFileWithLongPath(src_file, dst_file):
-  dst_dir = os.path.dirname(dst_file)
-  if port_symlink.IsWindows():
-    # Work around for file-length path limitations on Windows.
-    src_dir = os.path.dirname(src_file)
-    file_name = os.path.basename(src_file)
-    shell_cmd = 'robocopy "%s" "%s" "%s" /MOV' % (src_dir, dst_dir, file_name)
-    rc = _SilentCall(shell_cmd)
-    if 1 != rc:  # Robocopy returns 1 if a file was copied.
-      raise OSError('File %s was not copied' % src_file)
-    expected_out_file = os.path.join(dst_dir, file_name)
-    if not _LongPathExists(expected_out_file):
-      raise OSError('File did not end up in %s' % dst_dir)
-    return
-  else:
-    if not os.path.isdir(dst_dir):
-      os.makedirs(dst_dir)
-    shutil.move(src_file, dst_file)
-    if not os.path.isfile(dst_file):
-      raise OSError('File did not end up in %s' % dst_dir)
-
-
-if __name__ == '__main__':
-  util.SetupDefaultLoggingConfig()
-  unittest.main(verbosity=2)
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index 9feeca1..995c8d6 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -34,7 +34,8 @@
     'variables': {
       'cobalt_webapi_extension_source_idl_files%': [],
       'cobalt_webapi_extension_generated_header_idl_files%': [],
-      'cobalt_v8_buildtime_snapshot%': "true",
+      'cobalt_v8_buildtime_snapshot%': 1,
+      'cobalt_v8_enable_embedded_builtins%': 1,
     },
 
     # Whether Cobalt is being built.
@@ -121,7 +122,7 @@
     'cobalt_version%': '<(BUILD_NUMBER)',
 
     # Defines what kind of rasterizer will be used.  This can be adjusted to
-    # force a stub graphics implementation or software graphics implementation.
+    # force a stub graphics implementation.
     # It can be one of the following options:
     #   'direct-gles' -- Uses a light wrapper over OpenGL ES to handle most
     #                    draw elements. This will fall back to the skia hardware
@@ -131,9 +132,6 @@
     #   'hardware'    -- As much hardware acceleration of graphics commands as
     #                    possible. This uses skia to wrap OpenGL ES commands.
     #                    Required for 360 rendering.
-    #   'software'    -- Perform most rasterization using the CPU and only
-    #                    interact with the GPU to send the final image to the
-    #                    output window.
     #   'stub'        -- Stub graphics rasterization.  A rasterizer object will
     #                    still be available and valid, but it will do nothing.
     'rasterizer_type%': 'direct-gles',
@@ -586,7 +584,7 @@
           'ENABLE_DEBUGGER',
         ],
       }],
-      ['cobalt_v8_buildtime_snapshot == "true"', {
+      ['cobalt_v8_buildtime_snapshot == 1', {
         'defines': [
           'COBALT_V8_BUILDTIME_SNAPSHOT=1',
         ],
@@ -595,7 +593,14 @@
         'defines': [
           'COBALT_ENABLE_QUIC',
         ],
-      }]
+      }],
+      ['host_os=="win"', {
+        # A few flags to mute MSVC compiler errors that does not appear on Linux.
+        'compiler_flags_host': [
+          '/wd4267',  # Possible loss of precision from size_t to a smaller type.
+          '/wd4715',  # Not all control paths return value.
+        ],
+      }],
     ],
   }, # end of target_defaults
 
diff --git a/src/cobalt/build/cobalt_configuration.py b/src/cobalt/build/cobalt_configuration.py
index ddb656e..bd661d4 100644
--- a/src/cobalt/build/cobalt_configuration.py
+++ b/src/cobalt/build/cobalt_configuration.py
@@ -152,6 +152,7 @@
         'storage_upgrade_test',
         'web_animations_test',
         'webdriver_test',
+        'websocket_test',
         'xhr_test',
     ]
 
diff --git a/src/cobalt/csp/source_list.cc b/src/cobalt/csp/source_list.cc
index e5098a4..71e54a8 100644
--- a/src/cobalt/csp/source_list.cc
+++ b/src/cobalt/csp/source_list.cc
@@ -120,11 +120,14 @@
 
   if (allow_insecure_connections_to_localhost_) {
     std::string host;
-#if SB_HAS(IPV6)
-    host = url.HostNoBrackets();
-#else
+    // This will be our host string if we are not using IPV6.
     host.append(valid_spec.c_str() + parsed.host.begin,
                 valid_spec.c_str() + parsed.host.begin + parsed.host.len);
+#if SB_API_VERSION >= SB_IPV6_REQUIRED_VERSION || SB_HAS(IPV6)
+#if SB_API_VERSION >= SB_IPV6_REQUIRED_VERSION
+    if (SbSocketIsIpv6Supported())
+#endif
+      host = url.HostNoBrackets();
 #endif
     if (net::HostStringIsLocalhost(host)) {
       return true;
diff --git a/src/cobalt/cssom/property_definitions.cc b/src/cobalt/cssom/property_definitions.cc
index 72d36ac..6a4db89 100644
--- a/src/cobalt/cssom/property_definitions.cc
+++ b/src/cobalt/cssom/property_definitions.cc
@@ -884,7 +884,7 @@
   SetShorthandPropertyDefinition(kBorderRadiusProperty, "border-radius",
                                  border_radius_longhand_properties);
 
-  //   https://www.w3.org/TR/css3-background/#border
+  //   https://www.w3.org/TR/css-backgrounds-3/#propdef-border
   LonghandPropertySet border_longhand_properties;
   border_longhand_properties.insert(kBorderColorProperty);
   border_longhand_properties.insert(kBorderStyleProperty);
diff --git a/src/cobalt/debug/backend/command_map.h b/src/cobalt/debug/backend/command_map.h
index 4e01994..b929808 100644
--- a/src/cobalt/debug/backend/command_map.h
+++ b/src/cobalt/debug/backend/command_map.h
@@ -42,18 +42,19 @@
       : agent_(agent), domain_(domain) {}
 
   // Calls the mapped method implementation.
-  // Returns a true iff the command method is mapped and has been run.
-  bool RunCommand(const Command& command) {
+  // Passes ownership of the command to the mapped method, otherwise returns
+  // ownership of the not-run command for a fallback JS implementation.
+  std::unique_ptr<Command> RunCommand(std::unique_ptr<Command> command) {
     // If the domain matches, trim it and the dot from the method name.
     const std::string& method =
-        (domain_ == command.GetDomain())
-            ? command.GetMethod().substr(domain_.size() + 1)
-            : command.GetMethod();
+        (domain_ == command->GetDomain())
+            ? command->GetMethod().substr(domain_.size() + 1)
+            : command->GetMethod();
     auto iter = this->find(method);
-    if (iter == this->end()) return false;
+    if (iter == this->end()) return command;
     auto command_fn = iter->second;
-    (agent_->*command_fn)(command);
-    return true;
+    (agent_->*command_fn)(*command);
+    return nullptr;
   }
 
   // Binds |RunCommand| to a callback to be registered with |DebugDispatcher|.
diff --git a/src/cobalt/debug/backend/debug_dispatcher.cc b/src/cobalt/debug/backend/debug_dispatcher.cc
index ec6f7f7..7454404 100644
--- a/src/cobalt/debug/backend/debug_dispatcher.cc
+++ b/src/cobalt/debug/backend/debug_dispatcher.cc
@@ -22,6 +22,10 @@
 #include "base/values.h"
 #include "cobalt/debug/debug_client.h"
 
+namespace {
+void NoOpResponseCallback(const base::Optional<std::string>& response) {}
+}  // namespace
+
 namespace cobalt {
 namespace debug {
 namespace backend {
@@ -78,13 +82,14 @@
   clients_.erase(client);
 }
 
-void DebugDispatcher::SendCommand(const Command& command) {
+void DebugDispatcher::SendCommand(std::unique_ptr<Command> command) {
   // Create a closure that will run the command and the response callback.
   // The task is either posted to the debug target (WebModule) thread if
   // that thread is running normally, or added to a queue of debugger tasks
   // being processed while paused.
-  base::Closure command_closure = base::Bind(&DebugDispatcher::DispatchCommand,
-                                             base::Unretained(this), command);
+  base::Closure command_closure =
+      base::Bind(&DebugDispatcher::DispatchCommand, base::Unretained(this),
+                 base::Passed(std::move(command)));
 
   if (is_paused_) {
     DispatchCommandWhilePaused(command_closure);
@@ -93,26 +98,37 @@
   }
 }
 
-void DebugDispatcher::DispatchCommand(Command command) {
+void DebugDispatcher::DispatchCommand(std::unique_ptr<Command> command) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  DomainRegistry::iterator iter = domain_registry_.find(command.GetDomain());
-  if (iter != domain_registry_.end() && iter->second.Run(command)) {
-    // The agent command implementation ran and sends its own response.
-    return;
+  // This workaround allows both the overlay console and remote DevTools to
+  // connect at the same time. Each time a client sends the "Runtime.enable"
+  // command, we first inject a "Runtime.disable" command so that the V8
+  // Inspector will send the "Runtime.executionContextCreated" event for every
+  // "Runtime.enable" command rather than just for the first one.
+  if (command->GetMethod() == "Runtime.enable") {
+    DispatchCommand(std::make_unique<Command>(
+        "Runtime.disable", "", base::Bind(&NoOpResponseCallback)));
+  }
+
+  DomainRegistry::iterator iter = domain_registry_.find(command->GetDomain());
+  if (iter != domain_registry_.end()) {
+    command = iter->second.Run(std::move(command));
+    // The agent command implementation kept the command to send the response.
+    if (!command) return;
   }
 
   // The agent didn't have a native implementation. Try to run a
   // JavaScript implementation (which the agent would have loaded at the
   // same time as it registered its domain command handler).
   JSONObject response =
-      RunScriptCommand(command.GetMethod(), command.GetParams());
+      RunScriptCommand(command->GetMethod(), command->GetParams());
   if (response) {
-    command.SendResponse(response);
+    command->SendResponse(response);
   } else {
-    DLOG(WARNING) << "Command not implemented: " << command.GetMethod();
-    command.SendErrorResponse(Command::kMethodNotFound,
-                              "Command not implemented");
+    DLOG(WARNING) << "Command not implemented: " << command->GetMethod();
+    command->SendErrorResponse(Command::kMethodNotFound,
+                               "Command not implemented");
   }
 }
 
diff --git a/src/cobalt/debug/backend/debug_dispatcher.h b/src/cobalt/debug/backend/debug_dispatcher.h
index 41dc4fd..c114035 100644
--- a/src/cobalt/debug/backend/debug_dispatcher.h
+++ b/src/cobalt/debug/backend/debug_dispatcher.h
@@ -106,8 +106,13 @@
     std::set<DebugClient*> clients_;
   };
 
-  // A command execution function stored in the domain registry.
-  typedef base::Callback<bool(const Command& command)> CommandHandler;
+  // A command execution function stored in the domain registry. If the command
+  // is supported, ownership of the command parameter should be kept and used to
+  // send the response. If the command is not supported, the command should be
+  // returned so the dispatcher can try calling a JS fallback implementation.
+  typedef base::Callback<std::unique_ptr<Command>(
+      std::unique_ptr<Command> command)>
+      CommandHandler;
 
   DebugDispatcher(script::ScriptDebugger* script_debugger,
                   DebugScriptRunner* script_runner);
@@ -150,7 +155,7 @@
   // called from any thread - the command will be run on the dispatcher's
   // message loop, and the response will be sent to the callback and message
   // loop held in the command object.
-  void SendCommand(const Command& command);
+  void SendCommand(std::unique_ptr<Command> command);
 
   // Sets or unsets the paused state and calls |HandlePause| if set.
   // Must be called on the debug target (WebModule) thread.
@@ -175,7 +180,7 @@
   // name in the command registry and running the corresponding function.
   // The response callback will be run on the message loop specified in the
   // info structure with the result as an argument.
-  void DispatchCommand(Command command);
+  void DispatchCommand(std::unique_ptr<Command> command);
 
   // Called by |SendCommand| if a debugger command is received while script
   // execution is paused.
diff --git a/src/cobalt/debug/backend/debugger_hooks_impl.cc b/src/cobalt/debug/backend/debugger_hooks_impl.cc
index 2ba4d2d..ef17028 100644
--- a/src/cobalt/debug/backend/debugger_hooks_impl.cc
+++ b/src/cobalt/debug/backend/debugger_hooks_impl.cc
@@ -32,26 +32,28 @@
   script_debugger_ = nullptr;
 }
 
-void DebuggerHooksImpl::AsyncTaskScheduled(void* task, const std::string& name,
-                                           bool recurring) const {
+void DebuggerHooksImpl::AsyncTaskScheduled(const void* task,
+                                           const std::string& name,
+                                           AsyncTaskFrequency frequency) const {
   if (script_debugger_) {
-    script_debugger_->AsyncTaskScheduled(task, name, recurring);
+    script_debugger_->AsyncTaskScheduled(
+        task, name, (frequency == AsyncTaskFrequency::kRecurring));
   }
 }
 
-void DebuggerHooksImpl::AsyncTaskStarted(void* task) const {
+void DebuggerHooksImpl::AsyncTaskStarted(const void* task) const {
   if (script_debugger_) {
     script_debugger_->AsyncTaskStarted(task);
   }
 }
 
-void DebuggerHooksImpl::AsyncTaskFinished(void* task) const {
+void DebuggerHooksImpl::AsyncTaskFinished(const void* task) const {
   if (script_debugger_) {
     script_debugger_->AsyncTaskFinished(task);
   }
 }
 
-void DebuggerHooksImpl::AsyncTaskCanceled(void* task) const {
+void DebuggerHooksImpl::AsyncTaskCanceled(const void* task) const {
   if (script_debugger_) {
     script_debugger_->AsyncTaskCanceled(task);
   }
diff --git a/src/cobalt/debug/backend/debugger_hooks_impl.h b/src/cobalt/debug/backend/debugger_hooks_impl.h
index 17a6115..a856cb1 100644
--- a/src/cobalt/debug/backend/debugger_hooks_impl.h
+++ b/src/cobalt/debug/backend/debugger_hooks_impl.h
@@ -31,11 +31,11 @@
 
 class DebuggerHooksImpl : public base::DebuggerHooks {
  public:
-  void AsyncTaskScheduled(void* task, const std::string& name,
-                          bool recurring) const override;
-  void AsyncTaskStarted(void* task) const override;
-  void AsyncTaskFinished(void* task) const override;
-  void AsyncTaskCanceled(void* task) const override;
+  void AsyncTaskScheduled(const void* task, const std::string& name,
+                          AsyncTaskFrequency frequency) const override;
+  void AsyncTaskStarted(const void* task) const override;
+  void AsyncTaskFinished(const void* task) const override;
+  void AsyncTaskCanceled(const void* task) const override;
 
  private:
   // Only DebugModule can attach/detach the debugger.
diff --git a/src/cobalt/debug/backend/script_debugger_agent.cc b/src/cobalt/debug/backend/script_debugger_agent.cc
index c9afc99..c2c312f 100644
--- a/src/cobalt/debug/backend/script_debugger_agent.cc
+++ b/src/cobalt/debug/backend/script_debugger_agent.cc
@@ -61,7 +61,8 @@
   return agent_state;
 }
 
-bool ScriptDebuggerAgent::RunCommand(const Command& command) {
+std::unique_ptr<Command> ScriptDebuggerAgent::RunCommand(
+    std::unique_ptr<Command> command) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Use an internal ID to store the pending command until we get a response.
@@ -69,21 +70,26 @@
 
   JSONObject message(new base::DictionaryValue());
   message->SetInteger(kId, command_id);
-  message->SetString(kMethod, command.GetMethod());
-  JSONObject params = JSONParse(command.GetParams());
+  message->SetString(kMethod, command->GetMethod());
+  JSONObject params = JSONParse(command->GetParams());
   if (params) {
     message->Set(kParams, std::move(params));
   }
 
   // Store the pending command before dispatching it so that we can find it if
   // the script debugger sends a synchronous response before returning.
-  pending_commands_.emplace(command_id, command);
-  if (!script_debugger_->DispatchProtocolMessage(command.GetMethod(),
-                                                 JSONStringify(message))) {
-    pending_commands_.erase(command_id);
-    return false;
+  std::string method = command->GetMethod();
+  pending_commands_.emplace(command_id, std::move(command));
+  if (script_debugger_->DispatchProtocolMessage(method,
+                                                JSONStringify(message))) {
+    // The command has been dispached; keep ownership of it in the map.
+    return nullptr;
   }
-  return true;
+
+  // Take the command back out of the map and return it for fallback.
+  command = std::move(pending_commands_.at(command_id));
+  pending_commands_.erase(command_id);
+  return command;
 }
 
 void ScriptDebuggerAgent::SendCommandResponse(
@@ -98,7 +104,7 @@
   // Use the stripped ID to lookup the command it's a response for.
   auto iter = pending_commands_.find(command_id);
   if (iter != pending_commands_.end()) {
-    iter->second.SendResponse(response);
+    iter->second->SendResponse(response);
     pending_commands_.erase(iter);
   } else {
     DLOG(ERROR) << "Spurious debugger response: " << json_response;
diff --git a/src/cobalt/debug/backend/script_debugger_agent.h b/src/cobalt/debug/backend/script_debugger_agent.h
index ef6956d..7e906f0 100644
--- a/src/cobalt/debug/backend/script_debugger_agent.h
+++ b/src/cobalt/debug/backend/script_debugger_agent.h
@@ -41,7 +41,7 @@
   bool IsSupportedDomain(const std::string& domain) {
     return supported_domains_.count(domain) != 0;
   }
-  bool RunCommand(const Command& command);
+  std::unique_ptr<Command> RunCommand(std::unique_ptr<Command> command);
   void SendCommandResponse(const std::string& json_response);
   void SendEvent(const std::string& json_event);
 
@@ -53,7 +53,7 @@
   const std::set<std::string> supported_domains_;
 
   int last_command_id_ = 0;
-  std::map<int, Command> pending_commands_;
+  std::map<int, std::unique_ptr<Command>> pending_commands_;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/command.h b/src/cobalt/debug/command.h
index 05659d5..f403d48 100644
--- a/src/cobalt/debug/command.h
+++ b/src/cobalt/debug/command.h
@@ -42,14 +42,17 @@
     kServerError = -32000,
   };
 
-  explicit Command(const std::string& method, const std::string& json_params,
-                   const DebugClient::ResponseCallback& response_callback)
+  Command(const std::string& method, const std::string& json_params,
+          const DebugClient::ResponseCallback& response_callback)
       : method_(method),
         domain_(method_, 0, method_.find('.')),
         json_params_(json_params),
         callback_(response_callback),
         task_runner_(base::MessageLoop::current()->task_runner()) {}
 
+  Command(Command&) = delete;
+  Command(Command&&) = delete;
+
   const std::string& GetMethod() const { return method_; }
   const std::string& GetDomain() const { return domain_; }
   const std::string& GetParams() const { return json_params_; }
diff --git a/src/cobalt/debug/debug_client.cc b/src/cobalt/debug/debug_client.cc
index db21401..cfb9269 100644
--- a/src/cobalt/debug/debug_client.cc
+++ b/src/cobalt/debug/debug_client.cc
@@ -67,7 +67,8 @@
     DLOG(WARNING) << "Debug client is not attached to dispatcher.";
     return;
   }
-  dispatcher_->SendCommand(Command(method, json_params, callback));
+  dispatcher_->SendCommand(
+      std::make_unique<Command>(method, json_params, callback));
 }
 
 }  // namespace debug
diff --git a/src/cobalt/debug/remote/debug_web_server.cc b/src/cobalt/debug/remote/debug_web_server.cc
index ef83a5c..1f27f07 100644
--- a/src/cobalt/debug/remote/debug_web_server.cc
+++ b/src/cobalt/debug/remote/debug_web_server.cc
@@ -79,38 +79,6 @@
   return base::nullopt;
 }
 
-base::Optional<std::string> GetLocalIpAddress() {
-  net::IPEndPoint ip_addr;
-  SbSocketAddress local_ip;
-  SbMemorySet(&local_ip, 0, sizeof(local_ip));
-  bool result = false;
-
-  // Prefer IPv4 addresses, as they're easier to type for debugging.
-  SbSocketAddressType address_types[] = {kSbSocketAddressTypeIpv4,
-                                         kSbSocketAddressTypeIpv6};
-
-  for (std::size_t i = 0; i != SB_ARRAY_SIZE(address_types); ++i) {
-    SbSocketAddress destination;
-    SbMemorySet(&(destination.address), 0, sizeof(destination.address));
-    destination.type = address_types[i];
-    if (!SbSocketGetInterfaceAddress(&destination, &local_ip, NULL)) {
-      continue;
-    }
-
-    if (ip_addr.FromSbSocketAddress(&local_ip)) {
-      result = true;
-      break;
-    }
-  }
-
-  if (!result) {
-    DLOG(WARNING) << "Unable to get a local interface address.";
-    return base::nullopt;
-  }
-
-  return ip_addr.ToStringWithoutPort();
-}
-
 const char kContentDir[] = "cobalt/debug/remote";
 const char kDetached[] = "Inspector.detached";
 const char kDetachReasonField[] = "params.reason";
@@ -121,13 +89,16 @@
 constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotation =
     net::DefineNetworkTrafficAnnotation("cobalt_debug_web_server",
                                         "cobalt_debug_web_server");
+
+constexpr int kUnattachedWebSocketId = -1;
 }  // namespace
 
 DebugWebServer::DebugWebServer(
-    int port, const CreateDebugClientCallback& create_debug_client_callback)
+    int port, const std::string& listen_ip,
+    const CreateDebugClientCallback& create_debug_client_callback)
     : http_server_thread_("DebugWebServer"),
       create_debug_client_callback_(create_debug_client_callback),
-      websocket_id_(-1),
+      websocket_id_(kUnattachedWebSocketId),
       // Local address will be set when the web server is successfully started.
       local_address_("Cobalt.Server.DevTools", "<NOT RUNNING>",
                      "Address to connect to for remote debugging.") {
@@ -142,8 +113,8 @@
   http_server_thread_.StartWithOptions(
       base::Thread::Options(base::MessageLoop::TYPE_IO, stack_size));
   http_server_thread_.message_loop()->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&DebugWebServer::StartServer, base::Unretained(this), port));
+      FROM_HERE, base::Bind(&DebugWebServer::StartServer,
+                            base::Unretained(this), port, listen_ip));
 }
 
 DebugWebServer::~DebugWebServer() {
@@ -213,6 +184,11 @@
   std::string path = info.path;
   DLOG(INFO) << "Got web socket request [" << connection_id << "]: " << path;
 
+  // Disconnect any other DevTools client that's already attached.
+  if (websocket_id_ != kUnattachedWebSocketId) {
+    server_->Close(websocket_id_);
+  }
+
   // Ignore the path and bind any web socket request to the debugger.
   websocket_id_ = connection_id;
   server_->AcceptWebSocket(connection_id, info, kNetworkTrafficAnnotation);
@@ -220,10 +196,16 @@
   debug_client_ = create_debug_client_callback_.Run(this);
 }
 
+void DebugWebServer::OnClose(int connection_id) {
+  if (connection_id == websocket_id_) {
+    websocket_id_ = kUnattachedWebSocketId;
+  }
+}
+
 void DebugWebServer::OnWebSocketMessage(int connection_id,
                                         const std::string& json) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(connection_id, websocket_id_);
+  DCHECK_EQ(connection_id, websocket_id_) << "Mismatched WebSocket ID";
 
   // Parse the json string to get id, method and params.
   JSONObject json_object = JSONParse(json);
@@ -324,35 +306,21 @@
                              kNetworkTrafficAnnotation);
 }
 
-int DebugWebServer::GetLocalAddress(std::string* out) const {
-  net::IPEndPoint ip_addr;
-  int result = server_->GetLocalAddress(&ip_addr);
-  if (result == net::OK) {
-    *out = std::string("http://") + ip_addr.ToString();
-  }
-  return result;
-}
-
-void DebugWebServer::StartServer(int port) {
+void DebugWebServer::StartServer(int port, const std::string& listen_ip) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Create http server
-  const base::Optional<std::string> ip_addr = GetLocalIpAddress();
-  if (!ip_addr) {
-    DLOG(WARNING)
-        << "Could not get a local IP address for the debug web server.";
-    return;
-  }
   auto* server_socket =
       new net::TCPServerSocket(NULL /*net_log*/, net::NetLogSource());
   server_socket->ListenWithAddressAndPort(
-      ip_addr.value(), static_cast<uint16_t>(port), 1 /*backlog*/);
+      listen_ip, static_cast<uint16_t>(port), 1 /*backlog*/);
   server_.reset(new net::HttpServer(
       std::unique_ptr<net::ServerSocket>(server_socket), this));
 
-  std::string address;
-  int result = GetLocalAddress(&address);
+  net::IPEndPoint ip_addr;
+  int result = server_->GetLocalInterfaceAddress(&ip_addr);
   if (result == net::OK) {
+    std::string address = "http://" + ip_addr.ToString();
     // clang-format off
     LOG(INFO) << "\n---------------------------------"
               << "\n Connect to the web debugger at:"
diff --git a/src/cobalt/debug/remote/debug_web_server.h b/src/cobalt/debug/remote/debug_web_server.h
index 896de2d..af57a25 100644
--- a/src/cobalt/debug/remote/debug_web_server.h
+++ b/src/cobalt/debug/remote/debug_web_server.h
@@ -38,7 +38,7 @@
 class DebugWebServer : public net::HttpServer::Delegate,
                        public DebugClient::Delegate {
  public:
-  DebugWebServer(int port,
+  DebugWebServer(int port, const std::string& listen_ip,
                  const CreateDebugClientCallback& create_debug_client_callback);
   ~DebugWebServer();
 
@@ -53,7 +53,7 @@
 
   void OnWebSocketMessage(int connection_id, const std::string& json) override;
 
-  void OnClose(int /*connection_id*/) override {}
+  void OnClose(int connection_id) override;
 
   // Debugger command response handler.
   void OnDebuggerResponse(int id, const base::Optional<std::string>& response);
@@ -66,9 +66,7 @@
   void OnDebugClientDetach(const std::string& reason) override;
 
  private:
-  int GetLocalAddress(std::string* out) const;
-
-  void StartServer(int port);
+  void StartServer(int port, const std::string& listen_ip);
 
   void StopServer();
 
diff --git a/src/cobalt/debug/remote/devtools/inspector_protocol/inspector_protocol.gyp b/src/cobalt/debug/remote/devtools/inspector_protocol/inspector_protocol.gyp
index 6137633..4ff730e 100644
--- a/src/cobalt/debug/remote/devtools/inspector_protocol/inspector_protocol.gyp
+++ b/src/cobalt/debug/remote/devtools/inspector_protocol/inspector_protocol.gyp
@@ -29,7 +29,7 @@
           'input_files': [
             'browser_protocol.pdl',
             'browser_protocol-1.3.json',
-            '<(DEPTH)/v8/src/inspector/js_protocol.json',
+            '<(DEPTH)/v8/include/js_protocol.pdl',
           ],
           'stamp_file': '<(SHARED_INTERMEDIATE_DIR)/cobalt/debug/remote/inspector_protocol/browser_protocol.stamp',
         },
@@ -57,7 +57,7 @@
           'script_path': '<(DEPTH)/third_party/inspector_protocol/concatenate_protocols.py',
           'input_files': [
             'browser_protocol.pdl',
-            '<(DEPTH)/v8/src/inspector/js_protocol.json',
+            '<(DEPTH)/v8/include/js_protocol.pdl',
           ],
           'output_file': '<(SHARED_INTERMEDIATE_DIR)/cobalt/debug/remote/inspector_protocol/inspector_protocol.json',
         },
diff --git a/src/cobalt/demos/content/deep-link-demo/deep-link-demo.html b/src/cobalt/demos/content/deep-link-demo/deep-link-demo.html
index a99d33d..011fdf0 100644
--- a/src/cobalt/demos/content/deep-link-demo/deep-link-demo.html
+++ b/src/cobalt/demos/content/deep-link-demo/deep-link-demo.html
@@ -5,9 +5,9 @@
     console.log("h5vcc.runtime.initialDeepLink: " +
                 h5vcc.runtime.initialDeepLink);
 
-    h5vcc.runtime.onDeepLink = function(link) {
+    h5vcc.runtime.onDeepLink.addListener(function(link) {
       console.log("h5vcc.runtime.onDeepLink: " + link);
-    };
+    });
   </script>
 </head>
 <body style="background-color:#48C"></body>
diff --git a/src/cobalt/demos/content/system-caption-settings/index.html b/src/cobalt/demos/content/system-caption-settings/index.html
new file mode 100644
index 0000000..48afae3
--- /dev/null
+++ b/src/cobalt/demos/content/system-caption-settings/index.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+    #captionWindow {
+      position: absolute;
+      bottom: 10px;
+      text-align: center;
+      padding: 25px;
+    }
+    </style>
+  </head>
+  <body>
+    <div id="captionWindow">
+      <span id="caption">Captions will look like this</span>
+    </div>
+  </body>
+  <script>
+    // Ignore characterEdgeStyle attribute because it is currently unsupported by Cobalt.
+    //
+    // Ignore fontFamily, backgroundOpacity, fontOpacity, and windowOpacity attributes because
+    // they require lengthy conversion (and will likely be provided if the other attributes are).
+
+    let settings = navigator.systemCaptionSettings;
+    console.log(settings);
+
+    let setCaptionStyle = (elementId, cssProperty, state, captionStyle) => {
+      if (state == "set" || state == "override") {
+        document.getElementById(elementId).style[cssProperty] = captionStyle;
+      }
+    }
+
+    setCaptionStyle("caption", "backgroundColor", settings.backgroundColorState, settings.backgroundColor);
+    setCaptionStyle("caption", "color", settings.fontColorState, settings.fontColor);
+    setCaptionStyle("caption", "fontSize", settings.fontSizeState, `${settings.fontSize}px`);
+    setCaptionStyle("captionWindow", "backgroundColor", settings.windowColorState, settings.windowColor);
+  </script>
+</html>
diff --git a/src/cobalt/doc/net_log.md b/src/cobalt/doc/net_log.md
new file mode 100644
index 0000000..dfc3c98
--- /dev/null
+++ b/src/cobalt/doc/net_log.md
@@ -0,0 +1,30 @@
+# Cobalt NetLog

+

+Chromium has a very useful network diagnostic tool called the NetLog and Cobalt

+is hooked up to use it. It's the main tool to track network traffic and debug

+network code.

+

+### Activate the NetLog

+

+The following command line switch will activate the NetLog and store net log

+record to the specified location.

+`./cobalt --net_log=/PATH/TO/YOUR_NETLOG_NAME.json`

+The output json file will be stored at the file location you choose.

+

+

+### Read the NetLog records

+

+The produced json file is not human-friendly, use the

+[NetLog Viewer](https://netlog-viewer.appspot.com/#import)

+

+Cobalt's net_log can not enable some features in the web viewer, but all the

+network traffic is recorded in the event tab.

+

+

+### Add NetLog entries

+

+To Add NetLog entry, get the NetLog instance owned by NetworkModule to where you

+want to add entries and start/end your entry according to the NetLog interface.

+

+A NetLog object is created at each NetworkModule initialization and is passed

+into Chromium net through URLRequestContext.

diff --git a/src/cobalt/doc/web_debugging.md b/src/cobalt/doc/web_debugging.md
index ca1d90d..b43a59b 100644
--- a/src/cobalt/doc/web_debugging.md
+++ b/src/cobalt/doc/web_debugging.md
@@ -39,10 +39,9 @@
 > If you have trouble connecting:
 > * Ensure you have an IP route from your desktop to the target device that
 >   allows traffic on the debugging port (default 9222).
-> * If both IPv4 andIPv6 networks are available, the debug server will prefer to
->   bind to the IPv4 network interface.
-> * If you are running Cobalt locally on your desktop, then use your actual IP
->   address (not localhost nor 127.0.0.1).
+> * If you are running Cobalt locally on your desktop, then use
+>   http://localhost:9222 since the Linux build only listens to the loopback
+>   network interface by default.
 
 If you're not sure what IP address to use, look in the terminal log output for a
 message telling you the URL of Cobalt's DevTools (which you may be able to open
@@ -174,3 +173,6 @@
 
 *   You can use the `--remote_debugging_port` command line switch to specify a
     remote debugging port other than the default 9222.
+
+*   You can use the `--dev_servers_listen_ip` command line switch to change
+    which network interface the remote debugging server is listening to.
diff --git a/src/cobalt/dom/abort_controller.cc b/src/cobalt/dom/abort_controller.cc
index 546d5c4..68ccb9a 100644
--- a/src/cobalt/dom/abort_controller.cc
+++ b/src/cobalt/dom/abort_controller.cc
@@ -17,8 +17,8 @@
 namespace cobalt {
 namespace dom {
 
-AbortController::AbortController() {
-  abort_signal_ = new AbortSignal();
+AbortController::AbortController(script::EnvironmentSettings* settings) {
+  abort_signal_ = new AbortSignal(settings);
 }
 
 void AbortController::Abort() {
diff --git a/src/cobalt/dom/abort_controller.h b/src/cobalt/dom/abort_controller.h
index be273b0..e8d0c5d 100644
--- a/src/cobalt/dom/abort_controller.h
+++ b/src/cobalt/dom/abort_controller.h
@@ -16,6 +16,8 @@
 #define COBALT_DOM_ABORT_CONTROLLER_H_
 
 #include "cobalt/dom/abort_signal.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/global_environment.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -26,7 +28,7 @@
 class AbortController : public script::Wrappable {
  public:
   // Web API: AbortController
-  AbortController();
+  explicit AbortController(script::EnvironmentSettings* settings);
 
   const scoped_refptr<AbortSignal>& signal() const { return abort_signal_; }
   void Abort();
diff --git a/src/cobalt/dom/abort_controller.idl b/src/cobalt/dom/abort_controller.idl
index 7612f56..36ebebd 100644
--- a/src/cobalt/dom/abort_controller.idl
+++ b/src/cobalt/dom/abort_controller.idl
@@ -14,7 +14,10 @@
 
 // https://dom.spec.whatwg.org/#interface-abortcontroller
 
-[Constructor]
+[
+  Constructor,
+  ConstructorCallWith=EnvironmentSettings,
+]
 interface AbortController {
   [SameObject] readonly attribute AbortSignal signal;
   void abort();
diff --git a/src/cobalt/dom/abort_signal.h b/src/cobalt/dom/abort_signal.h
index 69db444..e544e31 100644
--- a/src/cobalt/dom/abort_signal.h
+++ b/src/cobalt/dom/abort_signal.h
@@ -19,6 +19,7 @@
 
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/event_target.h"
+#include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
 namespace dom {
@@ -27,7 +28,8 @@
 //    https://dom.spec.whatwg.org/#interface-AbortSignal
 class AbortSignal : public EventTarget {
  public:
-  AbortSignal() {}
+  explicit AbortSignal(script::EnvironmentSettings* settings)
+      : EventTarget(settings) {}
 
   // Web API: AbortSignal
   bool aborted() const { return aborted_; }
diff --git a/src/cobalt/dom/animation_frame_request_callback_list.cc b/src/cobalt/dom/animation_frame_request_callback_list.cc
index eaa9cab..2b27de6 100644
--- a/src/cobalt/dom/animation_frame_request_callback_list.cc
+++ b/src/cobalt/dom/animation_frame_request_callback_list.cc
@@ -15,6 +15,7 @@
 #include "cobalt/dom/animation_frame_request_callback_list.h"
 
 #include "base/trace_event/trace_event.h"
+#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/dom/global_stats.h"
 
 namespace cobalt {
@@ -30,6 +31,9 @@
   frame_request_callbacks_.emplace_back(
       new FrameRequestCallbackWithCancelledFlag(owner_,
                                                 frame_request_callback));
+  debugger_hooks_->AsyncTaskScheduled(
+      frame_request_callbacks_.back().get(), "requestAnimationFrame",
+      base::DebuggerHooks::AsyncTaskFrequency::kOneshot);
   return static_cast<int32>(frame_request_callbacks_.size());
 }
 
@@ -38,7 +42,9 @@
   // frame request callback.
   const size_t handle = static_cast<size_t>(in_handle);
   if (handle > 0 && handle <= frame_request_callbacks_.size()) {
-    frame_request_callbacks_[handle - 1]->cancelled = true;
+    auto& callback = frame_request_callbacks_.at(handle - 1);
+    debugger_hooks_->AsyncTaskCanceled(callback.get());
+    callback->cancelled = true;
   }
 }
 
@@ -52,6 +58,7 @@
   for (InternalList::const_iterator iter = frame_request_callbacks_.begin();
        iter != frame_request_callbacks_.end(); ++iter) {
     if (!(*iter)->cancelled) {
+      base::ScopedAsyncTask async_task(debugger_hooks_, iter->get());
       (*iter)->callback.value().Run(animation_time);
     }
   }
diff --git a/src/cobalt/dom/animation_frame_request_callback_list.h b/src/cobalt/dom/animation_frame_request_callback_list.h
index 83a22ae..a809cd9 100644
--- a/src/cobalt/dom/animation_frame_request_callback_list.h
+++ b/src/cobalt/dom/animation_frame_request_callback_list.h
@@ -23,6 +23,10 @@
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/wrappable.h"
 
+namespace base {
+class DebuggerHooks;
+}
+
 namespace cobalt {
 namespace dom {
 
@@ -34,8 +38,9 @@
   typedef script::CallbackFunction<void(double)> FrameRequestCallback;
   typedef script::ScriptValue<FrameRequestCallback> FrameRequestCallbackArg;
 
-  explicit AnimationFrameRequestCallbackList(script::Wrappable* const owner)
-      : owner_(owner) {}
+  explicit AnimationFrameRequestCallbackList(
+      script::Wrappable* const owner, base::DebuggerHooks* const debugger_hooks)
+      : owner_(owner), debugger_hooks_(debugger_hooks) {}
 
   int32 RequestAnimationFrame(
       const FrameRequestCallbackArg& frame_request_callback);
@@ -63,6 +68,7 @@
       InternalList;
 
   script::Wrappable* const owner_;
+  base::DebuggerHooks* const debugger_hooks_;
   // Our list of frame request callbacks.
   InternalList frame_request_callbacks_;
 };
diff --git a/src/cobalt/dom/audio_track_list.h b/src/cobalt/dom/audio_track_list.h
index 130f006..0e5662d 100644
--- a/src/cobalt/dom/audio_track_list.h
+++ b/src/cobalt/dom/audio_track_list.h
@@ -18,6 +18,7 @@
 #include "cobalt/dom/audio_track.h"
 #include "cobalt/dom/html_media_element.h"
 #include "cobalt/dom/track_list_base.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -29,8 +30,9 @@
  public:
   // Custom, not in any spec.
   //
-  explicit AudioTrackList(HTMLMediaElement* media_element)
-      : TrackListBase<AudioTrack>(media_element) {}
+  AudioTrackList(script::EnvironmentSettings* settings,
+                 HTMLMediaElement* media_element)
+      : TrackListBase<AudioTrack>(settings, media_element) {}
 
   // Web API: AudioTrackList
   //
diff --git a/src/cobalt/dom/captions/system_caption_settings.cc b/src/cobalt/dom/captions/system_caption_settings.cc
index 42f65a5..876057b 100644
--- a/src/cobalt/dom/captions/system_caption_settings.cc
+++ b/src/cobalt/dom/captions/system_caption_settings.cc
@@ -35,7 +35,7 @@
 namespace dom {
 namespace captions {
 
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 namespace {
 
 CaptionColor ToCobaltCaptionColor(SbAccessibilityCaptionColor color) {
@@ -173,7 +173,7 @@
 
 }  // namespace
 
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 
 void SystemCaptionSettings::OnCaptionSettingsChanged() {
   DispatchEventNameAndRunCallback(
@@ -182,7 +182,7 @@
 }
 
 base::Optional<std::string> SystemCaptionSettings::background_color() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -200,11 +200,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::background_color_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -215,11 +215,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::background_opacity() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -237,11 +237,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::background_opacity_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -252,11 +252,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::character_edge_style() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -276,11 +276,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::character_edge_style_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -291,11 +291,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::font_color() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -313,11 +313,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::font_color_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -328,11 +328,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::font_family() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -350,11 +350,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::font_family_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -365,11 +365,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::font_opacity() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -387,11 +387,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::font_opacity_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -402,11 +402,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::font_size() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -425,11 +425,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::font_size_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -440,11 +440,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::window_color() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -462,11 +462,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::window_color_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -477,11 +477,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 base::Optional<std::string> SystemCaptionSettings::window_opacity() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -499,11 +499,11 @@
   }
 #else
   return base::nullopt;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 CaptionState SystemCaptionSettings::window_opacity_state() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -514,11 +514,11 @@
   }
 #else
   return CaptionState::kCaptionStateUnsupported;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 bool SystemCaptionSettings::is_enabled() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -527,7 +527,7 @@
 
 #else
   return false;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 void SystemCaptionSettings::set_is_enabled(bool active) {
@@ -535,7 +535,7 @@
 }
 
 bool SystemCaptionSettings::supports_is_enabled() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -544,11 +544,11 @@
 
 #else
   return false;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 bool SystemCaptionSettings::supports_set_enabled() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -557,11 +557,11 @@
 
 #else
   return false;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 bool SystemCaptionSettings::supports_override() {
-#if SB_HAS(CAPTIONS)
+#if SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
   SbAccessibilityCaptionSettings caption_settings;
   SbMemorySet(&caption_settings, 0, sizeof(caption_settings));
   bool success = SbAccessibilityGetCaptionSettings(&caption_settings);
@@ -570,7 +570,7 @@
 
 #else
   return false;
-#endif  // SB_HAS(CAPTIONS)
+#endif  // SB_API_VERSION >= SB_CAPTIONS_REQUIRED_VERSION || SB_HAS(CAPTIONS)
 }
 
 const EventTarget::EventListenerScriptValue* SystemCaptionSettings::onchanged()
diff --git a/src/cobalt/dom/captions/system_caption_settings.h b/src/cobalt/dom/captions/system_caption_settings.h
index e9af9c0..f48c6f8 100644
--- a/src/cobalt/dom/captions/system_caption_settings.h
+++ b/src/cobalt/dom/captions/system_caption_settings.h
@@ -27,6 +27,7 @@
 #include "cobalt/dom/captions/caption_opacity_percentage.h"
 #include "cobalt/dom/captions/caption_state.h"
 #include "cobalt/dom/event_target.h"
+#include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
 namespace dom {
@@ -34,7 +35,8 @@
 
 class SystemCaptionSettings : public EventTarget {
  public:
-  SystemCaptionSettings() {}
+  explicit SystemCaptionSettings(script::EnvironmentSettings* settings)
+      : EventTarget(settings) {}
 
   base::Optional<std::string> background_color();
   CaptionState background_color_state();
diff --git a/src/cobalt/dom/captions/system_caption_settings.idl b/src/cobalt/dom/captions/system_caption_settings.idl
index fbb8d79..46a5bc0 100644
--- a/src/cobalt/dom/captions/system_caption_settings.idl
+++ b/src/cobalt/dom/captions/system_caption_settings.idl
@@ -41,7 +41,6 @@
 // not supported, it will return false. If you try to write to it and doing so
 // is not supported, the value will silently not change.
 
-[Constructor]
 interface SystemCaptionSettings : EventTarget {
 
   // TODO: Make the functions for style properties return nullable enum types
@@ -101,4 +100,4 @@
 
   attribute EventHandler onchanged;
 
-};
\ No newline at end of file
+};
diff --git a/src/cobalt/dom/crypto_test.cc b/src/cobalt/dom/crypto_test.cc
index 2e714a2..f629920 100644
--- a/src/cobalt/dom/crypto_test.cc
+++ b/src/cobalt/dom/crypto_test.cc
@@ -16,6 +16,7 @@
 
 #include "cobalt/dom/crypto.h"
 
+#include "base/test/scoped_task_environment.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/dom_settings.h"
@@ -40,6 +41,7 @@
 
 TEST(CryptoTest, GetRandomValues) {
   StrictMock<MockExceptionState> exception_state;
+  base::test::ScopedTaskEnvironment task_env_;
   std::unique_ptr<script::JavaScriptEngine> javascript_engine =
       script::JavaScriptEngine::CreateEngine();
   scoped_refptr<script::GlobalEnvironment> global_environment =
@@ -92,6 +94,7 @@
   scoped_refptr<Crypto> crypto = new Crypto;
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
+  base::test::ScopedTaskEnvironment task_env_;
   std::unique_ptr<script::JavaScriptEngine> javascript_engine =
       script::JavaScriptEngine::CreateEngine();
   scoped_refptr<script::GlobalEnvironment> global_environment =
@@ -115,6 +118,7 @@
   // QuotaExceededErr.
   scoped_refptr<Crypto> crypto = new Crypto;
   StrictMock<MockExceptionState> exception_state;
+  base::test::ScopedTaskEnvironment task_env_;
   std::unique_ptr<script::JavaScriptEngine> javascript_engine =
       script::JavaScriptEngine::CreateEngine();
   scoped_refptr<script::GlobalEnvironment> global_environment =
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
index 1c12230..5d19f85 100644
--- a/src/cobalt/dom/custom_event_test.cc
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -21,12 +21,12 @@
 #include "base/callback.h"
 #include "base/optional.h"
 #include "base/threading/platform_thread.h"
-#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/css_parser/parser.h"
 #include "cobalt/cssom/viewport_size.h"
 #include "cobalt/dom/custom_event_init.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/testing/gtest_workarounds.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
@@ -60,7 +60,7 @@
  public:
   CustomEventTest()
       : message_loop_(base::MessageLoop::TYPE_DEFAULT),
-        environment_settings_(new script::EnvironmentSettings),
+        environment_settings_(new testing::StubEnvironmentSettings),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_error_callback_)),
         fetcher_factory_(new loader::FetcherFactory(NULL)),
@@ -72,10 +72,10 @@
     engine_ = script::JavaScriptEngine::CreateEngine();
     global_environment_ = engine_->CreateGlobalEnvironment();
     window_ = new Window(
-        ViewportSize(1920, 1080), 1.f, base::kApplicationStateStarted,
-        css_parser_.get(), dom_parser_.get(), fetcher_factory_.get(),
-        loader_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
-        &local_storage_database_, NULL, NULL, NULL, NULL,
+        environment_settings_.get(), ViewportSize(1920, 1080), 1.f,
+        base::kApplicationStateStarted, css_parser_.get(), dom_parser_.get(),
+        fetcher_factory_.get(), loader_factory_.get(), NULL, NULL, NULL, NULL,
+        NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL,
         global_environment_->script_value_factory(), NULL, NULL, url_, "",
         "en-US", "en", base::Callback<void(const GURL&)>(),
         base::Bind(&MockErrorCallback::Run,
@@ -87,8 +87,7 @@
         base::Closure() /* window_minimize */, NULL, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
-        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL,
-        null_debugger_hooks_);
+        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
   }
@@ -98,7 +97,7 @@
  private:
   base::MessageLoop message_loop_;
   std::unique_ptr<script::JavaScriptEngine> engine_;
-  const std::unique_ptr<script::EnvironmentSettings> environment_settings_;
+  const std::unique_ptr<testing::StubEnvironmentSettings> environment_settings_;
   scoped_refptr<script::GlobalEnvironment> global_environment_;
   MockErrorCallback mock_error_callback_;
   std::unique_ptr<css_parser::Parser> css_parser_;
@@ -107,7 +106,6 @@
   std::unique_ptr<loader::LoaderFactory> loader_factory_;
   dom::LocalStorageDatabase local_storage_database_;
   GURL url_;
-  base::NullDebuggerHooks null_debugger_hooks_;
   scoped_refptr<Window> window_;
 };
 
diff --git a/src/cobalt/dom/directionality.h b/src/cobalt/dom/directionality.h
index 0ae5659..5c6f825 100644
--- a/src/cobalt/dom/directionality.h
+++ b/src/cobalt/dom/directionality.h
@@ -20,10 +20,8 @@
 
 // The enum Directionality is used to track the explicit direction of the html
 // element:
-// https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
-// NOTE: Value "auto" is not supported.
+//   https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#the-directionality
 enum Directionality {
-  kNoExplicitDirectionality,
   kLeftToRightDirectionality,
   kRightToLeftDirectionality,
 };
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index f14bfa4..ab3e410 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -72,7 +72,7 @@
 
 Document::Document(HTMLElementContext* html_element_context,
                    const Options& options)
-    : ALLOW_THIS_IN_INITIALIZER_LIST(Node(this)),
+    : ALLOW_THIS_IN_INITIALIZER_LIST(Node(html_element_context, this)),
       html_element_context_(html_element_context),
       page_visibility_state_(html_element_context_->page_visibility_state()),
       window_(options.window),
@@ -286,6 +286,33 @@
 
 const scoped_refptr<Location>& Document::location() const { return location_; }
 
+// Algorithm for dir:
+//   https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#dom-dir
+std::string Document::dir() const {
+  // The dir IDL attribute on Document objects must reflect the dir content
+  // attribute of the html element, if any, limited to only known values. If
+  // there is no such element, then the attribute must return the empty string
+  // and do nothing on setting.
+  HTMLHtmlElement* html_element = html();
+  if (!html_element) {
+    return "";
+  }
+  return html_element->dir();
+}
+
+// Algorithm for dir:
+//   https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#dom-dir
+void Document::set_dir(const std::string& value) {
+  // The dir IDL attribute on Document objects must reflect the dir content
+  // attribute of the html element, if any, limited to only known values. If
+  // there is no such element, then the attribute must return the empty string
+  // and do nothing on setting.
+  HTMLHtmlElement* html_element = html();
+  if (html_element) {
+    html_element->set_dir(value);
+  }
+}
+
 // Algorithm for body:
 //   https://www.w3.org/TR/html5/dom.html#dom-document-body
 scoped_refptr<HTMLBodyElement> Document::body() const {
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index 2cc9f32..19eb1d3 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -194,6 +194,9 @@
   //
   const scoped_refptr<Location>& location() const;
 
+  std::string dir() const;
+  void set_dir(const std::string& value);
+
   scoped_refptr<HTMLBodyElement> body() const;
   void set_body(const scoped_refptr<HTMLBodyElement>& body);
 
diff --git a/src/cobalt/dom/document_html5.idl b/src/cobalt/dom/document_html5.idl
index 9cee3f0..cd0b0fd 100644
--- a/src/cobalt/dom/document_html5.idl
+++ b/src/cobalt/dom/document_html5.idl
@@ -17,6 +17,7 @@
 [OverrideBuiltins]
 partial /*sealed*/ interface Document {
   [PutForwards=href, Unforgeable] readonly attribute Location? location;
+  attribute DOMString dir;
   // body attribute is changed, from the spec's:
   //   attribute HTMLElement? body;
   // This is because we don't support frameset element, body has to be an
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index 3a7c4f9..e2cd356 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/document.h"
 
+#include <memory>
+
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/css_parser/parser.h"
 #include "cobalt/cssom/css_style_sheet.h"
@@ -39,6 +39,7 @@
 #include "cobalt/dom/node_list.h"
 #include "cobalt/dom/testing/gtest_workarounds.h"
 #include "cobalt/dom/testing/html_collection_testing.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/text.h"
 #include "cobalt/dom/ui_event.h"
 #include "cobalt/script/testing/mock_exception_state.h"
@@ -48,10 +49,10 @@
 namespace dom {
 namespace {
 
+using script::testing::MockExceptionState;
+using ::testing::_;
 using ::testing::SaveArg;
 using ::testing::StrictMock;
-using ::testing::_;
-using script::testing::MockExceptionState;
 
 //////////////////////////////////////////////////////////////////////////
 // DocumentTest
@@ -63,6 +64,7 @@
   ~DocumentTest() override;
 
   base::MessageLoop message_loop_;
+  testing::StubEnvironmentSettings environment_settings_;
   std::unique_ptr<css_parser::Parser> css_parser_;
   std::unique_ptr<DomStatTracker> dom_stat_tracker_;
   HTMLElementContext html_element_context_;
@@ -71,10 +73,10 @@
 DocumentTest::DocumentTest()
     : css_parser_(css_parser::Parser::Create()),
       dom_stat_tracker_(new DomStatTracker("DocumentTest")),
-      html_element_context_(NULL, NULL, css_parser_.get(), NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, dom_stat_tracker_.get(), "",
-                            base::kApplicationStateStarted, NULL) {
+      html_element_context_(
+          &environment_settings_, NULL, NULL, css_parser_.get(), NULL, NULL,
+          NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+          dom_stat_tracker_.get(), "", base::kApplicationStateStarted, NULL) {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 }
 
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index e4a81eb..0eceae5 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -124,6 +124,8 @@
         'event_queue.h',
         'event_target.cc',
         'event_target.h',
+        'event_target_listener_info.cc',
+        'event_target_listener_info.h',
         'focus_event.cc',
         'focus_event.h',
         'focus_event_init.h',
@@ -135,8 +137,6 @@
         'font_face_updater.h',
         'font_list.cc',
         'font_list.h',
-        'generic_event_handler_reference.cc',
-        'generic_event_handler_reference.h',
         'global_stats.cc',
         'global_stats.h',
         'history.cc',
diff --git a/src/cobalt/dom/dom_parser_test.cc b/src/cobalt/dom/dom_parser_test.cc
index 5f0bf77..4d159e3 100644
--- a/src/cobalt/dom/dom_parser_test.cc
+++ b/src/cobalt/dom/dom_parser_test.cc
@@ -12,14 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/dom/dom_parser.h"
+
 #include <memory>
 #include <string>
 
 #include "base/threading/platform_thread.h"
 #include "cobalt/dom/document.h"
-#include "cobalt/dom/dom_parser.h"
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/dom/testing/stub_css_parser.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_script_runner.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
@@ -34,6 +36,7 @@
   DOMParserTest();
   ~DOMParserTest() override {}
 
+  testing::StubEnvironmentSettings environment_settings_;
   loader::FetcherFactory fetcher_factory_;
   loader::LoaderFactory loader_factory_;
   testing::StubCSSParser stub_css_parser_;
@@ -50,12 +53,12 @@
           0 /* encoded_image_cache_capacity */, base::ThreadPriority::DEFAULT),
       dom_parser_parser_(new dom_parser::Parser()),
       html_element_context_(
-          &fetcher_factory_, &loader_factory_, &stub_css_parser_,
-          dom_parser_parser_.get(), NULL /* can_play_type_handler */,
-          NULL /* web_media_player_factory */, &stub_script_runner_,
-          NULL /* script_value_factory */, NULL /* media_source_registry */,
-          NULL /* resource_provider */, NULL /* animated_image_tracker */,
-          NULL /* image_cache */,
+          &environment_settings_, &fetcher_factory_, &loader_factory_,
+          &stub_css_parser_, dom_parser_parser_.get(),
+          NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
+          &stub_script_runner_, NULL /* script_value_factory */,
+          NULL /* media_source_registry */, NULL /* resource_provider */,
+          NULL /* animated_image_tracker */, NULL /* image_cache */,
           NULL /* reduced_image_cache_capacity_manager */,
           NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
           NULL /* dom_stat_tracker */, "" /* language */,
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc
index 66ef360..9490e3b 100644
--- a/src/cobalt/dom/dom_settings.cc
+++ b/src/cobalt/dom/dom_settings.cc
@@ -23,23 +23,24 @@
 
 DOMSettings::DOMSettings(
     const int max_dom_element_depth, loader::FetcherFactory* fetcher_factory,
-    network::NetworkModule* network_module, const scoped_refptr<Window>& window,
+    network::NetworkModule* network_module,
     MediaSourceRegistry* media_source_registry, Blob::Registry* blob_registry,
     media::CanPlayTypeHandler* can_play_type_handler,
     script::JavaScriptEngine* engine,
     script::GlobalEnvironment* global_environment,
+    base::DebuggerHooks* debugger_hooks,
     MutationObserverTaskManager* mutation_observer_task_manager,
     const Options& options)
     : max_dom_element_depth_(max_dom_element_depth),
       microphone_options_(options.microphone_options),
       fetcher_factory_(fetcher_factory),
       network_module_(network_module),
-      window_(window),
       media_source_registry_(media_source_registry),
       blob_registry_(blob_registry),
       can_play_type_handler_(can_play_type_handler),
       javascript_engine_(engine),
       global_environment_(global_environment),
+      debugger_hooks_(debugger_hooks),
       mutation_observer_task_manager_(mutation_observer_task_manager) {}
 
 DOMSettings::~DOMSettings() {}
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h
index b83a49b..804b02a 100644
--- a/src/cobalt/dom/dom_settings.h
+++ b/src/cobalt/dom/dom_settings.h
@@ -16,6 +16,7 @@
 #define COBALT_DOM_DOM_SETTINGS_H_
 
 #include "base/memory/ref_counted.h"
+#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/dom/blob.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
 #include "cobalt/dom/url_registry.h"
@@ -35,7 +36,7 @@
 namespace script {
 class GlobalEnvironment;
 class JavaScriptEngine;
-}
+}  // namespace script
 namespace dom {
 class MediaSource;
 class Window;
@@ -54,12 +55,12 @@
   DOMSettings(const int max_dom_element_depth,
               loader::FetcherFactory* fetcher_factory,
               network::NetworkModule* network_module,
-              const scoped_refptr<Window>& window,
               MediaSourceRegistry* media_source_registry,
               Blob::Registry* blob_registry,
               media::CanPlayTypeHandler* can_play_type_handler,
               script::JavaScriptEngine* engine,
               script::GlobalEnvironment* global_environment_proxy,
+              base::DebuggerHooks* debugger_hooks,
               MutationObserverTaskManager* mutation_observer_task_manager,
               const Options& options = Options());
   ~DOMSettings() override;
@@ -92,6 +93,7 @@
   media::CanPlayTypeHandler* can_play_type_handler() const {
     return can_play_type_handler_;
   }
+  base::DebuggerHooks* debugger_hooks() const { return debugger_hooks_; }
   MutationObserverTaskManager* mutation_observer_task_manager() const {
     return mutation_observer_task_manager_;
   }
@@ -114,6 +116,7 @@
   media::CanPlayTypeHandler* can_play_type_handler_;
   script::JavaScriptEngine* javascript_engine_;
   script::GlobalEnvironment* global_environment_;
+  base::DebuggerHooks* debugger_hooks_;
   MutationObserverTaskManager* mutation_observer_task_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(DOMSettings);
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc
index ab4ec8f..f30ffa3 100644
--- a/src/cobalt/dom/element_test.cc
+++ b/src/cobalt/dom/element_test.cc
@@ -33,6 +33,7 @@
 #include "cobalt/dom/node_list.h"
 #include "cobalt/dom/testing/gtest_workarounds.h"
 #include "cobalt/dom/testing/html_collection_testing.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/text.h"
 #include "cobalt/dom/xml_document.h"
 #include "cobalt/dom_parser/parser.h"
@@ -50,6 +51,7 @@
   ElementTest();
   ~ElementTest() override;
 
+  testing::StubEnvironmentSettings environment_settings_;
   std::unique_ptr<css_parser::Parser> css_parser_;
   std::unique_ptr<dom_parser::Parser> dom_parser_;
   std::unique_ptr<DomStatTracker> dom_stat_tracker_;
@@ -62,9 +64,10 @@
     : css_parser_(css_parser::Parser::Create()),
       dom_parser_(new dom_parser::Parser()),
       dom_stat_tracker_(new DomStatTracker("ElementTest")),
-      html_element_context_(NULL, NULL, css_parser_.get(), dom_parser_.get(),
+      html_element_context_(&environment_settings_, NULL, NULL,
+                            css_parser_.get(), dom_parser_.get(), NULL, NULL,
                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, dom_stat_tracker_.get(), "",
+                            NULL, dom_stat_tracker_.get(), "",
                             base::kApplicationStateStarted, NULL) {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/eme/media_key_session.cc b/src/cobalt/dom/eme/media_key_session.cc
index 8cf2253..3570c14 100644
--- a/src/cobalt/dom/eme/media_key_session.cc
+++ b/src/cobalt/dom/eme/media_key_session.cc
@@ -35,10 +35,12 @@
 // See step 3.1 of
 // https://www.w3.org/TR/encrypted-media/#dom-mediakeys-createsession.
 MediaKeySession::MediaKeySession(
+    script::EnvironmentSettings* settings,
     const scoped_refptr<media::DrmSystem>& drm_system,
     script::ScriptValueFactory* script_value_factory,
     const ClosedCallback& closed_callback)
-    : ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
+    : EventTarget(settings),
+      ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
       drm_system_(drm_system),
       drm_system_session_(drm_system->CreateSession(
           base::Bind(&MediaKeySession::OnSessionUpdateKeyStatuses,
diff --git a/src/cobalt/dom/eme/media_key_session.h b/src/cobalt/dom/eme/media_key_session.h
index d8acc09..e95c7de 100644
--- a/src/cobalt/dom/eme/media_key_session.h
+++ b/src/cobalt/dom/eme/media_key_session.h
@@ -47,7 +47,8 @@
   typedef base::Callback<void(MediaKeySession* session)> ClosedCallback;
 
   // Custom, not in any spec.
-  MediaKeySession(const scoped_refptr<media::DrmSystem>& drm_system,
+  MediaKeySession(script::EnvironmentSettings* settings,
+                  const scoped_refptr<media::DrmSystem>& drm_system,
                   script::ScriptValueFactory* script_value_factory,
                   const ClosedCallback& closed_callback);
 
diff --git a/src/cobalt/dom/eme/media_key_system_access.cc b/src/cobalt/dom/eme/media_key_system_access.cc
index 602069c..988fe43 100644
--- a/src/cobalt/dom/eme/media_key_system_access.cc
+++ b/src/cobalt/dom/eme/media_key_system_access.cc
@@ -36,7 +36,8 @@
 // See
 // https://www.w3.org/TR/encrypted-media/#dom-mediakeysystemaccess-createmediakeys.
 script::Handle<MediaKeySystemAccess::InterfacePromise>
-MediaKeySystemAccess::CreateMediaKeys() const {
+MediaKeySystemAccess::CreateMediaKeys(
+    script::EnvironmentSettings* settings) const {
   // 1. Let promise be a new promise.
   script::Handle<MediaKeySystemAccess::InterfacePromise> promise =
       script_value_factory_->CreateInterfacePromise<scoped_refptr<MediaKeys>>();
@@ -59,7 +60,7 @@
   // 2.10. Let media keys be a new MediaKeys object.
   // 2.10.5. Let the cdm instance value be instance.
   scoped_refptr<MediaKeys> media_keys(
-      new MediaKeys(drm_system, script_value_factory_));
+      new MediaKeys(settings, drm_system, script_value_factory_));
 
   // 2.11. Resolve promise with media keys.
   promise->Resolve(media_keys);
diff --git a/src/cobalt/dom/eme/media_key_system_access.h b/src/cobalt/dom/eme/media_key_system_access.h
index 0f18419..5849374 100644
--- a/src/cobalt/dom/eme/media_key_system_access.h
+++ b/src/cobalt/dom/eme/media_key_system_access.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "cobalt/dom/eme/media_key_system_configuration.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value_factory.h"
 #include "cobalt/script/wrappable.h"
@@ -42,7 +43,8 @@
   const MediaKeySystemConfiguration& GetConfiguration() const {
     return configuration_;
   }
-  script::Handle<InterfacePromise> CreateMediaKeys() const;
+  script::Handle<InterfacePromise> CreateMediaKeys(
+      script::EnvironmentSettings* settings) const;
 
   DEFINE_WRAPPABLE_TYPE(MediaKeySystemAccess);
 
diff --git a/src/cobalt/dom/eme/media_key_system_access.idl b/src/cobalt/dom/eme/media_key_system_access.idl
index 3dae4e1..319ba16 100644
--- a/src/cobalt/dom/eme/media_key_system_access.idl
+++ b/src/cobalt/dom/eme/media_key_system_access.idl
@@ -17,5 +17,5 @@
 interface MediaKeySystemAccess {
   readonly attribute DOMString keySystem;
   MediaKeySystemConfiguration getConfiguration();
-  Promise<MediaKeys> createMediaKeys();
+  [CallWith=EnvironmentSettings] Promise<MediaKeys> createMediaKeys();
 };
diff --git a/src/cobalt/dom/eme/media_keys.cc b/src/cobalt/dom/eme/media_keys.cc
index b7c94d6..26376f8 100644
--- a/src/cobalt/dom/eme/media_keys.cc
+++ b/src/cobalt/dom/eme/media_keys.cc
@@ -23,10 +23,14 @@
 namespace dom {
 namespace eme {
 
-MediaKeys::MediaKeys(const scoped_refptr<media::DrmSystem>& drm_system,
+MediaKeys::MediaKeys(script::EnvironmentSettings* settings,
+                     const scoped_refptr<media::DrmSystem>& drm_system,
                      script::ScriptValueFactory* script_value_factory)
-    : script_value_factory_(script_value_factory), drm_system_(drm_system) {
-  SB_DCHECK(drm_system_->is_valid()) << "DrmSystem provided on initialization is invalid.";
+    : settings_(settings),
+      script_value_factory_(script_value_factory),
+      drm_system_(drm_system) {
+  SB_DCHECK(drm_system_->is_valid())
+      << "DrmSystem provided on initialization is invalid.";
 }
 
 // See https://www.w3.org/TR/encrypted-media/#dom-mediakeys-createsession.
@@ -44,7 +48,7 @@
   // |MediaKeys| are passed to |MediaKeySession| as weak pointer because the
   // order of destruction is not guaranteed due to JavaScript memory management.
   scoped_refptr<MediaKeySession> session(new MediaKeySession(
-      drm_system_, script_value_factory_,
+      settings_, drm_system_, script_value_factory_,
       base::Bind(&MediaKeys::OnSessionClosed, AsWeakPtr())));
   open_sessions_.push_back(session);
   return session;
diff --git a/src/cobalt/dom/eme/media_keys.h b/src/cobalt/dom/eme/media_keys.h
index d276af7..313f52f 100644
--- a/src/cobalt/dom/eme/media_keys.h
+++ b/src/cobalt/dom/eme/media_keys.h
@@ -24,6 +24,7 @@
 #include "cobalt/dom/eme/media_key_session.h"
 #include "cobalt/dom/eme/media_key_session_type.h"
 #include "cobalt/media/base/drm_system.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/script_value_factory.h"
 #include "cobalt/script/wrappable.h"
 #include "starboard/drm.h"
@@ -42,7 +43,8 @@
   typedef script::Handle<script::Promise<bool>> BoolPromiseHandle;
   typedef script::ScriptValue<script::Promise<bool>> BoolPromiseValue;
 
-  MediaKeys(const scoped_refptr<media::DrmSystem>& drm_system,
+  MediaKeys(script::EnvironmentSettings* settings,
+            const scoped_refptr<media::DrmSystem>& drm_system,
             script::ScriptValueFactory* script_value_factory);
 
   scoped_refptr<media::DrmSystem> drm_system() const { return drm_system_; }
@@ -66,6 +68,7 @@
       BoolPromiseValue::Reference* promise_reference, SbDrmStatus status,
       const std::string& error_message);
 
+  script::EnvironmentSettings* settings_;
   script::ScriptValueFactory* script_value_factory_;
   scoped_refptr<media::DrmSystem> drm_system_;
 
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
index abf844b..05d7bce 100644
--- a/src/cobalt/dom/error_event_test.cc
+++ b/src/cobalt/dom/error_event_test.cc
@@ -21,12 +21,12 @@
 #include "base/callback.h"
 #include "base/optional.h"
 #include "base/threading/platform_thread.h"
-#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/css_parser/parser.h"
 #include "cobalt/cssom/viewport_size.h"
 #include "cobalt/dom/error_event_init.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/testing/gtest_workarounds.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
@@ -60,7 +60,7 @@
  public:
   ErrorEventTest()
       : message_loop_(base::MessageLoop::TYPE_DEFAULT),
-        environment_settings_(new script::EnvironmentSettings),
+        environment_settings_(new testing::StubEnvironmentSettings),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_load_complete_callback_)),
         fetcher_factory_(new loader::FetcherFactory(NULL)),
@@ -74,11 +74,12 @@
 
     ViewportSize view_size(1920, 1080);
     window_ = new Window(
-        view_size, 1.f, base::kApplicationStateStarted, css_parser_.get(),
-        dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(), NULL,
-        NULL, NULL, NULL, NULL, NULL, &local_storage_database_, NULL, NULL,
-        NULL, NULL, global_environment_->script_value_factory(), NULL, NULL,
-        url_, "", "en-US", "en", base::Callback<void(const GURL&)>(),
+        environment_settings_.get(), view_size, 1.f,
+        base::kApplicationStateStarted, css_parser_.get(), dom_parser_.get(),
+        fetcher_factory_.get(), loader_factory_.get(), NULL, NULL, NULL, NULL,
+        NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL,
+        global_environment_->script_value_factory(), NULL, NULL, url_, "",
+        "en-US", "en", base::Callback<void(const GURL&)>(),
         base::Bind(&MockLoadCompleteCallback::Run,
                    base::Unretained(&mock_load_complete_callback_)),
         NULL, network_bridge::PostSender(), csp::kCSPRequired,
@@ -88,8 +89,7 @@
         base::Closure() /* window_minimize */, NULL, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
-        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL,
-        null_debugger_hooks_);
+        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
 
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
@@ -100,7 +100,7 @@
  private:
   base::MessageLoop message_loop_;
   std::unique_ptr<script::JavaScriptEngine> engine_;
-  const std::unique_ptr<script::EnvironmentSettings> environment_settings_;
+  const std::unique_ptr<testing::StubEnvironmentSettings> environment_settings_;
   scoped_refptr<script::GlobalEnvironment> global_environment_;
   MockLoadCompleteCallback mock_load_complete_callback_;
   std::unique_ptr<css_parser::Parser> css_parser_;
@@ -109,7 +109,6 @@
   std::unique_ptr<loader::LoaderFactory> loader_factory_;
   dom::LocalStorageDatabase local_storage_database_;
   GURL url_;
-  base::NullDebuggerHooks null_debugger_hooks_;
   scoped_refptr<Window> window_;
 };
 
diff --git a/src/cobalt/dom/event_queue_test.cc b/src/cobalt/dom/event_queue_test.cc
index a76a29d..625cafb 100644
--- a/src/cobalt/dom/event_queue_test.cc
+++ b/src/cobalt/dom/event_queue_test.cc
@@ -12,21 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/event_queue.h"
 
+#include <memory>
+
 #include "base/message_loop/message_loop.h"
 #include "cobalt/dom/event.h"
 #include "cobalt/dom/event_target.h"
 #include "cobalt/dom/testing/mock_event_listener.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/script/testing/fake_script_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::_;
 using ::testing::AllOf;
 using ::testing::Eq;
 using ::testing::Property;
-using ::testing::_;
 
 namespace cobalt {
 namespace dom {
@@ -48,11 +49,13 @@
                     _))
         .RetiresOnSaturation();
   }
+  testing::StubEnvironmentSettings environment_settings_;
   base::MessageLoop message_loop_;
 };
 
 TEST_F(EventQueueTest, EventWithoutTargetTest) {
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("event"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
@@ -68,7 +71,8 @@
 }
 
 TEST_F(EventQueueTest, EventWithTargetTest) {
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("event"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
@@ -85,7 +89,8 @@
 }
 
 TEST_F(EventQueueTest, CancelAllEventsTest) {
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("event"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
@@ -105,8 +110,10 @@
 // correctness of event propagation like capturing or bubbling are tested in
 // the unit tests of EventTarget.
 TEST_F(EventQueueTest, EventWithDifferentTargetTest) {
-  scoped_refptr<EventTarget> event_target_1 = new EventTarget;
-  scoped_refptr<EventTarget> event_target_2 = new EventTarget;
+  scoped_refptr<EventTarget> event_target_1 =
+      new EventTarget(&environment_settings_);
+  scoped_refptr<EventTarget> event_target_2 =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("event"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
diff --git a/src/cobalt/dom/event_target.cc b/src/cobalt/dom/event_target.cc
index f256173..367d872 100644
--- a/src/cobalt/dom/event_target.cc
+++ b/src/cobalt/dom/event_target.cc
@@ -12,16 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/event_target.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/global_stats.h"
 #include "cobalt/xhr/xml_http_request_event_target.h"
 #include "nb/memory_scope.h"
@@ -29,6 +31,16 @@
 namespace cobalt {
 namespace dom {
 
+EventTarget::EventTarget(
+    script::EnvironmentSettings* settings,
+    UnpackOnErrorEventsBool onerror_event_parameter_handling)
+    : debugger_hooks_(
+          base::polymorphic_downcast<DOMSettings*>(settings)->debugger_hooks()),
+      unpack_onerror_events_(onerror_event_parameter_handling ==
+                             kUnpackOnErrorEvents) {
+  DCHECK(debugger_hooks_);
+}
+
 void EventTarget::AddEventListener(const std::string& type,
                                    const EventListenerScriptValue& listener,
                                    bool use_capture) {
@@ -37,11 +49,9 @@
     return;
   }
 
-  std::unique_ptr<GenericEventHandlerReference> listener_reference(
-      new GenericEventHandlerReference(this, listener));
-
-  AddEventListenerInternal(base::Token(type), std::move(listener_reference),
-                           use_capture, kNotAttribute);
+  AddEventListenerInternal(base::WrapUnique(new EventTargetListenerInfo(
+      this, base::Token(type), EventTargetListenerInfo::kAddEventListener,
+      use_capture, listener)));
 }
 
 void EventTarget::RemoveEventListener(const std::string& type,
@@ -52,11 +62,13 @@
     return;
   }
 
+  EventTargetListenerInfo listener_info(
+      this, base::Token(type), EventTargetListenerInfo::kAddEventListener,
+      use_capture, listener);
   for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
        iter != event_listener_infos_.end(); ++iter) {
-    if ((*iter)->listener_type == kNotAttribute &&
-        (*iter)->type == type.c_str() && (*iter)->listener->EqualTo(listener) &&
-        (*iter)->use_capture == use_capture) {
+    if ((*iter)->EqualTo(listener_info)) {
+      debugger_hooks_->AsyncTaskCanceled((*iter)->task());
       event_listener_infos_.erase(iter);
       return;
     }
@@ -157,69 +169,54 @@
     base::Token type, const EventListenerScriptValue& listener) {
   DCHECK(!unpack_onerror_events_ || type != base::Tokens::error());
 
-  std::unique_ptr<GenericEventHandlerReference> listener_reference(
-      new GenericEventHandlerReference(this, listener));
-  SetAttributeEventListenerInternal(type, std::move(listener_reference));
+  AddEventListenerInternal(base::WrapUnique(new EventTargetListenerInfo(
+      this, type, EventTargetListenerInfo::kSetAttribute,
+      false /* use_capture */, listener)));
 }
 
 const EventTarget::EventListenerScriptValue*
 EventTarget::GetAttributeEventListener(base::Token type) const {
   DCHECK(!unpack_onerror_events_ || type != base::Tokens::error());
 
-  GenericEventHandlerReference* handler =
+  EventTargetListenerInfo* listener_info =
       GetAttributeEventListenerInternal(type);
-  return handler ? handler->event_listener_value() : NULL;
+  return listener_info ? listener_info->event_listener_value() : NULL;
 }
 
 void EventTarget::SetAttributeOnErrorEventListener(
     base::Token type, const OnErrorEventListenerScriptValue& listener) {
   DCHECK_EQ(base::Tokens::error(), type);
 
-  std::unique_ptr<GenericEventHandlerReference> listener_reference(
-      new GenericEventHandlerReference(this, listener));
-  SetAttributeEventListenerInternal(type, std::move(listener_reference));
+  AddEventListenerInternal(base::WrapUnique(new EventTargetListenerInfo(
+      this, type, EventTargetListenerInfo::kSetAttribute,
+      false /* use_capture */, unpack_onerror_events_, listener)));
 }
 
 const EventTarget::OnErrorEventListenerScriptValue*
 EventTarget::GetAttributeOnErrorEventListener(base::Token type) const {
   DCHECK_EQ(base::Tokens::error(), type);
 
-  GenericEventHandlerReference* handler =
+  EventTargetListenerInfo* listener_info =
       GetAttributeEventListenerInternal(type);
-  return handler ? handler->on_error_event_listener_value() : NULL;
+  return listener_info ? listener_info->on_error_event_listener_value() : NULL;
 }
 
 bool EventTarget::HasOneOrMoreAttributeEventListener() const {
   for (EventListenerInfos::const_iterator iter = event_listener_infos_.begin();
        iter != event_listener_infos_.end(); ++iter) {
-    if ((*iter)->listener_type == kAttribute) {
+    if ((*iter)->is_attribute()) {
       return true;
     }
   }
   return false;
 }
 
-void EventTarget::SetAttributeEventListenerInternal(
-    base::Token type,
-    std::unique_ptr<GenericEventHandlerReference> event_handler) {
-  // Remove existing attribute listener of the same type.
-  for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
-       iter != event_listener_infos_.end(); ++iter) {
-    if ((*iter)->listener_type == kAttribute && (*iter)->type == type) {
-      event_listener_infos_.erase(iter);
-      break;
-    }
-  }
-
-  AddEventListenerInternal(type, std::move(event_handler), false, kAttribute);
-}
-
-GenericEventHandlerReference* EventTarget::GetAttributeEventListenerInternal(
+EventTargetListenerInfo* EventTarget::GetAttributeEventListenerInternal(
     base::Token type) const {
   for (EventListenerInfos::const_iterator iter = event_listener_infos_.begin();
        iter != event_listener_infos_.end(); ++iter) {
-    if ((*iter)->listener_type == kAttribute && (*iter)->type == type) {
-      return (*iter)->listener.get();
+    if ((*iter)->is_attribute() && (*iter)->type() == type) {
+      return iter->get();
     }
   }
   return NULL;
@@ -235,12 +232,9 @@
   EventListenerInfos event_listener_infos;
   for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
        iter != event_listener_infos_.end(); ++iter) {
-    if ((*iter)->type == event->type()) {
-      event_listener_infos.emplace_back(new EventListenerInfo(
-          (*iter)->type,
-          base::WrapUnique(
-              new GenericEventHandlerReference(this, *(*iter)->listener)),
-          (*iter)->use_capture, (*iter)->listener_type));
+    if ((*iter)->type() == event->type()) {
+      event_listener_infos.emplace_back(
+          base::WrapUnique(new EventTargetListenerInfo(this, **iter)));
     }
   }
 
@@ -251,16 +245,17 @@
     }
     // Only call listeners marked as capture during capturing phase.
     if (event->event_phase() == Event::kCapturingPhase &&
-        !(*iter)->use_capture) {
+        !(*iter)->use_capture()) {
       continue;
     }
     // Don't call any listeners marked as capture during bubbling phase.
-    if (event->event_phase() == Event::kBubblingPhase && (*iter)->use_capture) {
+    if (event->event_phase() == Event::kBubblingPhase &&
+        (*iter)->use_capture()) {
       continue;
     }
 
-    (*iter)->listener->HandleEvent(event, (*iter)->listener_type == kAttribute,
-                                   unpack_onerror_events_);
+    base::ScopedAsyncTask async_task(debugger_hooks_, (*iter)->task());
+    (*iter)->HandleEvent(event);
   }
 
   event->set_current_target(NULL);
@@ -273,27 +268,38 @@
 }
 
 void EventTarget::AddEventListenerInternal(
-    base::Token type, std::unique_ptr<GenericEventHandlerReference> listener,
-    bool use_capture, Type listener_type) {
+    std::unique_ptr<EventTargetListenerInfo> listener_info) {
   TRACK_MEMORY_SCOPE("DOM");
 
-  if (listener->IsNull()) {
+  // Remove existing attribute listener of the same type.
+  if (listener_info->is_attribute()) {
+    for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
+         iter != event_listener_infos_.end(); ++iter) {
+      if ((*iter)->is_attribute() && (*iter)->type() == listener_info->type()) {
+        debugger_hooks_->AsyncTaskCanceled((*iter)->task());
+        event_listener_infos_.erase(iter);
+        break;
+      }
+    }
+  }
+
+  if (listener_info->IsNull()) {
     return;
   }
 
   for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
        iter != event_listener_infos_.end(); ++iter) {
-    if ((*iter)->type == type && (*iter)->listener->EqualTo(*listener) &&
-        (*iter)->use_capture == use_capture &&
-        (*iter)->listener_type == listener_type) {
+    if ((*iter)->EqualTo(*listener_info)) {
       // Attribute listeners should have already been removed.
-      DCHECK_EQ(listener_type, kNotAttribute);
+      DCHECK(!listener_info->is_attribute());
       return;
     }
   }
 
-  event_listener_infos_.emplace_back(new EventListenerInfo(
-      type, std::move(listener), use_capture, listener_type));
+  debugger_hooks_->AsyncTaskScheduled(
+      listener_info->task(), listener_info->type().c_str(),
+      base::DebuggerHooks::AsyncTaskFrequency::kRecurring);
+  event_listener_infos_.push_back(std::move(listener_info));
 }
 
 bool EventTarget::HasEventListener(base::Token type) {
@@ -301,26 +307,12 @@
 
   for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
        iter != event_listener_infos_.end(); ++iter) {
-    if ((*iter)->type == type) {
+    if ((*iter)->type() == type) {
       return true;
     }
   }
   return false;
 }
 
-EventTarget::EventListenerInfo::EventListenerInfo(
-    base::Token type, std::unique_ptr<GenericEventHandlerReference> listener,
-    bool use_capture, Type listener_type)
-    : type(type),
-      listener(std::move(listener)),
-      use_capture(use_capture),
-      listener_type(listener_type) {
-  GlobalStats::GetInstance()->AddEventListener();
-}
-
-EventTarget::EventListenerInfo::~EventListenerInfo() {
-  GlobalStats::GetInstance()->RemoveEventListener();
-}
-
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h
index b4e9bd9..82715ce 100644
--- a/src/cobalt/dom/event_target.h
+++ b/src/cobalt/dom/event_target.h
@@ -23,13 +23,15 @@
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/base/token.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/event.h"
 #include "cobalt/dom/event_listener.h"
-#include "cobalt/dom/generic_event_handler_reference.h"
+#include "cobalt/dom/event_target_listener_info.h"
 #include "cobalt/dom/on_error_event_listener.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/wrappable.h"
@@ -45,13 +47,6 @@
 class EventTarget : public script::Wrappable,
                     public base::SupportsWeakPtr<EventTarget> {
  public:
-  // EventHandlers are implemented as EventListener?, so use this to
-  // differentiate between the two.
-  enum Type {
-    kAttribute,
-    kNotAttribute,
-  };
-
   // Helper enum to decide whether or not onerror event parameters should be
   // unpacked or not (e.g. in the special case of the |window| object).
   // This special handling is described in:
@@ -65,13 +60,11 @@
   // The parameter |unpack_onerror_events| can be set to true (e.g. for the
   // |window| object) in order to indicate that the ErrorEvent should have
   // its members unpacked before calling its event handler.  This is to
-  // accommodate for a special case in the window.onerror handling.  This
-  // special handling
+  // accommodate for a special case in the window.onerror handling.
   explicit EventTarget(
+      script::EnvironmentSettings* settings,
       UnpackOnErrorEventsBool onerror_event_parameter_handling =
-          kDoNotUnpackOnErrorEvents)
-      : unpack_onerror_events_(onerror_event_parameter_handling ==
-                               kUnpackOnErrorEvents) {}
+          kDoNotUnpackOnErrorEvents);
 
   typedef script::ScriptValue<EventListener> EventListenerScriptValue;
   typedef script::ScriptValue<OnErrorEventListener>
@@ -476,32 +469,24 @@
   DEFINE_WRAPPABLE_TYPE(EventTarget);
   void TraceMembers(script::Tracer* tracer) override;
 
- private:
-  struct EventListenerInfo {
-    EventListenerInfo(base::Token type,
-                      std::unique_ptr<GenericEventHandlerReference> listener,
-                      bool use_capture, Type listener_type);
-    ~EventListenerInfo();
+  base::DebuggerHooks* debugger_hooks() { return debugger_hooks_; }
 
-    base::Token type;
-    std::unique_ptr<GenericEventHandlerReference> listener;
-    bool use_capture;
-    Type listener_type;
-  };
-  typedef std::vector<std::unique_ptr<EventListenerInfo>> EventListenerInfos;
+ private:
+  typedef std::vector<std::unique_ptr<EventTargetListenerInfo>>
+      EventListenerInfos;
 
   void SetAttributeEventListenerInternal(
-      base::Token type,
-      std::unique_ptr<GenericEventHandlerReference> event_handler);
-  GenericEventHandlerReference* GetAttributeEventListenerInternal(
+      std::unique_ptr<EventTargetListenerInfo> event_handler);
+  EventTargetListenerInfo* GetAttributeEventListenerInternal(
       base::Token type) const;
 
   void AddEventListenerInternal(
-      base::Token type, std::unique_ptr<GenericEventHandlerReference> listener,
-      bool use_capture, Type listener_type);
+      std::unique_ptr<EventTargetListenerInfo> listener);
 
   EventListenerInfos event_listener_infos_;
 
+  base::DebuggerHooks* debugger_hooks_;
+
   // Tracks whether this current event listener should unpack the onerror
   // event object when calling its callback.  This is needed to implement
   // the special case of window.onerror handling.
diff --git a/src/cobalt/dom/event_target_listener_info.cc b/src/cobalt/dom/event_target_listener_info.cc
new file mode 100644
index 0000000..7309029
--- /dev/null
+++ b/src/cobalt/dom/event_target_listener_info.cc
@@ -0,0 +1,143 @@
+// Copyright 2018 The Cobalt Authors. 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/event_target_listener_info.h"
+
+#include "base/trace_event/trace_event.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/dom/global_stats.h"
+
+namespace cobalt {
+namespace dom {
+
+EventTargetListenerInfo::EventTargetListenerInfo(
+    script::Wrappable* wrappable, base::Token type, AttachMethod attach,
+    bool use_capture, const EventListenerScriptValue& script_value)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(task_(this)),
+      type_(type),
+      is_attribute_(attach == kSetAttribute),
+      use_capture_(use_capture),
+      unpack_error_event_(false) {
+  if (!script_value.IsNull()) {
+    GlobalStats::GetInstance()->AddEventListener();
+    event_listener_reference_.reset(
+        new EventListenerScriptValue::Reference(wrappable, script_value));
+  }
+}
+
+EventTargetListenerInfo::EventTargetListenerInfo(
+    script::Wrappable* wrappable, base::Token type, AttachMethod attach,
+    bool use_capture, bool unpack_error_event,
+    const OnErrorEventListenerScriptValue& script_value)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(task_(this)),
+      type_(type),
+      is_attribute_(attach == kSetAttribute),
+      use_capture_(use_capture),
+      unpack_error_event_(unpack_error_event) {
+  if (!script_value.IsNull()) {
+    GlobalStats::GetInstance()->AddEventListener();
+    on_error_event_listener_reference_.reset(
+        new OnErrorEventListenerScriptValue::Reference(wrappable,
+                                                       script_value));
+  }
+}
+
+EventTargetListenerInfo::EventTargetListenerInfo(
+    script::Wrappable* wrappable, const EventTargetListenerInfo& other)
+    : task_(other.task_),
+      type_(other.type_),
+      is_attribute_(other.is_attribute_),
+      use_capture_(other.use_capture_),
+      unpack_error_event_(other.unpack_error_event_) {
+  if (other.event_listener_reference_) {
+    DCHECK(!other.event_listener_reference_->referenced_value().IsNull());
+    GlobalStats::GetInstance()->AddEventListener();
+    event_listener_reference_.reset(new EventListenerScriptValue::Reference(
+        wrappable, other.event_listener_reference_->referenced_value()));
+  } else if (other.on_error_event_listener_reference_) {
+    GlobalStats::GetInstance()->AddEventListener();
+    on_error_event_listener_reference_.reset(
+        new OnErrorEventListenerScriptValue::Reference(
+            wrappable,
+            other.on_error_event_listener_reference_->referenced_value()));
+  }
+}
+
+EventTargetListenerInfo::~EventTargetListenerInfo() {
+  if (!IsNull()) {
+    GlobalStats::GetInstance()->RemoveEventListener();
+  }
+}
+
+void EventTargetListenerInfo::HandleEvent(const scoped_refptr<Event>& event) {
+  TRACE_EVENT1("cobalt::dom", "EventTargetListenerInfo::HandleEvent",
+               "Event Name", TRACE_STR_COPY(event->type().c_str()));
+  bool had_exception;
+  base::Optional<bool> result;
+
+  // Forward the HandleEvent() call to the appropriate internal object.
+  if (event_listener_reference_) {
+    // Non-onerror event handlers cannot have their parameters unpacked.
+    result = event_listener_reference_->value().HandleEvent(
+        event->current_target(), event, &had_exception);
+  } else if (on_error_event_listener_reference_) {
+    result = on_error_event_listener_reference_->value().HandleEvent(
+        event->current_target(), event, &had_exception, unpack_error_event_);
+  } else {
+    NOTREACHED();
+    had_exception = true;
+  }
+
+  if (had_exception) {
+    return;
+  }
+  // EventHandlers (EventListeners set as attributes) may return false rather
+  // than call event.preventDefault() in the handler function.
+  if (is_attribute() && result && !result.value()) {
+    event->PreventDefault();
+  }
+}
+
+bool EventTargetListenerInfo::EqualTo(const EventTargetListenerInfo& other) {
+  if (type() != other.type() || is_attribute() != other.is_attribute() ||
+      use_capture() != other.use_capture()) {
+    return false;
+  }
+
+  if (IsNull() && other.IsNull()) {
+    return true;
+  }
+
+  if (event_listener_reference_ && other.event_listener_reference_) {
+    return event_listener_reference_->referenced_value().EqualTo(
+        other.event_listener_reference_->referenced_value());
+  }
+  if (on_error_event_listener_reference_ &&
+      other.on_error_event_listener_reference_) {
+    return on_error_event_listener_reference_->referenced_value().EqualTo(
+        other.on_error_event_listener_reference_->referenced_value());
+  }
+  return false;
+}
+
+bool EventTargetListenerInfo::IsNull() const {
+  return (!event_listener_reference_ ||
+          event_listener_reference_->referenced_value().IsNull()) &&
+         (!on_error_event_listener_reference_ ||
+          on_error_event_listener_reference_->referenced_value().IsNull());
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/event_target_listener_info.h b/src/cobalt/dom/event_target_listener_info.h
new file mode 100644
index 0000000..0323236
--- /dev/null
+++ b/src/cobalt/dom/event_target_listener_info.h
@@ -0,0 +1,135 @@
+// Copyright 2018 The Cobalt Authors. 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_EVENT_TARGET_LISTENER_INFO_H_
+#define COBALT_DOM_EVENT_TARGET_LISTENER_INFO_H_
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/event_listener.h"
+#include "cobalt/dom/on_error_event_listener.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// Holds the event listener for an EventTarget, along with metadata describing
+// in what manner the listener was attached to the EventTarget.
+//
+// The listener itself is a script::ScriptValue<T> where T may be either of:
+// [EventListener, OnErrorEventListener].  In particular it primarily
+// allows code in event_target.cc to not need to concern itself with which
+// exact EventListener script value type it is dealing with.  The need for
+// this abstraction arises from the fact that the |window.onerror| event
+// handler requires special case handling:
+//   https://html.spec.whatwg.org/#onerroreventhandler
+//
+// NOTE that this is *not* an ideal solution to the problem of generalizing
+// over multiple ScriptValue types.  The problem is that the ScriptValue
+// base class is both templated and abstract, making it difficult to cast its
+// internal type to get a new ScriptValue.  This problem could be solved by
+// refactoring ScriptValue such that there is a non-templated abstract type
+// (say, RawScriptValue) with a function like "void* GetValue()", but usually
+// not referenced directly by client code.  Instead there would be a separate
+// templated concrete wrapper type (say, ScriptValue) that wraps RawScriptValue
+// and manages the casting and type checking.  This would allow us to convert
+// between OnErrorEventListener and EventListener, if OnErrorEventListener was
+// derived from EventListener.
+class EventTargetListenerInfo {
+ public:
+  typedef script::ScriptValue<EventListener> EventListenerScriptValue;
+  typedef script::ScriptValue<OnErrorEventListener>
+      OnErrorEventListenerScriptValue;
+
+  // Whether an event listener is attached as an attribute or with
+  // AddEventListener().
+  enum AttachMethod {
+    kSetAttribute,
+    kAddEventListener,
+  };
+
+  EventTargetListenerInfo(script::Wrappable* wrappable, base::Token type,
+                          AttachMethod attach, bool use_capture,
+                          const EventListenerScriptValue& script_value);
+  EventTargetListenerInfo(script::Wrappable* wrappable, base::Token type,
+                          AttachMethod attach, bool use_capture,
+                          bool unpack_error_event,
+                          const OnErrorEventListenerScriptValue& script_value);
+  EventTargetListenerInfo(script::Wrappable* wrappable,
+                          const EventTargetListenerInfo& other);
+
+  EventTargetListenerInfo(const EventTargetListenerInfo&) = delete;
+  EventTargetListenerInfo& operator=(const EventTargetListenerInfo&) = delete;
+
+  ~EventTargetListenerInfo();
+
+  const void* task() const { return task_; }
+  base::Token type() const { return type_; }
+  bool is_attribute() const { return is_attribute_; }
+  bool use_capture() const { return use_capture_; }
+
+  // Forwards on to the internal event listener's HandleEvent() call, passing
+  // in the value of |unpack_error_event| if the internal type is a
+  // OnErrorEventListenerScriptValue type.
+  void HandleEvent(const scoped_refptr<Event>& event);
+
+  bool EqualTo(const EventTargetListenerInfo& other);
+  bool IsNull() const;
+
+  // If the internal type is a EventListenerScriptValue, then its value will
+  // be returned, otherwise null is returned;
+  const EventListenerScriptValue* event_listener_value() {
+    return event_listener_reference_
+               ? &event_listener_reference_->referenced_value()
+               : nullptr;
+  }
+
+  // If the internal type is a OnErrorEventListenerScriptValue, then its value
+  // will be returned, otherwise null is returned;
+  const OnErrorEventListenerScriptValue* on_error_event_listener_value() {
+    return on_error_event_listener_reference_
+               ? &on_error_event_listener_reference_->referenced_value()
+               : nullptr;
+  }
+
+ private:
+  // A nonce to identify the "scheduled" asynchronous task that could call the
+  // listener when the event is fired. The constructors that create a new
+  // "attachment" of a listener initialize it to |this| as a unique nonce value.
+  // However, the copy(ish) constructor copies the task since the copy still
+  // represents the same attachment of the same listener. It is specifically NOT
+  // tied to the ScriptValue since the same JS listener may be attached multiple
+  // times to one or several |EventTarget|s, and each of those attachments is a
+  // unique task.
+  const void* const task_;
+
+  base::Token const type_;
+  bool const is_attribute_;
+  bool const use_capture_;
+  bool const unpack_error_event_;
+
+  // At most only one of the below two fields may be non-null...  They are
+  // serving as a poor man's std::variant.
+  std::unique_ptr<EventListenerScriptValue::Reference>
+      event_listener_reference_;
+  std::unique_ptr<OnErrorEventListenerScriptValue::Reference>
+      on_error_event_listener_reference_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_EVENT_TARGET_LISTENER_INFO_H_
diff --git a/src/cobalt/dom/event_target_test.cc b/src/cobalt/dom/event_target_test.cc
index 834ae3a..1e09756 100644
--- a/src/cobalt/dom/event_target_test.cc
+++ b/src/cobalt/dom/event_target_test.cc
@@ -12,35 +12,50 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/event_target.h"
 
+#include <memory>
+
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/testing/mock_event_listener.h"
 #include "cobalt/script/testing/fake_script_value.h"
 #include "cobalt/script/testing/mock_exception_state.h"
+#include "cobalt/test/mock_debugger_hooks.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace dom {
 namespace {
 
+using script::testing::FakeScriptValue;
+using script::testing::MockExceptionState;
+using ::testing::_;
 using ::testing::AllOf;
 using ::testing::DoAll;
 using ::testing::Eq;
 using ::testing::InSequence;
 using ::testing::Invoke;
 using ::testing::InvokeWithoutArgs;
+using testing::MockEventListener;
 using ::testing::Pointee;
 using ::testing::Property;
 using ::testing::SaveArg;
 using ::testing::StrictMock;
-using ::testing::_;
-using script::testing::FakeScriptValue;
-using script::testing::MockExceptionState;
-using testing::MockEventListener;
+
+constexpr auto kRecurring = base::DebuggerHooks::AsyncTaskFrequency::kRecurring;
+
+class EventTargetTest : public ::testing::Test {
+ protected:
+  EventTargetTest()
+      : environment_settings_(0, nullptr, nullptr, nullptr, nullptr, nullptr,
+                              nullptr, nullptr, &debugger_hooks_, nullptr,
+                              DOMSettings::Options()) {}
+
+  StrictMock<test::MockDebuggerHooks> debugger_hooks_;
+  DOMSettings environment_settings_;
+};
 
 base::Optional<bool> DispatchEventOnCurrentTarget(
     const scoped_refptr<script::Wrappable>, const scoped_refptr<Event>& event,
@@ -63,37 +78,48 @@
   return base::nullopt;
 }
 
-TEST(EventTargetTest, SingleEventListenerFired) {
+TEST_F(EventTargetTest, SingleEventListenerFired) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
 
-  event_listener->ExpectHandleEventCall(event, event_target);
+  const void* async_task;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
+
+  event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
-TEST(EventTargetTest, SingleEventListenerNotFired) {
+TEST_F(EventTargetTest, SingleEventListenerNotFired) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
 
-  event_listener->ExpectNoHandleEventCall();
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "notfired", kRecurring));
   event_target->AddEventListener(
       "notfired", FakeScriptValue<EventListener>(event_listener.get()), false);
+
+  event_listener->ExpectNoHandleEventCall();
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
 // Test if multiple event listeners of different event types can be added and
 // fired properly.
-TEST(EventTargetTest, MultipleEventListeners) {
+TEST_F(EventTargetTest, MultipleEventListeners) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listenerfired_1 =
       MockEventListener::Create();
@@ -103,9 +129,16 @@
       MockEventListener::Create();
 
   InSequence in_sequence;
-  event_listenerfired_1->ExpectHandleEventCall(event, event_target);
-  event_listenerfired_2->ExpectHandleEventCall(event, event_target);
-  event_listenernot_fired->ExpectNoHandleEventCall();
+
+  const void* async_task_1;
+  const void* async_task_2;
+  const void* async_task_not_fired;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "notfired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_not_fired));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
 
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listenerfired_1.get()),
@@ -116,37 +149,61 @@
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listenerfired_2.get()),
       true);
+
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listenerfired_1->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
+
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_2));
+  event_listenerfired_2->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_2));
+
+  event_listenernot_fired->ExpectNoHandleEventCall();
+
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
 // Test if event listener can be added and later removed.
-TEST(EventTargetTest, AddRemoveEventListener) {
+TEST_F(EventTargetTest, AddRemoveEventListener) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
 
-  event_listener->ExpectHandleEventCall(event, event_target);
+  const void* async_task_1;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
-  event_listener->ExpectNoHandleEventCall();
+  EXPECT_CALL(debugger_hooks_, AsyncTaskCanceled(async_task_1));
   event_target->RemoveEventListener(
       "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
+  event_listener->ExpectNoHandleEventCall();
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
-  event_listener->ExpectHandleEventCall(event, event_target);
+  const void* async_task_2;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_2));
+  event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_2));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
 // Test if attribute event listener works.
-TEST(EventTargetTest, AttributeListener) {
+TEST_F(EventTargetTest, AttributeListener) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> non_attribute_event_listener =
       MockEventListener::Create();
@@ -155,75 +212,113 @@
   std::unique_ptr<MockEventListener> attribute_event_listener2 =
       MockEventListener::Create();
 
+  const void* non_attribute_async_task;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&non_attribute_async_task));
   event_target->AddEventListener(
       "fired",
       FakeScriptValue<EventListener>(non_attribute_event_listener.get()),
       false);
-
-  non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
-  attribute_event_listener1->ExpectHandleEventCall(event, event_target);
+  const void* async_task_1;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
   event_target->SetAttributeEventListener(
       base::Token("fired"),
       FakeScriptValue<EventListener>(attribute_event_listener1.get()));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(non_attribute_async_task));
+  non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(non_attribute_async_task));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  attribute_event_listener1->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
-  non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
-  attribute_event_listener1->ExpectNoHandleEventCall();
-  attribute_event_listener2->ExpectHandleEventCall(event, event_target);
+  const void* async_task_2;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskCanceled(async_task_1));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
   event_target->SetAttributeEventListener(
       base::Token("fired"),
       FakeScriptValue<EventListener>(attribute_event_listener2.get()));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(non_attribute_async_task));
+  non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(non_attribute_async_task));
+  attribute_event_listener1->ExpectNoHandleEventCall();
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_2));
+  attribute_event_listener2->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_2));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
-  non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
-  attribute_event_listener1->ExpectNoHandleEventCall();
-  attribute_event_listener2->ExpectNoHandleEventCall();
+  EXPECT_CALL(debugger_hooks_, AsyncTaskCanceled(async_task_2));
   event_target->SetAttributeEventListener(base::Token("fired"),
                                           FakeScriptValue<EventListener>(NULL));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(non_attribute_async_task));
+  non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(non_attribute_async_task));
+  attribute_event_listener1->ExpectNoHandleEventCall();
+  attribute_event_listener2->ExpectNoHandleEventCall();
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
 // Test if one event listener can be used by multiple events.
-TEST(EventTargetTest, EventListenerReuse) {
+TEST_F(EventTargetTest, EventListenerReuse) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event_1 = new Event(base::Token("fired_1"));
   scoped_refptr<Event> event_2 = new Event(base::Token("fired_2"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
 
-  event_listener->ExpectHandleEventCall(event_1, event_target);
-  event_listener->ExpectHandleEventCall(event_2, event_target);
+  const void* async_task_1;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired_1", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
   event_target->AddEventListener(
       "fired_1", FakeScriptValue<EventListener>(event_listener.get()), false);
+  const void* async_task_2;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired_2", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
   event_target->AddEventListener(
       "fired_2", FakeScriptValue<EventListener>(event_listener.get()), false);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listener->ExpectHandleEventCall(event_1, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_2));
+  event_listener->ExpectHandleEventCall(event_2, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_2));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 
-  event_listener->ExpectHandleEventCall(event_1, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskCanceled(async_task_2));
   event_target->RemoveEventListener(
       "fired_2", FakeScriptValue<EventListener>(event_listener.get()), false);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listener->ExpectHandleEventCall(event_1, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 
-  event_listener->ExpectHandleEventCall(event_1, event_target);
   // The capture flag is not the same so the event will not be removed.
   event_target->RemoveEventListener(
       "fired_1", FakeScriptValue<EventListener>(event_listener.get()), true);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listener->ExpectHandleEventCall(event_1, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 
-  event_listener->ExpectNoHandleEventCall();
+  EXPECT_CALL(debugger_hooks_, AsyncTaskCanceled(async_task_1));
   event_target->RemoveEventListener(
       "fired_1", FakeScriptValue<EventListener>(event_listener.get()), false);
+  event_listener->ExpectNoHandleEventCall();
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 }
 
-TEST(EventTargetTest, StopPropagation) {
+TEST_F(EventTargetTest, StopPropagation) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listenerfired_1 =
       MockEventListener::Create();
@@ -231,68 +326,97 @@
       MockEventListener::Create();
 
   InSequence in_sequence;
-  event_listenerfired_1->ExpectHandleEventCall(
-      event, event_target, &MockEventListener::StopPropagation);
-  event_listenerfired_2->ExpectHandleEventCall(event, event_target);
 
+  const void* async_task_1;
+  const void* async_task_2;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listenerfired_1.get()),
       false);
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listenerfired_2.get()),
       true);
+
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listenerfired_1->ExpectHandleEventCall(
+      event, event_target, &MockEventListener::StopPropagation);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_2));
+  event_listenerfired_2->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_2));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
-TEST(EventTargetTest, StopImmediatePropagation) {
+TEST_F(EventTargetTest, StopImmediatePropagation) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listenerfired_1 =
       MockEventListener::Create();
   std::unique_ptr<MockEventListener> event_listenerfired_2 =
       MockEventListener::Create();
 
-  event_listenerfired_1->ExpectHandleEventCall(
-      event, event_target, &MockEventListener::StopImmediatePropagation);
-  event_listenerfired_2->ExpectNoHandleEventCall();
-
+  const void* async_task_1;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listenerfired_1.get()),
       false);
+  const void* async_task_2;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listenerfired_2.get()),
       true);
+
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listenerfired_1->ExpectHandleEventCall(
+      event, event_target, &MockEventListener::StopImmediatePropagation);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
+  event_listenerfired_2->ExpectNoHandleEventCall();
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
-TEST(EventTargetTest, PreventDefault) {
+TEST_F(EventTargetTest, PreventDefault) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<Event> event;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   std::unique_ptr<MockEventListener> event_listenerfired =
       MockEventListener::Create();
 
+  const void* async_task;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listenerfired.get()),
       false);
   event = new Event(base::Token("fired"), Event::kNotBubbles,
                     Event::kNotCancelable);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task));
   event_listenerfired->ExpectHandleEventCall(
       event, event_target, &MockEventListener::PreventDefault);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
   event =
       new Event(base::Token("fired"), Event::kNotBubbles, Event::kCancelable);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task));
   event_listenerfired->ExpectHandleEventCall(
       event, event_target, &MockEventListener::PreventDefault);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task));
   EXPECT_FALSE(event_target->DispatchEvent(event, &exception_state));
 }
 
-TEST(EventTargetTest, RaiseException) {
+TEST_F(EventTargetTest, RaiseException) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event;
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
@@ -316,68 +440,110 @@
             base::polymorphic_downcast<DOMException*>(exception.get())->code());
   exception = NULL;
 
+  const void* async_task;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task));
   event_target->AddEventListener(
       "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
   event = new Event(base::Token("fired"), Event::kNotBubbles,
                     Event::kNotCancelable);
   // Dispatch event again when it is being dispatched.
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task));
   EXPECT_CALL(*event_listener, HandleEvent(_, _, _))
       .WillOnce(Invoke(DispatchEventOnCurrentTarget));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
-TEST(EventTargetTest, AddSameListenerMultipleTimes) {
+TEST_F(EventTargetTest, AddSameListenerMultipleTimes) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
   FakeScriptValue<EventListener> script_object(event_listener.get());
 
   InSequence in_sequence;
-  event_listener->ExpectHandleEventCall(event, event_target);
 
   // The same listener should only get added once.
+  const void* async_task;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task));
   event_target->AddEventListener("fired", script_object, false);
   event_target->AddEventListener("fired", script_object, false);
   event_target->AddEventListener("fired", script_object, false);
+
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task));
+  event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
-TEST(EventTargetTest, AddSameAttributeListenerMultipleTimes) {
+TEST_F(EventTargetTest, AddSameAttributeListenerMultipleTimes) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
   FakeScriptValue<EventListener> script_object(event_listener.get());
 
   InSequence in_sequence;
-  event_listener->ExpectHandleEventCall(event, event_target);
 
   // The same listener should only get added once.
+  const void* async_task_1;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
   event_target->SetAttributeEventListener(base::Token("fired"), script_object);
+
+  const void* async_task_2;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskCanceled(async_task_1));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
   event_target->SetAttributeEventListener(base::Token("fired"), script_object);
+
+  const void* async_task_3;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskCanceled(async_task_2));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_3));
   event_target->SetAttributeEventListener(base::Token("fired"), script_object);
+
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_3));
+  event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_3));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
-TEST(EventTargetTest, SameEventListenerAsAttribute) {
+TEST_F(EventTargetTest, SameEventListenerAsAttribute) {
   StrictMock<MockExceptionState> exception_state;
-  scoped_refptr<EventTarget> event_target = new EventTarget;
+  scoped_refptr<EventTarget> event_target =
+      new EventTarget(&environment_settings_);
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   std::unique_ptr<MockEventListener> event_listener =
       MockEventListener::Create();
   FakeScriptValue<EventListener> script_object(event_listener.get());
 
   InSequence in_sequence;
-  event_listener->ExpectHandleEventCall(event, event_target);
-  event_listener->ExpectHandleEventCall(event, event_target);
 
   // The same script object can be registered as both an attribute and
   // non-attribute listener. Both should be fired.
+  const void* async_task_1;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_1));
   event_target->AddEventListener("fired", script_object, false);
+
+  const void* async_task_2;
+  EXPECT_CALL(debugger_hooks_, AsyncTaskScheduled(_, "fired", kRecurring))
+      .WillOnce(SaveArg<0>(&async_task_2));
   event_target->SetAttributeEventListener(base::Token("fired"), script_object);
+
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_1));
+  event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_1));
+  EXPECT_CALL(debugger_hooks_, AsyncTaskStarted(async_task_2));
+  event_listener->ExpectHandleEventCall(event, event_target);
+  EXPECT_CALL(debugger_hooks_, AsyncTaskFinished(async_task_2));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
diff --git a/src/cobalt/dom/generic_event_handler_reference.cc b/src/cobalt/dom/generic_event_handler_reference.cc
deleted file mode 100644
index 1066cf8..0000000
--- a/src/cobalt/dom/generic_event_handler_reference.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2018 The Cobalt Authors. 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/generic_event_handler_reference.h"
-
-#include "base/trace_event/trace_event.h"
-#include "cobalt/dom/event.h"
-#include "cobalt/dom/event_target.h"
-
-namespace cobalt {
-namespace dom {
-
-GenericEventHandlerReference::GenericEventHandlerReference(
-    script::Wrappable* wrappable,
-    const EventListenerScriptValue& script_value) {
-  if (!script_value.IsNull()) {
-    event_listener_reference_.reset(
-        new EventListenerScriptValue::Reference(wrappable, script_value));
-  }
-}
-
-GenericEventHandlerReference::GenericEventHandlerReference(
-    script::Wrappable* wrappable,
-    const OnErrorEventListenerScriptValue& script_value) {
-  if (!script_value.IsNull()) {
-    on_error_event_listener_reference_.reset(
-        new OnErrorEventListenerScriptValue::Reference(wrappable,
-                                                       script_value));
-  }
-}
-
-GenericEventHandlerReference::GenericEventHandlerReference(
-    script::Wrappable* wrappable, const GenericEventHandlerReference& other) {
-  if (other.event_listener_reference_) {
-    event_listener_reference_.reset(new EventListenerScriptValue::Reference(
-        wrappable, other.event_listener_reference_->referenced_value()));
-  } else if (other.on_error_event_listener_reference_) {
-    on_error_event_listener_reference_.reset(
-        new OnErrorEventListenerScriptValue::Reference(
-            wrappable,
-            other.on_error_event_listener_reference_->referenced_value()));
-  }
-}
-
-void GenericEventHandlerReference::HandleEvent(
-    const scoped_refptr<Event>& event, bool is_attribute,
-    bool unpack_error_event) {
-  TRACE_EVENT1("cobalt::dom", "GenericEventHandlerReference::HandleEvent",
-               "Event Name", TRACE_STR_COPY(event->type().c_str()));
-  bool had_exception;
-  base::Optional<bool> result;
-
-  // Forward the HandleEvent() call to the appropriate internal object.
-  if (event_listener_reference_) {
-    // Non-onerror event handlers cannot have their parameters unpacked.
-    result = event_listener_reference_->value().HandleEvent(
-        event->current_target(), event, &had_exception);
-  } else if (on_error_event_listener_reference_) {
-    result = on_error_event_listener_reference_->value().HandleEvent(
-        event->current_target(), event, &had_exception, unpack_error_event);
-  } else {
-    NOTREACHED();
-    had_exception = true;
-  }
-
-  if (had_exception) {
-    return;
-  }
-  // EventHandlers (EventListeners set as attributes) may return false rather
-  // than call event.preventDefault() in the handler function.
-  if (is_attribute && result && !result.value()) {
-    event->PreventDefault();
-  }
-}
-
-bool GenericEventHandlerReference::EqualTo(
-    const EventListenerScriptValue& other) {
-  return (IsNull() && other.IsNull()) ||
-         (event_listener_reference_ &&
-          event_listener_reference_->referenced_value().EqualTo(other));
-}
-
-bool GenericEventHandlerReference::EqualTo(
-    const GenericEventHandlerReference& other) {
-  if (IsNull() && other.IsNull()) {
-    return true;
-  }
-
-  if (event_listener_reference_ && other.event_listener_reference_) {
-    return event_listener_reference_->referenced_value().EqualTo(
-        other.event_listener_reference_->referenced_value());
-  }
-  if (on_error_event_listener_reference_ &&
-      other.on_error_event_listener_reference_) {
-    return on_error_event_listener_reference_->referenced_value().EqualTo(
-        other.on_error_event_listener_reference_->referenced_value());
-  }
-  return false;
-}
-
-bool GenericEventHandlerReference::IsNull() const {
-  return (!event_listener_reference_ ||
-          event_listener_reference_->referenced_value().IsNull()) &&
-         (!on_error_event_listener_reference_ ||
-          on_error_event_listener_reference_->referenced_value().IsNull());
-}
-
-}  // namespace dom
-}  // namespace cobalt
diff --git a/src/cobalt/dom/generic_event_handler_reference.h b/src/cobalt/dom/generic_event_handler_reference.h
deleted file mode 100644
index 8a02c15..0000000
--- a/src/cobalt/dom/generic_event_handler_reference.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2018 The Cobalt Authors. 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_GENERIC_EVENT_HANDLER_REFERENCE_H_
-#define COBALT_DOM_GENERIC_EVENT_HANDLER_REFERENCE_H_
-
-#include <memory>
-
-#include "base/memory/ref_counted.h"
-#include "cobalt/dom/event_listener.h"
-#include "cobalt/dom/on_error_event_listener.h"
-#include "cobalt/script/script_value.h"
-#include "cobalt/script/wrappable.h"
-
-namespace cobalt {
-namespace dom {
-
-// Essentially acts as an abstract interface of the union for types
-// [script::ScriptValue<EventListener>,
-//  script::ScriptValue<OnErrorEventListener>].  In particular it primarily
-// allows code in event_target.cc to not need to concern itself with which
-// exact EventListener script value type it is dealing with.  The need for
-// this abstraction arises from the fact that the |window.onerror| event
-// handler requires special case handling:
-//   https://html.spec.whatwg.org/#onerroreventhandler )
-//
-// NOTE that this is *not* an ideal solution to the problem of generalizing
-// over multiple ScriptValue types.  The problem is that the ScriptValue
-// base class is both templated and abstract, making it difficult to cast its
-// internal type to get a new ScriptValue.  This problem could be solved by
-// refactoring ScriptValue such that there is a non-templated abstract type
-// (say, RawScriptValue) with a function like "void* GetValue()", but usually
-// not referenced directly by client code.  Instead there would be a separate
-// templated concrete wrapper type (say, ScriptValue) that wraps RawScriptValue
-// and manages the casting and type checking.  This would allow us to convert
-// between OnErrorEventListener and EventListener, if OnErrorEventListener was
-// derived from EventListener.
-class GenericEventHandlerReference {
- public:
-  typedef script::ScriptValue<EventListener> EventListenerScriptValue;
-  typedef script::ScriptValue<OnErrorEventListener>
-      OnErrorEventListenerScriptValue;
-
-  GenericEventHandlerReference(script::Wrappable* wrappable,
-                               const EventListenerScriptValue& script_value);
-  GenericEventHandlerReference(
-      script::Wrappable* wrappable,
-      const OnErrorEventListenerScriptValue& script_value);
-  GenericEventHandlerReference(script::Wrappable* wrappable,
-                               const GenericEventHandlerReference& other);
-
-  // Forwards on to the internal event handler's HandleEvent() call, passing
-  // in the value of |unpack_error_event| if the internal type is a
-  // OnErrorEventListenerScriptValue type.
-  void HandleEvent(const scoped_refptr<Event>& event, bool is_attribute,
-                   bool unpack_error_event);
-
-  bool EqualTo(const EventListenerScriptValue& other);
-  bool EqualTo(const GenericEventHandlerReference& other);
-  bool IsNull() const;
-
-  // If the internal type is a EventListenerScriptValue, then its value will
-  // be returned, otherwise null is returned;
-  const EventListenerScriptValue* event_listener_value() {
-    return event_listener_reference_
-               ? &event_listener_reference_->referenced_value()
-               : nullptr;
-  }
-
-  // If the internal type is a OnErrorEventListenerScriptValue, then its value
-  // will be returned, otherwise null is returned;
-  const OnErrorEventListenerScriptValue* on_error_event_listener_value() {
-    return on_error_event_listener_reference_
-               ? &on_error_event_listener_reference_->referenced_value()
-               : nullptr;
-  }
-
- private:
-  // At most only one of the below two fields may be non-null...  They are
-  // serving as a poor man's std::variant.
-  std::unique_ptr<EventListenerScriptValue::Reference>
-      event_listener_reference_;
-  std::unique_ptr<OnErrorEventListenerScriptValue::Reference>
-      on_error_event_listener_reference_;
-
-  DISALLOW_COPY_AND_ASSIGN(GenericEventHandlerReference);
-};
-
-}  // namespace dom
-}  // namespace cobalt
-
-#endif  // COBALT_DOM_GENERIC_EVENT_HANDLER_REFERENCE_H_
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index c52faae..1d3a522 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -169,9 +169,9 @@
   // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
   // https://dev.w3.org/html5/spec-preview/common-dom-interfaces.html#limited-to-only-known-values
   // NOTE: Value "auto" is not supported.
-  if (directionality_ == kLeftToRightDirectionality) {
+  if (dir_ == kDirLeftToRight) {
     return "ltr";
-  } else if (directionality_ == kRightToLeftDirectionality) {
+  } else if (dir_ == kDirRightToLeft) {
     return "rtl";
   } else {
     return "";
@@ -179,10 +179,7 @@
 }
 
 void HTMLElement::set_dir(const std::string& value) {
-  // The dir attribute is limited to only known values. On setting, the dir
-  // attribute must be set to the specified new value.
-  // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
-  // https://dev.w3.org/html5/spec-preview/common-dom-interfaces.html#limited-to-only-known-values
+  // Funnel through OnSetAttribute.
   SetAttribute("dir", value);
 }
 
@@ -360,7 +357,7 @@
   int32 element_scroll_width = 0;
   if (layout_boxes_) {
     element_scroll_width = static_cast<int32>(
-        layout_boxes_->GetScrollArea(directionality_).width());
+        layout_boxes_->GetScrollArea(directionality()).width());
   }
 
   // 3. Let viewport width be the width of the viewport excluding the width of
@@ -396,7 +393,7 @@
   int32 element_scroll_height = 0;
   if (layout_boxes_) {
     element_scroll_height = static_cast<int32>(
-        layout_boxes_->GetScrollArea(directionality_).height());
+        layout_boxes_->GetScrollArea(directionality()).height());
   }
 
   // 3. Let viewport height be the height of the viewport excluding the height
@@ -776,7 +773,7 @@
 
   // Copy cached attributes.
   new_html_element->tabindex_ = tabindex_;
-  new_html_element->directionality_ = directionality_;
+  new_html_element->dir_ = dir_;
 
   return new_html_element;
 }
@@ -1144,6 +1141,7 @@
       pseudo_element->reset_layout_boxes();
     }
   }
+  directionality_ = base::nullopt;
 }
 
 void HTMLElement::OnUiNavBlur() {
@@ -1170,7 +1168,7 @@
     : Element(document, local_name),
       dom_stat_tracker_(document->html_element_context()->dom_stat_tracker()),
       locked_for_focus_(false),
-      directionality_(kNoExplicitDirectionality),
+      dir_(kDirNotDefined),
       style_(new cssom::CSSDeclaredStyleDeclaration(
           document->html_element_context()->css_parser())),
       computed_style_valid_(false),
@@ -1242,7 +1240,7 @@
                                  const std::string& value) {
   // Be sure to update HTMLElement::Duplicate() to copy over values as needed.
   if (name == "dir") {
-    SetDirectionality(value);
+    SetDir(value);
   } else if (name == "tabindex") {
     SetTabIndex(value);
   }
@@ -1254,7 +1252,7 @@
 
 void HTMLElement::OnRemoveAttribute(const std::string& name) {
   if (name == "dir") {
-    SetDirectionality("");
+    SetDir("");
   } else if (name == "tabindex") {
     SetTabIndex("");
   }
@@ -1391,18 +1389,26 @@
   ClearRuleMatchingState();
 }
 
-void HTMLElement::SetDirectionality(const std::string& value) {
+void HTMLElement::SetDir(const std::string& value) {
+  // https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#the-dir-attribute
   // NOTE: Value "auto" is not supported.
-  Directionality previous_directionality = directionality_;
+  auto previous_dir = dir_;
   if (value == "ltr") {
-    directionality_ = kLeftToRightDirectionality;
+    dir_ = kDirLeftToRight;
   } else if (value == "rtl") {
-    directionality_ = kRightToLeftDirectionality;
+    dir_ = kDirRightToLeft;
   } else {
-    directionality_ = kNoExplicitDirectionality;
+    dir_ = kDirNotDefined;
+
+    // Reset the attribute so that element.getAttribute('dir') returns the
+    // same thing as element.dir.
+    if (value.size() > 0) {
+      LOG(WARNING) << "Unsupported value '" << value << "' for attribute 'dir'";
+      SetAttribute("dir", "");
+    }
   }
 
-  if (directionality_ != previous_directionality) {
+  if (dir_ != previous_dir) {
     InvalidateLayoutBoxesOfNodeAndAncestors();
     InvalidateLayoutBoxesOfDescendants();
   }
@@ -1417,6 +1423,92 @@
   }
 }
 
+// Algorithm:
+//   https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#the-directionality
+Directionality HTMLElement::directionality() {
+  // Use the cached value if available.
+  if (directionality_) {
+    return *directionality_;
+  }
+
+  // The directionality of an element (any element, not just an HTML element)
+  // is either 'ltr' or 'rtl', and is determined as per the first appropriate
+  // set of steps from the following list:
+
+  // If the element's dir attribute is in the ltr state
+  // If the element is a document element and the dir attribute is not in a
+  //   defined state (i.e. it is not present or has an invalid value)
+  // If the element is an input element whose type attribute is in the
+  //   Telephone state, and the dir attribute is not in a defined state (i.e.
+  //   it is not present or has an invalid value)
+  // --> The directionality of the element is 'ltr'.
+  if (dir_ == kDirLeftToRight) {
+    directionality_ = kLeftToRightDirectionality;
+    return *directionality_;
+  }
+  // [Case of undefined 'dir' is handled later in this function.]
+
+  // If the element's dir attribute is in the rtl state
+  // --> The directionality of the element is 'rtl'.
+  if (dir_ == kDirRightToLeft) {
+    directionality_ = kRightToLeftDirectionality;
+    return *directionality_;
+  }
+
+  // If the element is an input element whose type attribute is in the Text,
+  //   Search, Telephone, URL, or E-mail state, and the dir attribute is in the
+  //   auto state
+  // If the element is a textarea element and the dir attribute is in the auto
+  //   state
+  // --> Cobalt does not support these element types.
+
+  // If the element's dir attribute is in the auto state
+  // If the element is a bdi element and the dir attribute is not in a defined
+  //   state (i.e. it is not present or has an invalid value)
+  // --> Find the first character in tree order that matches the following
+  //       criteria:
+  //       The character is from a Text node that is a descendant of the
+  //         element whose directionality is being determined.
+  //       The character is of bidirectional character type L, AL, or R. [BIDI]
+  //       The character is not in a Text node that has an ancestor element
+  //         that is a descendant of the element whose directionality is being
+  //         determined and that is either:
+  //           A bdi element.
+  //           A script element.
+  //           A style element.
+  //           A textarea element.
+  //           An element with a dir attribute in a defined state.
+  //     If such a character is found and it is of bidirectional character type
+  //       AL or R, the directionality of the element is 'rtl'.
+  //     If such a character is found and it is of bidirectional character type
+  //       L, the directionality of the element is 'ltr'.
+  //     Otherwise, if the element is a document element, the directionality of
+  //       the element is 'ltr'.
+  //     Otherwise, the directionality of the element is the same as the
+  //       element's parent element's directionality.
+
+  // If the element has a parent element and the dir attribute is not in a
+  //   defined state (i.e. it is not present or has an invalid value)
+  // --> The directionality of the element is the same as the element's parent
+  //       element's directionality.
+  for (Node* ancestor_node = parent_node(); ancestor_node;
+       ancestor_node = ancestor_node->parent_node()) {
+    Element* ancestor_element = ancestor_node->AsElement();
+    if (!ancestor_element) {
+      continue;
+    }
+    HTMLElement* ancestor_html_element = ancestor_element->AsHTMLElement();
+    if (!ancestor_html_element) {
+      continue;
+    }
+    directionality_ = ancestor_html_element->directionality();
+    return *directionality_;
+  }
+
+  directionality_ = kLeftToRightDirectionality;
+  return *directionality_;
+}
+
 namespace {
 
 scoped_refptr<cssom::CSSComputedStyleData> PromoteMatchingRulesToComputedStyle(
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 5cf5a48..5166bf8 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -134,6 +134,14 @@
     kAncestorsAreNotDisplayed,
   };
 
+  // https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#the-dir-attribute
+  // NOTE: 'auto' is not supported.
+  enum DirState {
+    kDirLeftToRight,
+    kDirRightToLeft,
+    kDirNotDefined,
+  };
+
   // Web API: HTMLElement
   //
   std::string dir() const;
@@ -220,9 +228,14 @@
   virtual scoped_refptr<HTMLVideoElement> AsHTMLVideoElement();
 
   // Returns the directionality of the element, which is based upon the
-  // underlying "dir" attribute, and is updated when the attribute changes.
-  // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionalityy.
-  Directionality directionality() const { return directionality_; }
+  // element's "dir" attribute if it was set, or that of the parent's if not
+  // set.
+  //   https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#the-directionality
+  Directionality directionality();
+
+  // Retrieve the dir attribute state. This is similar to dir() but returns the
+  // enumerated state rather than string.
+  DirState dir_state() const { return dir_; }
 
   // Rule matching related methods.
   //
@@ -368,14 +381,9 @@
   void RunFocusingSteps();
   void RunUnFocusingSteps();
 
-  // This both updates the directionality based upon the string value and
+  // This both updates the 'dir' attribute based upon the string value and
   // invalidates layout box caching if the value has changed.
-  // NOTE1: Value "auto" is not supported.
-  // NOTE2: Cobalt does not support either the CSS 'direction" or "unicode-bidi'
-  // properties, and instead relies entirely upon the 'dir' attribute for
-  // determining directionality of elements. As a result of this, setting the
-  // directionality does not invalidate the computed style.
-  void SetDirectionality(const std::string& value);
+  void SetDir(const std::string& value);
 
   // Update the cached value of tabindex.
   void SetTabIndex(const std::string& value);
@@ -416,15 +424,17 @@
 
   bool locked_for_focus_;
 
-  // The directionality of the html element is determined by the 'dir'
-  // attribute.
-  // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
-  // NOTE1: Value "auto" is not supported.
-  // NOTE2: Cobalt does not support either the CSS 'direction" or "unicode-bidi'
+  // This represents the enumerated value of the 'dir' attribute.
+  //   https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#the-dir-attribute
+  DirState dir_;
+
+  // This represents the computed directionality for this element.
+  //   https://html.spec.whatwg.org/commit-snapshots/ebcac971c2add28a911283899da84ec509876c44/#the-directionality
+  // NOTE: Cobalt does not support either the CSS 'direction' or 'unicode-bidi'
   // properties, and instead relies entirely upon the 'dir' attribute for
   // determining directionality. Inheritance of directionality occurs via the
   // base direction of the parent element's paragraph.
-  Directionality directionality_;
+  base::Optional<Directionality> directionality_;
 
   // Cache the tabindex value.
   base::Optional<int32> tabindex_;
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc
index f288b4e..7e55422 100644
--- a/src/cobalt/dom/html_element_context.cc
+++ b/src/cobalt/dom/html_element_context.cc
@@ -16,11 +16,18 @@
 
 #include "cobalt/dom/html_element_factory.h"
 
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+#include "cobalt/dom/testing/stub_environment_settings.h"
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+
 namespace cobalt {
 namespace dom {
 
+#if !defined(COBALT_BUILD_TYPE_GOLD)
 HTMLElementContext::HTMLElementContext()
-    : fetcher_factory_(NULL),
+    : stub_environment_settings_(new testing::StubEnvironmentSettings),
+      environment_settings_(stub_environment_settings_.get()),
+      fetcher_factory_(NULL),
       loader_factory_(NULL),
       css_parser_(NULL),
       dom_parser_(NULL),
@@ -42,8 +49,10 @@
       html_element_factory_(new HTMLElementFactory()) {
   sync_load_thread_.Start();
 }
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
 
 HTMLElementContext::HTMLElementContext(
+    script::EnvironmentSettings* environment_settings,
     loader::FetcherFactory* fetcher_factory,
     loader::LoaderFactory* loader_factory, cssom::CSSParser* css_parser,
     Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
@@ -62,7 +71,8 @@
     base::ApplicationState initial_application_state,
     base::WaitableEvent* synchronous_loader_interrupt,
     float video_playback_rate_multiplier)
-    : fetcher_factory_(fetcher_factory),
+    : environment_settings_(environment_settings),
+      fetcher_factory_(fetcher_factory),
       loader_factory_(loader_factory),
       css_parser_(css_parser),
       dom_parser_(dom_parser),
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h
index e67b6d1..71d761c 100644
--- a/src/cobalt/dom/html_element_context.h
+++ b/src/cobalt/dom/html_element_context.h
@@ -33,6 +33,7 @@
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/media/web_media_player_factory.h"
 #include "cobalt/page_visibility/page_visibility_state.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/script_runner.h"
 #include "cobalt/script/script_value_factory.h"
 
@@ -49,8 +50,13 @@
  public:
   typedef UrlRegistry<MediaSource> MediaSourceRegistry;
 
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+  // No-args constructor for tests.
   HTMLElementContext();
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+
   HTMLElementContext(
+      script::EnvironmentSettings* environment_settings,
       loader::FetcherFactory* fetcher_factory,
       loader::LoaderFactory* loader_factory, cssom::CSSParser* css_parser,
       Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
@@ -71,6 +77,10 @@
       float video_playback_rate_multiplier = 1.0);
   ~HTMLElementContext();
 
+  script::EnvironmentSettings* environment_settings() const {
+    return environment_settings_;
+  }
+
   loader::FetcherFactory* fetcher_factory() { return fetcher_factory_; }
 
   loader::LoaderFactory* loader_factory() { return loader_factory_; }
@@ -146,6 +156,12 @@
   }
 
  private:
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+  // StubEnvironmentSettings for no-args test constructor.
+  std::unique_ptr<script::EnvironmentSettings> stub_environment_settings_;
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+
+  script::EnvironmentSettings* environment_settings_;
   loader::FetcherFactory* const fetcher_factory_;
   loader::LoaderFactory* const loader_factory_;
   cssom::CSSParser* const css_parser_;
diff --git a/src/cobalt/dom/html_element_factory.cc b/src/cobalt/dom/html_element_factory.cc
index 5af905d..6612cdd 100644
--- a/src/cobalt/dom/html_element_factory.cc
+++ b/src/cobalt/dom/html_element_factory.cc
@@ -15,6 +15,7 @@
 #include "cobalt/dom/html_element_factory.h"
 
 #include "base/bind.h"
+#include "base/third_party/icu/icu_utf.h"
 #include "cobalt/dom/html_anchor_element.h"
 #include "cobalt/dom/html_audio_element.h"
 #include "cobalt/dom/html_body_element.h"
@@ -54,6 +55,91 @@
   return new T(document, base::Token(local_name));
 }
 
+bool IsValidAsciiChar(char32_t c) {
+  const bool isLowerAscii = c >= 'a' && c <= 'z';
+  const bool isLowerDigit = c >= '0' && c <= '9';
+  return isLowerAscii || isLowerDigit || c == '.' || c == '_';
+}
+
+// Grandfathered HTML elements that meet CustomElement naming spec:
+// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+bool IsBlacklistedTag(const char* tag, int length) {
+  switch (length) {
+    case 9:
+      if (SbStringCompareAll(tag, "font-face") == 0) {
+        return true;
+      }
+      break;
+    case 13:
+      if (SbStringCompareAll(tag, "font-face-src") == 0 ||
+          SbStringCompareAll(tag, "missing-glyph") == 0 ||
+          SbStringCompareAll(tag, "color-profile") == 0 ||
+          SbStringCompareAll(tag, "font-face-uri") == 0) {
+        return true;
+      }
+      break;
+    case 14:
+      if (SbStringCompareAll(tag, "font-face-name") == 0 ||
+          SbStringCompareAll(tag, "annotation-xml") == 0) {
+        return true;
+      }
+      break;
+    case 16:
+      if (SbStringCompareAll(tag, "font-face-format") == 0) {
+        return true;
+      }
+      break;
+    default:
+      return false;
+  }
+  return false;
+}
+
+// Follows naming spec at
+// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+bool IsValidCustomElementName(base::Token tag_name) {
+  // Consider adding support for customElements.define in order to
+  // formally register CustomElements rather than filter out errors
+  // for all potential custom names.
+
+  const char* tag = tag_name.c_str();
+  char c = tag[0];
+  if (c < 'a' || c > 'z') {
+    return false;
+  }
+  bool contains_hyphen = false;
+  int length = 1;
+
+  while ((c = tag[length]) != '\0') {
+    // Early return to avoid utf32 conversion cost for most cases.
+    if (IsValidAsciiChar(c)) {
+      length++;
+      continue;
+    }
+    if (c == '-') {
+      contains_hyphen = true;
+      length++;
+      continue;
+    }
+    base_icu::UChar32 c32;
+
+    CBU8_NEXT(tag, length, -1, c32);
+    bool is_valid_char =
+        c32 == 0xb7 || (c32 >= 0xc0 && c32 <= 0xd6) ||
+        (c32 >= 0xd8 && c32 <= 0xf6) || (c32 >= 0xf8 && c32 <= 0x037d) ||
+        (c32 >= 0x037f && c32 <= 0x1fff) || (c32 >= 0x200c && c32 <= 0x200d) ||
+        (c32 >= 0x203f && c32 <= 0x2040) || (c32 >= 0x2070 && c32 <= 0x218f) ||
+        (c32 >= 0x2c00 && c32 <= 0x2fef) || (c32 >= 0x3001 && c32 <= 0xd7ff) ||
+        (c32 >= 0xf900 && c32 <= 0xfdcf) || (c32 >= 0xfdf0 && c32 <= 0xfffd) ||
+        (c32 >= 0x00010000 && c32 <= 0x000effff);
+    if (!is_valid_char) {
+      return false;
+    }
+  }
+
+  return contains_hyphen && !IsBlacklistedTag(tag, length);
+}
+
 }  // namespace
 
 HTMLElementFactory::HTMLElementFactory() {
@@ -89,7 +175,8 @@
   if (iter != tag_name_to_create_html_element_t_callback_map_.end()) {
     return iter->second.Run(document);
   } else {
-    LOG(WARNING) << "Unknown HTML element: <" << tag_name << ">.";
+    LOG_IF(WARNING, !IsValidCustomElementName(tag_name))
+        << "Unknown HTML element: <" << tag_name << ">.";
     return new HTMLUnknownElement(document, tag_name);
   }
 }
diff --git a/src/cobalt/dom/html_element_factory_test.cc b/src/cobalt/dom/html_element_factory_test.cc
index f252c75..8b54486 100644
--- a/src/cobalt/dom/html_element_factory_test.cc
+++ b/src/cobalt/dom/html_element_factory_test.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/html_element_factory.h"
 
+#include <memory>
+
 #include "base/message_loop/message_loop.h"
 #include "base/threading/platform_thread.h"
 #include "cobalt/dom/document.h"
@@ -41,6 +41,7 @@
 #include "cobalt/dom/html_unknown_element.h"
 #include "cobalt/dom/html_video_element.h"
 #include "cobalt/dom/testing/stub_css_parser.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_script_runner.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
@@ -61,8 +62,9 @@
         dom_parser_(new dom_parser::Parser()),
         dom_stat_tracker_(new DomStatTracker("HTMLElementFactoryTest")),
         html_element_context_(
-            &fetcher_factory_, &loader_factory_, &stub_css_parser_,
-            dom_parser_.get(), NULL /* can_play_type_handler */,
+            &environment_settings_, &fetcher_factory_, &loader_factory_,
+            &stub_css_parser_, dom_parser_.get(),
+            NULL /* can_play_type_handler */,
             NULL /* web_media_player_factory */, &stub_script_runner_,
             NULL /* script_value_factory */, NULL /* media_source_registry */,
             NULL /* resource_provider */, NULL /* animated_image_tracker */,
@@ -75,6 +77,7 @@
         document_(new Document(&html_element_context_)) {}
   ~HTMLElementFactoryTest() override {}
 
+  testing::StubEnvironmentSettings environment_settings_;
   loader::FetcherFactory fetcher_factory_;
   loader::LoaderFactory loader_factory_;
   std::unique_ptr<Parser> dom_parser_;
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index 8fa740f..25ad26b 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/html_element.h"
 
+#include <memory>
+
 #include "base/basictypes.h"
 #include "base/message_loop/message_loop.h"
 #include "cobalt/base/polymorphic_downcast.h"
@@ -32,6 +32,7 @@
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/dom/layout_boxes.h"
 #include "cobalt/dom/named_node_map.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_window.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/media_session/media_session.h"
@@ -40,8 +41,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using cobalt::cssom::ViewportSize;
-using testing::Return;
 using testing::_;
+using testing::Return;
 
 namespace cobalt {
 namespace dom {
@@ -69,10 +70,9 @@
 const char kFooBarDeclarationString[] = "foo: bar;";
 const char kDisplayInlineDeclarationString[] = "display: inline;";
 const char* kHtmlElementTagNames[] = {
-  // "audio", "script", and "video" are excluded since they need more setup.
-  "a", "body", "br", "div", "head", "h1", "html", "img", "link",
-  "meta", "p", "span", "style", "title"
-};
+    // "audio", "script", and "video" are excluded since they need more setup.
+    "a",   "body", "br",   "div", "head", "h1",    "html",
+    "img", "link", "meta", "p",   "span", "style", "title"};
 
 class MockLayoutBoxes : public LayoutBoxes {
  public:
@@ -132,10 +132,10 @@
  protected:
   HTMLElementTest()
       : dom_stat_tracker_(new DomStatTracker("HTMLElementTest")),
-        html_element_context_(NULL, NULL, &css_parser_, NULL, NULL, NULL, NULL,
+        html_element_context_(&environment_settings_, NULL, NULL, &css_parser_,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              dom_stat_tracker_.get(), "",
-                              base::kApplicationStateStarted, NULL),
+                              NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
+                              "", base::kApplicationStateStarted, NULL),
         document_(new Document(&html_element_context_)) {}
   ~HTMLElementTest() override {}
 
@@ -147,6 +147,7 @@
   void SetElementStyle(const scoped_refptr<cssom::CSSDeclaredStyleData>& data,
                        HTMLElement* html_element);
 
+  testing::StubEnvironmentSettings environment_settings_;
   cssom::testing::MockCSSParser css_parser_;
   std::unique_ptr<DomStatTracker> dom_stat_tracker_;
   HTMLElementContext html_element_context_;
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index d803d68..44340f2 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -25,6 +25,7 @@
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/base/instance_counter.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/cssom/map_to_mesh_function.h"
 #include "cobalt/dom/csp_delegate.h"
@@ -69,6 +70,8 @@
 
 #endif  // LOG_MEDIA_ELEMENT_ACTIVITIES
 
+DECLARE_INSTANCE_COUNTER(HTMLMediaElement);
+
 loader::RequestMode GetRequestMode(
     const base::Optional<std::string>& cross_origin_attribute) {
   // https://html.spec.whatwg.org/#cors-settings-attribute
@@ -91,7 +94,7 @@
   if (resource_url.SchemeIs("data")) {
     return true;
   }
-  // Check if resource_url is an hls url. Hls url must contain "hls_variant"
+  // Check if resource_url is an hls url. Hls url must contain "hls_variant".
   return resource_url.spec().find("hls_variant") != std::string::npos;
 }
 #endif  // SB_HAS(PLAYER_WITH_URL)
@@ -151,12 +154,14 @@
       request_mode_(loader::kNoCORSMode) {
   TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::HTMLMediaElement()");
   MLOG();
+  ON_INSTANCE_CREATED(HTMLMediaElement);
 }
 
 HTMLMediaElement::~HTMLMediaElement() {
   TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::~HTMLMediaElement()");
   MLOG();
   ClearMediaSource();
+  ON_INSTANCE_RELEASED(HTMLMediaElement);
 }
 
 scoped_refptr<MediaError> HTMLMediaElement::error() const {
@@ -593,6 +598,10 @@
   if (!src.empty()) {
     set_src(src);
   }
+
+  if (HasAttribute("muted")) {
+    set_muted(true);
+  }
 }
 
 void HTMLMediaElement::TraceMembers(script::Tracer* tracer) {
@@ -1599,7 +1608,7 @@
 
 void HTMLMediaElement::SawUnsupportedTracks() { NOTIMPLEMENTED(); }
 
-float HTMLMediaElement::Volume() const { return volume(NULL); }
+float HTMLMediaElement::Volume() const { return muted_ ? 0 : volume(NULL); }
 
 void HTMLMediaElement::SourceOpened(ChunkDemuxer* chunk_demuxer) {
   TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::SourceOpened()");
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc
index 57bc8c0..c3df595 100644
--- a/src/cobalt/dom/media_source.cc
+++ b/src/cobalt/dom/media_source.cc
@@ -64,10 +64,10 @@
 namespace cobalt {
 namespace dom {
 
-using media::PipelineStatus;
-using media::CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR;
 using media::CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR;
+using media::CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR;
 using media::PIPELINE_OK;
+using media::PipelineStatus;
 
 namespace {
 
@@ -102,12 +102,13 @@
 
 }  // namespace
 
-MediaSource::MediaSource()
-    : chunk_demuxer_(NULL),
+MediaSource::MediaSource(script::EnvironmentSettings* settings)
+    : EventTarget(settings),
+      chunk_demuxer_(NULL),
       ready_state_(kMediaSourceReadyStateClosed),
       ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
-      source_buffers_(new SourceBufferList(&event_queue_)),
-      active_source_buffers_(new SourceBufferList(&event_queue_)),
+      source_buffers_(new SourceBufferList(settings, &event_queue_)),
+      active_source_buffers_(new SourceBufferList(settings, &event_queue_)),
       live_seekable_range_(new TimeRanges) {}
 
 MediaSource::~MediaSource() { SetReadyState(kMediaSourceReadyStateClosed); }
@@ -218,7 +219,7 @@
   switch (status) {
     case ChunkDemuxer::kOk:
       source_buffer =
-          new SourceBuffer(guid, this, chunk_demuxer_, &event_queue_);
+          new SourceBuffer(settings, guid, this, chunk_demuxer_, &event_queue_);
       break;
     case ChunkDemuxer::kNotSupported:
       DOMException::Raise(DOMException::kNotSupportedErr, exception_state);
diff --git a/src/cobalt/dom/media_source.h b/src/cobalt/dom/media_source.h
index 5c3157e..5951b33 100644
--- a/src/cobalt/dom/media_source.h
+++ b/src/cobalt/dom/media_source.h
@@ -77,7 +77,7 @@
 
   // Custom, not in any spec.
   //
-  MediaSource();
+  explicit MediaSource(script::EnvironmentSettings* settings);
   ~MediaSource();
 
   // Web API: MediaSource
diff --git a/src/cobalt/dom/media_source.idl b/src/cobalt/dom/media_source.idl
index 5529e0f..63a64d8 100644
--- a/src/cobalt/dom/media_source.idl
+++ b/src/cobalt/dom/media_source.idl
@@ -15,7 +15,11 @@
 // https://www.w3.org/TR/media-source/#idl-def-mediasource
 // https://www.w3.org/TR/2016/CR-media-source-20160705/#idl-def-MediaSource
 
-[Constructor] interface MediaSource : EventTarget {
+[
+  Constructor,
+  ConstructorCallWith=EnvironmentSettings,
+]
+interface MediaSource : EventTarget {
   // All the source buffers created by this object.
   readonly attribute SourceBufferList sourceBuffers;
   // Subset of sourceBuffers that provide data for the selected/enabled tracks.
diff --git a/src/cobalt/dom/mutation_observer.cc b/src/cobalt/dom/mutation_observer.cc
index 55c33ca..0472f70 100644
--- a/src/cobalt/dom/mutation_observer.cc
+++ b/src/cobalt/dom/mutation_observer.cc
@@ -15,6 +15,7 @@
 #include "cobalt/dom/mutation_observer.h"
 
 #include "base/trace_event/trace_event.h"
+#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
@@ -83,8 +84,9 @@
 
 MutationObserver::MutationObserver(
     const NativeMutationCallback& native_callback,
-    MutationObserverTaskManager* task_manager)
-    : task_manager_(task_manager) {
+    MutationObserverTaskManager* task_manager,
+    base::DebuggerHooks* debugger_hooks)
+    : task_manager_(task_manager), debugger_hooks_(debugger_hooks) {
   callback_.reset(new NativeCallback(native_callback));
   task_manager_->OnMutationObserverCreated(this);
 }
@@ -92,7 +94,9 @@
 MutationObserver::MutationObserver(script::EnvironmentSettings* settings,
                                    const MutationCallbackArg& callback)
     : task_manager_(base::polymorphic_downcast<DOMSettings*>(settings)
-                        ->mutation_observer_task_manager()) {
+                        ->mutation_observer_task_manager()),
+      debugger_hooks_(base::polymorphic_downcast<DOMSettings*>(settings)
+                          ->debugger_hooks()) {
   callback_.reset(new ScriptCallback(callback, this));
   task_manager_->OnMutationObserverCreated(this);
 }
@@ -108,6 +112,7 @@
 }
 
 void MutationObserver::Disconnect() {
+  CancelDebuggerAsyncTasks();
   // The disconnect() method must, for each node in the context object's
   // list of nodes, remove any registered observer on node for which the context
   // object is the observer, and also empty context object's record queue.
@@ -123,6 +128,7 @@
 }
 
 MutationObserver::MutationRecordSequence MutationObserver::TakeRecords() {
+  CancelDebuggerAsyncTasks();
   // The takeRecords() method must return a copy of the record queue and then
   // empty the record queue.
   MutationRecordSequence record_queue;
@@ -135,6 +141,10 @@
   TRACE_EVENT0("cobalt::dom", "MutationObserver::QueueMutationRecord()");
   record_queue_.push_back(record);
   task_manager_->QueueMutationObserverMicrotask();
+  MutationRecord* task = record.get();
+  debugger_hooks_->AsyncTaskScheduled(
+      task, record->type().c_str(),
+      base::DebuggerHooks::AsyncTaskFrequency::kOneshot);
 }
 
 bool MutationObserver::Notify() {
@@ -143,7 +153,8 @@
   // Step 3 of "notify mutation observers" steps:
   //     1. Let queue be a copy of mo's record queue.
   //     2. Empty mo's record queue.
-  MutationRecordSequence records = TakeRecords();
+  MutationRecordSequence records;
+  records.swap(record_queue_);
 
   //     3. Remove all transient registered observers whose observer is mo.
   // TODO: handle transient registered observers.
@@ -152,6 +163,9 @@
   //        argument, and mo (itself) as second argument and callback this
   //        value. If this throws an exception, report the exception.
   if (!records.empty()) {
+    // Report the first (earliest) stack as the async cause.
+    MutationRecord* task = records.begin()->get();
+    base::ScopedAsyncTask async_task(debugger_hooks_, task);
     return callback_->RunCallback(records, base::WrapRefCounted(this));
   }
   // If no records, return true to indicate no error occurred.
@@ -163,6 +177,13 @@
   tracer->TraceItems(record_queue_);
 }
 
+void MutationObserver::CancelDebuggerAsyncTasks() {
+  for (auto record : record_queue_) {
+    MutationRecord* task = record.get();
+    debugger_hooks_->AsyncTaskCanceled(task);
+  }
+}
+
 void MutationObserver::TrackObservedNode(const scoped_refptr<dom::Node>& node) {
   for (WeakNodeVector::iterator it = observed_nodes_.begin();
        it != observed_nodes_.end();) {
diff --git a/src/cobalt/dom/mutation_observer.h b/src/cobalt/dom/mutation_observer.h
index 962ea0a..c7f50ef 100644
--- a/src/cobalt/dom/mutation_observer.h
+++ b/src/cobalt/dom/mutation_observer.h
@@ -29,6 +29,10 @@
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/wrappable.h"
 
+namespace base {
+class DebuggerHooks;
+}  // namespace base
+
 namespace cobalt {
 namespace dom {
 
@@ -55,7 +59,8 @@
   // Not part of the spec. Support creating MutationObservers from native Cobalt
   // code.
   MutationObserver(const NativeMutationCallback& native_callback,
-                   MutationObserverTaskManager* task_manager);
+                   MutationObserverTaskManager* task_manager,
+                   base::DebuggerHooks* debugger_hooks);
 
   // Web Api: MutationObserver
   MutationObserver(script::EnvironmentSettings* settings,
@@ -95,6 +100,7 @@
   void TraceMembers(script::Tracer* tracer) override;
 
  private:
+  void CancelDebuggerAsyncTasks();
   void TrackObservedNode(const scoped_refptr<dom::Node>& node);
 
   void ObserveInternal(const scoped_refptr<Node>& target,
@@ -106,6 +112,7 @@
   WeakNodeVector observed_nodes_;
   MutationRecordSequence record_queue_;
   MutationObserverTaskManager* task_manager_;
+  base::DebuggerHooks* debugger_hooks_;
 };
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/mutation_observer_test.cc b/src/cobalt/dom/mutation_observer_test.cc
index e168c80..edae725 100644
--- a/src/cobalt/dom/mutation_observer_test.cc
+++ b/src/cobalt/dom/mutation_observer_test.cc
@@ -26,12 +26,15 @@
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/testing/mock_exception_state.h"
 #include "cobalt/test/empty_document.h"
+#include "cobalt/test/mock_debugger_hooks.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
 using ::testing::SaveArg;
 
+constexpr auto kOneshot = base::DebuggerHooks::AsyncTaskFrequency::kOneshot;
+
 namespace cobalt {
 namespace dom {
 // Helper struct for childList mutations.
@@ -42,6 +45,8 @@
   scoped_refptr<dom::NodeList> removed_nodes;
 };
 
+typedef ::testing::StrictMock<test::MockDebuggerHooks> DebuggerHooksMock;
+
 class MutationCallbackMock {
  public:
   MOCK_METHOD2(NativeMutationCallback,
@@ -53,6 +58,7 @@
  protected:
   dom::Document* document() { return empty_document_.document(); }
   MutationObserverTaskManager* task_manager() { return &task_manager_; }
+  DebuggerHooksMock* debugger_hooks() { return &debugger_hooks_; }
   MutationCallbackMock* callback_mock() { return &callback_mock_; }
 
   scoped_refptr<Element> CreateDiv() {
@@ -66,7 +72,7 @@
     return new MutationObserver(
         base::Bind(&MutationCallbackMock::NativeMutationCallback,
                    base::Unretained(&callback_mock_)),
-        &task_manager_);
+        &task_manager_, &debugger_hooks_);
   }
 
   ChildListMutationArguments CreateChildListMutationArguments() {
@@ -85,6 +91,7 @@
 
  private:
   MutationObserverTaskManager task_manager_;
+  DebuggerHooksMock debugger_hooks_;
   test::EmptyDocument empty_document_;
   MutationCallbackMock callback_mock_;
   base::MessageLoop message_loop_;
@@ -220,9 +227,14 @@
   scoped_refptr<MutationRecord> record =
       MutationRecord::CreateCharacterDataMutationRecord(
           target, std::string("old_character_data"));
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(),
+              AsyncTaskScheduled(_, "characterData", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->QueueMutationRecord(record);
 
   // The queued record can be taken once.
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task));
   records = observer->TakeRecords();
   ASSERT_EQ(1, records.size());
   ASSERT_EQ(records.at(0), record);
@@ -238,13 +250,19 @@
   scoped_refptr<MutationRecord> record =
       MutationRecord::CreateCharacterDataMutationRecord(
           target, std::string("old_character_data"));
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(),
+              AsyncTaskScheduled(_, "characterData", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->QueueMutationRecord(record);
 
   // Callback should be fired with the first argument being a sequence of the
   // queued record, and the second argument being the observer.
   MutationObserver::MutationRecordSequence records;
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskStarted(async_task));
   EXPECT_CALL(*callback_mock(), NativeMutationCallback(_, observer))
       .WillOnce(SaveArg<0>(&records));
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskFinished(async_task));
   observer->Notify();
   ASSERT_EQ(1, records.size());
   EXPECT_EQ(record, records.at(0));
@@ -253,6 +271,26 @@
   // fired.
   records = observer->TakeRecords();
   EXPECT_TRUE(records.empty());
+
+  // Queue another mutation record on the same ovserver.
+  record = MutationRecord::CreateAttributeMutationRecord(
+      target, "attribute_name", std::string("old_attribute_data"));
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskScheduled(_, "attributes", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
+  observer->QueueMutationRecord(record);
+
+  // Check that the new record goes to the callback.
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskStarted(async_task));
+  EXPECT_CALL(*callback_mock(), NativeMutationCallback(_, observer))
+      .WillOnce(SaveArg<0>(&records));
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskFinished(async_task));
+  observer->Notify();
+  ASSERT_EQ(1, records.size());
+  EXPECT_EQ(record, records.at(0));
+
+  // No more records after notifying.
+  records = observer->TakeRecords();
+  EXPECT_TRUE(records.empty());
 }
 
 TEST_F(MutationObserverTest, ReportMutation) {
@@ -273,6 +311,13 @@
   MutationReporter reporter(target.get(), std::move(registered_observers));
 
   // Report a few mutations.
+  const void* async_task_1;
+  const void* async_task_2;
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskScheduled(_, "attributes", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task_1));
+  EXPECT_CALL(*debugger_hooks(),
+              AsyncTaskScheduled(_, "characterData", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task_2));
   reporter.ReportAttributesMutation("attribute_name", std::string("old_value"));
   reporter.ReportCharacterDataMutation("old_character_data");
   ChildListMutationArguments args = CreateChildListMutationArguments();
@@ -281,6 +326,8 @@
 
   // Check that mutation records for the mutation types we care about have
   // been queued.
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task_1));
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task_2));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(2, records.size());
   EXPECT_EQ(records.at(0)->type(), "attributes");
@@ -306,12 +353,19 @@
       RegisteredObserver(target.get(), observer, init));
   MutationReporter reporter(target.get(), std::move(registered_observers));
 
-  // Report a few attribute mutations.
+  // Report a few attribute mutations, two of which will get through the filter.
+  const void* async_task_1;
+  const void* async_task_2;
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskScheduled(_, "attributes", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task_1))
+      .WillOnce(SaveArg<0>(&async_task_2));
   reporter.ReportAttributesMutation("banana", std::string("rotten"));
   reporter.ReportAttributesMutation("apple", std::string("wormy"));
   reporter.ReportAttributesMutation("potato", std::string("mashed"));
 
   // Check that mutation records for the filtered attrbiutes have been queued.
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task_1));
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task_2));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(2, records.size());
   EXPECT_STREQ(records.at(0)->attribute_name()->c_str(), "banana");
@@ -415,6 +469,12 @@
   MutationObserverInit options;
   options.set_subtree(true);
   options.set_child_list(true);
+
+  const void* async_task_1;
+  const void* async_task_2;
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskScheduled(_, "childList", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task_1))
+      .WillOnce(SaveArg<0>(&async_task_2));
   observer->Observe(root, options);
 
   scoped_refptr<Element> child1 = document()->CreateElement("div");
@@ -425,6 +485,8 @@
   root->AppendChild(child1);
   child1->AppendChild(child2);
 
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task_1));
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task_2));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(2, records.size());
   EXPECT_EQ("childList", records.at(0)->type());
@@ -458,10 +520,15 @@
   MutationObserverInit options;
   options.set_subtree(true);
   options.set_child_list(true);
+
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskScheduled(_, "childList", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->Observe(root, options);
 
   child1->RemoveChild(child2);
 
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(1, records.size());
   EXPECT_EQ("childList", records.at(0)->type());
@@ -481,10 +548,16 @@
   MutationObserverInit options;
   options.set_subtree(true);
   options.set_character_data(true);
+
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(),
+              AsyncTaskScheduled(_, "characterData", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->Observe(root, options);
 
   text->set_data("new-data");
 
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(1, records.size());
   EXPECT_EQ("characterData", records.at(0)->type());
@@ -502,10 +575,16 @@
   MutationObserverInit options;
   options.set_subtree(true);
   options.set_character_data_old_value(true);
+
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(),
+              AsyncTaskScheduled(_, "characterData", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->Observe(root, options);
 
   text->set_data("new-data");
 
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(1, records.size());
   EXPECT_EQ("characterData", records.at(0)->type());
@@ -525,11 +604,16 @@
   MutationObserverInit options;
   options.set_attributes(true);
   options.set_attribute_filter(filter);
+
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskScheduled(_, "attributes", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->Observe(root, options);
 
   root->SetAttribute("banana", "yellow");
   root->SetAttribute("apple", "brown");
 
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(1, records.size());
   EXPECT_EQ("attributes", records.at(0)->type());
@@ -546,10 +630,15 @@
   scoped_refptr<MutationObserver> observer = CreateObserver();
   MutationObserverInit options;
   options.set_attribute_old_value(true);
+
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskScheduled(_, "attributes", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->Observe(root, options);
 
   root->SetAttribute("banana", "yellow");
 
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task));
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   ASSERT_EQ(1, records.size());
   EXPECT_EQ("attributes", records.at(0)->type());
@@ -570,11 +659,17 @@
   MutationObserverInit options;
   options.set_subtree(true);
   options.set_character_data(true);
+
+  const void* async_task;
+  EXPECT_CALL(*debugger_hooks(),
+              AsyncTaskScheduled(_, "characterData", kOneshot))
+      .WillOnce(SaveArg<0>(&async_task));
   observer->Observe(root, options);
 
   // This should queue up a mutation record.
   text->set_data("new-data");
 
+  EXPECT_CALL(*debugger_hooks(), AsyncTaskCanceled(async_task));
   observer->Disconnect();
   MutationObserver::MutationRecordSequence records = observer->TakeRecords();
   // MutationObserver.disconnect() should clear any queued records.
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index be7175b..f9507de 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/navigator.h"
 
+#include <memory>
+
 #include "base/optional.h"
 #include "cobalt/dom/captions/system_caption_settings.h"
 #include "cobalt/dom/dom_exception.h"
@@ -37,8 +37,8 @@
 namespace dom {
 
 Navigator::Navigator(
-    const std::string& user_agent, const std::string& language,
-    scoped_refptr<MediaSession> media_session,
+    script::EnvironmentSettings* settings, const std::string& user_agent,
+    const std::string& language, scoped_refptr<MediaSession> media_session,
     scoped_refptr<cobalt::dom::captions::SystemCaptionSettings> captions,
     script::ScriptValueFactory* script_value_factory)
     : user_agent_(user_agent),
@@ -46,7 +46,8 @@
       mime_types_(new MimeTypeArray()),
       plugins_(new PluginArray()),
       media_session_(media_session),
-      media_devices_(new media_capture::MediaDevices(script_value_factory)),
+      media_devices_(
+          new media_capture::MediaDevices(settings, script_value_factory)),
       system_caption_settings_(captions),
       script_value_factory_(script_value_factory) {}
 
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index 8bde10b..3a4e119 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -38,7 +38,8 @@
 class Navigator : public script::Wrappable {
  public:
   Navigator(
-      const std::string& user_agent, const std::string& language,
+      script::EnvironmentSettings* settings, const std::string& user_agent,
+      const std::string& language,
       scoped_refptr<cobalt::media_session::MediaSession> media_session,
       scoped_refptr<cobalt::dom::captions::SystemCaptionSettings> captions,
       script::ScriptValueFactory* script_value_factory);
@@ -80,10 +81,6 @@
   DEFINE_WRAPPABLE_TYPE(Navigator);
   void TraceMembers(script::Tracer* tracer) override;
 
-  void SetEnvironmentSettings(script::EnvironmentSettings* settings) {
-    media_devices_->SetEnvironmentSettings(settings);
-  }
-
  private:
   ~Navigator() override {}
 
diff --git a/src/cobalt/dom/navigator_licenses_test.cc b/src/cobalt/dom/navigator_licenses_test.cc
index ffdac52..d7f2172 100644
--- a/src/cobalt/dom/navigator_licenses_test.cc
+++ b/src/cobalt/dom/navigator_licenses_test.cc
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "cobalt/dom/navigator.h"
-
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
@@ -21,8 +21,10 @@
 
 // Tests the Navigator::licenses function for non-empty return.
 TEST(NavigatorLicensesTest, NonEmpty) {
-  scoped_refptr<cobalt::dom::Navigator> navigator = new cobalt::dom::Navigator(
-      std::string(), std::string(), nullptr, nullptr, nullptr);
+  testing::StubEnvironmentSettings environment_settings;
+  scoped_refptr<cobalt::dom::Navigator> navigator =
+      new cobalt::dom::Navigator(&environment_settings, std::string(),
+                                 std::string(), nullptr, nullptr, nullptr);
 
   ASSERT_TRUE(navigator != nullptr);
   EXPECT_FALSE(navigator->licenses().empty());
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index c7f01b7..e411663 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -450,7 +450,11 @@
 }
 
 Node::Node(Document* document)
-    : node_document_(base::AsWeakPtr(document)),
+    : Node(document->html_element_context(), document) {}
+
+Node::Node(HTMLElementContext* html_element_context, Document* document)
+    : EventTarget(html_element_context->environment_settings()),
+      node_document_(base::AsWeakPtr(document)),
       parent_(NULL),
       previous_sibling_(NULL),
       last_child_(NULL),
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h
index 3f6cd23..921a31a 100644
--- a/src/cobalt/dom/node.h
+++ b/src/cobalt/dom/node.h
@@ -37,6 +37,7 @@
 class DocumentType;
 class Element;
 class HTMLCollection;
+class HTMLElementContext;
 class NodeList;
 class Text;
 
@@ -238,6 +239,12 @@
   void TraceMembers(script::Tracer* tracer) override;
 
  protected:
+  // Constructor only for Document, since its html_element_context is not yet
+  // initialized.
+  Node(HTMLElementContext* html_element_context, Document* document);
+
+  // Constructor for everything else since we can get html_element_context()
+  // from the document.
   explicit Node(Document* document);
   virtual ~Node();
 
diff --git a/src/cobalt/dom/node_list_live_test.cc b/src/cobalt/dom/node_list_live_test.cc
index b13b8d9..6d23f82 100644
--- a/src/cobalt/dom/node_list_live_test.cc
+++ b/src/cobalt/dom/node_list_live_test.cc
@@ -18,6 +18,7 @@
 #include "cobalt/dom/dom_stat_tracker.h"
 #include "cobalt/dom/element.h"
 #include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
@@ -27,14 +28,15 @@
  protected:
   NodeListLiveTest()
       : dom_stat_tracker_("NodeListLiveTest"),
-        html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              &dom_stat_tracker_, "",
+        html_element_context_(&environment_settings_, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, &dom_stat_tracker_, "",
                               base::kApplicationStateStarted, NULL),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListLiveTest() override {}
 
+  testing::StubEnvironmentSettings environment_settings_;
   DomStatTracker dom_stat_tracker_;
   HTMLElementContext html_element_context_;
   scoped_refptr<Document> document_;
diff --git a/src/cobalt/dom/node_list_test.cc b/src/cobalt/dom/node_list_test.cc
index e1a3bc4..4cd3d6f 100644
--- a/src/cobalt/dom/node_list_test.cc
+++ b/src/cobalt/dom/node_list_test.cc
@@ -12,14 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/node_list.h"
 
+#include <memory>
+
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/dom_stat_tracker.h"
 #include "cobalt/dom/element.h"
 #include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
@@ -29,14 +30,15 @@
  protected:
   NodeListTest()
       : dom_stat_tracker_(new DomStatTracker("NodeListTest")),
-        html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              dom_stat_tracker_.get(), "",
+        html_element_context_(&environment_settings_, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, dom_stat_tracker_.get(), "",
                               base::kApplicationStateStarted, NULL),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListTest() override {}
 
+  testing::StubEnvironmentSettings environment_settings_;
   std::unique_ptr<DomStatTracker> dom_stat_tracker_;
   HTMLElementContext html_element_context_;
   scoped_refptr<Document> document_;
diff --git a/src/cobalt/dom/on_screen_keyboard.cc b/src/cobalt/dom/on_screen_keyboard.cc
index ffa725b..fde3fe7 100644
--- a/src/cobalt/dom/on_screen_keyboard.cc
+++ b/src/cobalt/dom/on_screen_keyboard.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/on_screen_keyboard.h"
 
+#include <memory>
+
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "cobalt/dom/event_target.h"
@@ -25,9 +25,10 @@
 namespace dom {
 
 OnScreenKeyboard::OnScreenKeyboard(
-    OnScreenKeyboardBridge* bridge,
+    script::EnvironmentSettings* settings, OnScreenKeyboardBridge* bridge,
     script::ScriptValueFactory* script_value_factory)
-    : bridge_(bridge),
+    : EventTarget(settings),
+      bridge_(bridge),
       script_value_factory_(script_value_factory),
       next_ticket_(0) {
   DCHECK(bridge_) << "OnScreenKeyboardBridge must not be NULL";
diff --git a/src/cobalt/dom/on_screen_keyboard.h b/src/cobalt/dom/on_screen_keyboard.h
index 57a0487..bc781f9 100644
--- a/src/cobalt/dom/on_screen_keyboard.h
+++ b/src/cobalt/dom/on_screen_keyboard.h
@@ -25,6 +25,7 @@
 #include "cobalt/dom/event_target.h"
 #include "cobalt/dom/on_screen_keyboard_bridge.h"
 #include "cobalt/dom/window.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/promise.h"
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/wrappable.h"
@@ -43,7 +44,8 @@
   typedef std::unordered_map<int, std::unique_ptr<VoidPromiseValue::Reference>>
       TicketToPromiseMap;
 
-  OnScreenKeyboard(OnScreenKeyboardBridge* bridge,
+  OnScreenKeyboard(script::EnvironmentSettings* settings,
+                   OnScreenKeyboardBridge* bridge,
                    script::ScriptValueFactory* script_value_factory);
 
   // Shows the on screen keyboard by calling a Starboard function.
diff --git a/src/cobalt/dom/on_screen_keyboard_test.cc b/src/cobalt/dom/on_screen_keyboard_test.cc
index 59e4260..bd2b4ac 100644
--- a/src/cobalt/dom/on_screen_keyboard_test.cc
+++ b/src/cobalt/dom/on_screen_keyboard_test.cc
@@ -19,12 +19,12 @@
 #include "base/callback.h"
 #include "base/optional.h"
 #include "base/threading/platform_thread.h"
-#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/bindings/testing/utils.h"
 #include "cobalt/css_parser/parser.h"
 #include "cobalt/cssom/viewport_size.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/testing/gtest_workarounds.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
@@ -193,7 +193,7 @@
 class OnScreenKeyboardTest : public ::testing::Test {
  public:
   OnScreenKeyboardTest()
-      : environment_settings_(new script::EnvironmentSettings),
+      : environment_settings_(new testing::StubEnvironmentSettings),
         message_loop_(base::MessageLoop::TYPE_DEFAULT),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_error_callback_)),
@@ -207,10 +207,11 @@
         global_environment_(engine_->CreateGlobalEnvironment()),
         on_screen_keyboard_bridge_(new OnScreenKeyboardMockBridge()),
         window_(new Window(
-            ViewportSize(1920, 1080), 1.f, base::kApplicationStateStarted,
-            css_parser_.get(), dom_parser_.get(), fetcher_factory_.get(),
-            loader_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
-            &local_storage_database_, NULL, NULL, NULL, NULL,
+            environment_settings_.get(), ViewportSize(1920, 1080), 1.f,
+            base::kApplicationStateStarted, css_parser_.get(),
+            dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(),
+            NULL, NULL, NULL, NULL, NULL, NULL, &local_storage_database_, NULL,
+            NULL, NULL, NULL,
             global_environment_
                 ->script_value_factory() /* script_value_factory */,
             NULL, NULL, url_, "", "en-US", "en",
@@ -226,7 +227,7 @@
             dom::Window::OnStartDispatchEventCallback(),
             dom::Window::OnStopDispatchEventCallback(),
             dom::ScreenshotManager::ProvideScreenshotFunctionCallback(),
-            NULL, null_debugger_hooks_)) {
+            NULL)) {
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
     on_screen_keyboard_bridge_->window_ = window_;
@@ -261,7 +262,7 @@
   Window* window() const { return window_.get(); }
 
  private:
-  const std::unique_ptr<script::EnvironmentSettings> environment_settings_;
+  const std::unique_ptr<testing::StubEnvironmentSettings> environment_settings_;
   base::MessageLoop message_loop_;
   MockErrorCallback mock_error_callback_;
   std::unique_ptr<css_parser::Parser> css_parser_;
@@ -274,7 +275,6 @@
   std::unique_ptr<script::JavaScriptEngine> engine_;
   scoped_refptr<script::GlobalEnvironment> global_environment_;
   std::unique_ptr<OnScreenKeyboardMockBridge> on_screen_keyboard_bridge_;
-  base::NullDebuggerHooks null_debugger_hooks_;
   scoped_refptr<Window> window_;
 };
 
@@ -294,7 +294,8 @@
 
 }  // namespace
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 TEST_F(OnScreenKeyboardTest, ObjectExists) {
   std::string result;
   EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard;", &result));
@@ -638,7 +639,7 @@
 TEST_F(OnScreenKeyboardTest, BoundingRect) {
   std::string result;
   EXPECT_CALL(*(on_screen_keyboard_bridge()), BoundingRectMock())
-      .WillOnce(testing::Return(nullptr));
+      .WillOnce(::testing::Return(nullptr));
   EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.boundingRect;", &result));
   EXPECT_EQ("null", result);
 }
@@ -663,7 +664,8 @@
   )";
   EXPECT_TRUE(EvaluateScript(script, NULL));
 }
-#else   // SB_HAS(ON_SCREEN_KEYBOARD)
+#else   // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 TEST_F(OnScreenKeyboardTest, ObjectDoesntExist) {
   std::string result;
 
@@ -692,7 +694,8 @@
   EXPECT_TRUE(EvaluateScript(object_script, &result));
   EXPECT_EQ("true", result);
 }
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index 18531c3..75f1f51 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -32,11 +32,12 @@
 #include "cobalt/dom/node.h"
 #include "cobalt/dom/node_descendants_iterator.h"
 #include "cobalt/dom/node_list.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_window.h"
 #include "cobalt/dom_parser/parser.h"
-#include "testing/gtest/include/gtest/gtest.h"
 #include "cobalt/script/script_exception.h"
 #include "cobalt/script/testing/mock_exception_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 using cobalt::cssom::ViewportSize;
 
@@ -52,9 +53,10 @@
       : css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser()),
         dom_stat_tracker_(new DomStatTracker("RuleMatchingTest")),
-        html_element_context_(NULL, NULL, css_parser_.get(), dom_parser_.get(),
+        html_element_context_(&environment_settings_, NULL, NULL,
+                              css_parser_.get(), dom_parser_.get(), NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, dom_stat_tracker_.get(), "",
+                              NULL, dom_stat_tracker_.get(), "",
                               base::kApplicationStateStarted, NULL),
         document_(new Document(&html_element_context_)),
         root_(document_->CreateElement("html")->AsHTMLElement()),
@@ -74,6 +76,7 @@
     return document_->style_sheets()->Item(index)->AsCSSStyleSheet();
   }
 
+  testing::StubEnvironmentSettings environment_settings_;
   std::unique_ptr<css_parser::Parser> css_parser_;
   std::unique_ptr<dom_parser::Parser> dom_parser_;
   std::unique_ptr<DomStatTracker> dom_stat_tracker_;
diff --git a/src/cobalt/dom/screenshot_manager.cc b/src/cobalt/dom/screenshot_manager.cc
index f687626..b7aa043 100644
--- a/src/cobalt/dom/screenshot_manager.cc
+++ b/src/cobalt/dom/screenshot_manager.cc
@@ -12,24 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom/screenshot_manager.h"
 
+#include <memory>
+
 #include "base/time/time.h"
 #include "cobalt/dom/screenshot.h"
 #include "cobalt/render_tree/node.h"
-#include "cobalt/script/array_buffer.h"
-
 #include "cobalt/render_tree/resource_provider_stub.h"
+#include "cobalt/script/array_buffer.h"
 
 namespace cobalt {
 namespace dom {
 
 ScreenshotManager::ScreenshotManager(
+    script::EnvironmentSettings* settings,
     const ScreenshotManager::ProvideScreenshotFunctionCallback&
         screenshot_function_callback)
-    : screenshot_function_callback_(screenshot_function_callback) {}
+    : environment_settings_(settings),
+      screenshot_function_callback_(screenshot_function_callback) {}
 
 void ScreenshotManager::Screenshot(
     loader::image::EncodedStaticImage::ImageFormat desired_format,
@@ -56,11 +57,6 @@
       render_tree_root, /*clip_rect=*/base::nullopt, fill_screenshot);
 }
 
-void ScreenshotManager::SetEnvironmentSettings(
-    script::EnvironmentSettings* settings) {
-  environment_settings_ = settings;
-}
-
 void ScreenshotManager::FillScreenshot(
     int64_t token,
     scoped_refptr<base::SingleThreadTaskRunner> expected_task_runner,
diff --git a/src/cobalt/dom/screenshot_manager.h b/src/cobalt/dom/screenshot_manager.h
index f26269f..3a2d559 100644
--- a/src/cobalt/dom/screenshot_manager.h
+++ b/src/cobalt/dom/screenshot_manager.h
@@ -49,14 +49,14 @@
                           const OnUnencodedImageCallback&)>;
 
   explicit ScreenshotManager(
-      const ProvideScreenshotFunctionCallback& screenshot_function_callback_);
+      script::EnvironmentSettings* settings,
+      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(
diff --git a/src/cobalt/dom/serializer_test.cc b/src/cobalt/dom/serializer_test.cc
index e7ac9f0..510d624 100644
--- a/src/cobalt/dom/serializer_test.cc
+++ b/src/cobalt/dom/serializer_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/dom/serializer.h"
+
 #include <memory>
 #include <sstream>
 #include <string>
@@ -21,7 +23,7 @@
 #include "cobalt/dom/document_type.h"
 #include "cobalt/dom/element.h"
 #include "cobalt/dom/html_element_context.h"
-#include "cobalt/dom/serializer.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom_parser/parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -33,6 +35,7 @@
   SerializerTest();
   ~SerializerTest() override {}
 
+  testing::StubEnvironmentSettings environment_settings_;
   std::unique_ptr<dom_parser::Parser> dom_parser_;
   std::unique_ptr<DomStatTracker> dom_stat_tracker_;
   HTMLElementContext html_element_context_;
@@ -45,9 +48,9 @@
 SerializerTest::SerializerTest()
     : dom_parser_(new dom_parser::Parser()),
       dom_stat_tracker_(new DomStatTracker("SerializerTest")),
-      html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            dom_stat_tracker_.get(), "",
+      html_element_context_(&environment_settings_, NULL, NULL, NULL, NULL,
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                            NULL, NULL, NULL, dom_stat_tracker_.get(), "",
                             base::kApplicationStateStarted, NULL),
       document_(new Document(&html_element_context_)),
       root_(new Element(document_, base::Token("root"))),
diff --git a/src/cobalt/dom/source_buffer.cc b/src/cobalt/dom/source_buffer.cc
index b879fc4..08ea6e4 100644
--- a/src/cobalt/dom/source_buffer.cc
+++ b/src/cobalt/dom/source_buffer.cc
@@ -86,10 +86,12 @@
 
 }  // namespace
 
-SourceBuffer::SourceBuffer(const std::string& id, MediaSource* media_source,
+SourceBuffer::SourceBuffer(script::EnvironmentSettings* settings,
+                           const std::string& id, MediaSource* media_source,
                            media::ChunkDemuxer* chunk_demuxer,
                            EventQueue* event_queue)
-    : id_(id),
+    : EventTarget(settings),
+      id_(id),
       chunk_demuxer_(chunk_demuxer),
       media_source_(media_source),
       track_defaults_(new TrackDefaultList(NULL)),
@@ -97,8 +99,10 @@
       mode_(kSourceBufferAppendModeSegments),
       updating_(false),
       timestamp_offset_(0),
-      audio_tracks_(new AudioTrackList(media_source->GetMediaElement())),
-      video_tracks_(new VideoTrackList(media_source->GetMediaElement())),
+      audio_tracks_(
+          new AudioTrackList(settings, media_source->GetMediaElement())),
+      video_tracks_(
+          new VideoTrackList(settings, media_source->GetMediaElement())),
       append_window_start_(0),
       append_window_end_(std::numeric_limits<double>::infinity()),
       first_initialization_segment_received_(false),
diff --git a/src/cobalt/dom/source_buffer.h b/src/cobalt/dom/source_buffer.h
index 8c8b904..efdd34b 100644
--- a/src/cobalt/dom/source_buffer.h
+++ b/src/cobalt/dom/source_buffer.h
@@ -65,6 +65,7 @@
 #include "cobalt/media/filters/chunk_demuxer.h"
 #include "cobalt/script/array_buffer.h"
 #include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/exception_state.h"
 
 namespace cobalt {
@@ -79,8 +80,9 @@
  public:
   // Custom, not in any spec.
   //
-  SourceBuffer(const std::string& id, MediaSource* media_source,
-               media::ChunkDemuxer* chunk_demuxer, EventQueue* event_queue);
+  SourceBuffer(script::EnvironmentSettings* settings, const std::string& id,
+               MediaSource* media_source, media::ChunkDemuxer* chunk_demuxer,
+               EventQueue* event_queue);
 
   // Web API: SourceBuffer
   //
diff --git a/src/cobalt/dom/source_buffer_list.cc b/src/cobalt/dom/source_buffer_list.cc
index 5417fa9..2431228 100644
--- a/src/cobalt/dom/source_buffer_list.cc
+++ b/src/cobalt/dom/source_buffer_list.cc
@@ -60,8 +60,9 @@
 const int kSizeOfSourceBufferToReserveInitially = 2;
 }  // namespace
 
-SourceBufferList::SourceBufferList(EventQueue* event_queue)
-    : event_queue_(event_queue) {
+SourceBufferList::SourceBufferList(script::EnvironmentSettings* settings,
+                                   EventQueue* event_queue)
+    : EventTarget(settings), event_queue_(event_queue) {
   DCHECK(event_queue_);
   source_buffers_.reserve(kSizeOfSourceBufferToReserveInitially);
 }
diff --git a/src/cobalt/dom/source_buffer_list.h b/src/cobalt/dom/source_buffer_list.h
index cbd4092..4e1da23 100644
--- a/src/cobalt/dom/source_buffer_list.h
+++ b/src/cobalt/dom/source_buffer_list.h
@@ -52,6 +52,7 @@
 #include "cobalt/dom/event_queue.h"
 #include "cobalt/dom/event_target.h"
 #include "cobalt/dom/source_buffer.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -65,7 +66,8 @@
  public:
   // Custom, not in any spec.
   //
-  explicit SourceBufferList(EventQueue* event_queue);
+  SourceBufferList(script::EnvironmentSettings* settings,
+                   EventQueue* event_queue);
   ~SourceBufferList() override;
 
   // Web API: SourceBuffer
diff --git a/src/cobalt/dom/testing/dom_testing.gyp b/src/cobalt/dom/testing/dom_testing.gyp
index 2f4ccd2..dfb0175 100644
--- a/src/cobalt/dom/testing/dom_testing.gyp
+++ b/src/cobalt/dom/testing/dom_testing.gyp
@@ -26,6 +26,7 @@
         'mock_event_listener.h',
         'stub_css_parser.cc',
         'stub_css_parser.h',
+        'stub_environment_settings.h',
         'stub_script_runner.cc',
         'stub_script_runner.h',
         'stub_window.h',
diff --git a/src/cobalt/dom/testing/stub_environment_settings.h b/src/cobalt/dom/testing/stub_environment_settings.h
new file mode 100644
index 0000000..81e11f7
--- /dev/null
+++ b/src/cobalt/dom/testing/stub_environment_settings.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Cobalt Authors. 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_TESTING_STUB_ENVIRONMENT_SETTINGS_H_
+#define COBALT_DOM_TESTING_STUB_ENVIRONMENT_SETTINGS_H_
+
+#include "cobalt/base/debugger_hooks.h"
+#include "cobalt/dom/dom_settings.h"
+
+namespace cobalt {
+namespace dom {
+namespace testing {
+
+class StubEnvironmentSettings : public DOMSettings {
+ public:
+  explicit StubEnvironmentSettings(const Options& options = Options())
+      : DOMSettings(0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+                    nullptr, &null_debugger_hooks_, nullptr, options) {}
+  ~StubEnvironmentSettings() override {}
+
+ private:
+  base::NullDebuggerHooks null_debugger_hooks_;
+};
+
+}  // namespace testing
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_TESTING_STUB_ENVIRONMENT_SETTINGS_H_
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
index 4e4964a..c059eab 100644
--- a/src/cobalt/dom/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -61,11 +61,17 @@
         dom_stat_tracker_(new dom::DomStatTracker("StubWindow")) {
     engine_ = script::JavaScriptEngine::CreateEngine();
     global_environment_ = engine_->CreateGlobalEnvironment();
+    environment_settings_ =
+        environment_settings.get()
+            ? std::move(environment_settings)
+            : std::unique_ptr<script::EnvironmentSettings>(new DOMSettings(
+                  0, NULL, NULL, NULL, NULL, NULL, engine_.get(),
+                  global_environment(), &null_debugger_hooks_, NULL));
     window_ = new dom::Window(
-        cssom::ViewportSize(1920, 1080), 1.f, base::kApplicationStateStarted,
-        css_parser_.get(), dom_parser_.get(), fetcher_factory_.get(),
-        loader_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
-        &local_storage_database_, NULL, NULL, NULL, NULL,
+        environment_settings_.get(), cssom::ViewportSize(1920, 1080), 1.f,
+        base::kApplicationStateStarted, css_parser_.get(), dom_parser_.get(),
+        fetcher_factory_.get(), loader_factory_.get(), NULL, NULL, NULL, NULL,
+        NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL,
         global_environment_->script_value_factory(), NULL,
         dom_stat_tracker_.get(), url_, "", "en-US", "en",
         base::Callback<void(const GURL&)>(),
@@ -77,15 +83,9 @@
         base::Closure() /* window_minimize */, NULL, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
-        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL,
-        null_debugger_hooks_);
-    environment_settings_ =
-        environment_settings.get()
-            ? std::move(environment_settings)
-            : std::unique_ptr<script::EnvironmentSettings>(
-                  new DOMSettings(0, NULL, NULL, window_, NULL, NULL, NULL,
-                                  engine_.get(), global_environment(), NULL));
-    window_->SetEnvironmentSettings(environment_settings_.get());
+        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
+    base::polymorphic_downcast<dom::DOMSettings*>(environment_settings_.get())
+        ->set_window(window_);
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
   }
diff --git a/src/cobalt/dom/track_list_base.h b/src/cobalt/dom/track_list_base.h
index 02ae484..37c83b1 100644
--- a/src/cobalt/dom/track_list_base.h
+++ b/src/cobalt/dom/track_list_base.h
@@ -26,6 +26,7 @@
 #include "cobalt/dom/event_target.h"
 #include "cobalt/dom/html_media_element.h"
 #include "cobalt/dom/track_event.h"
+#include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
 namespace dom {
@@ -34,7 +35,9 @@
 template <typename TrackType>
 class TrackListBase : public EventTarget {
  public:
-  explicit TrackListBase(HTMLMediaElement* media_element) {
+  TrackListBase(script::EnvironmentSettings* settings,
+                HTMLMediaElement* media_element)
+      : EventTarget(settings) {
     DCHECK(media_element);
     media_element_ = base::AsWeakPtr(media_element);
   }
diff --git a/src/cobalt/dom/video_track_list.h b/src/cobalt/dom/video_track_list.h
index 38c80c0..2cdbcba 100644
--- a/src/cobalt/dom/video_track_list.h
+++ b/src/cobalt/dom/video_track_list.h
@@ -21,6 +21,7 @@
 #include "cobalt/dom/html_media_element.h"
 #include "cobalt/dom/track_list_base.h"
 #include "cobalt/dom/video_track.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -32,8 +33,9 @@
  public:
   // Custom, not in any spec.
   //
-  explicit VideoTrackList(HTMLMediaElement* media_element)
-      : TrackListBase<VideoTrack>(media_element) {}
+  VideoTrackList(script::EnvironmentSettings* settings,
+                 HTMLMediaElement* media_element)
+      : TrackListBase<VideoTrack>(settings, media_element) {}
 
   // Web API: VideoTrackList
   //
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index d11d43d..62edced 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -91,8 +91,8 @@
 }  // namespace
 
 Window::Window(
-    const ViewportSize& view_size, float device_pixel_ratio,
-    base::ApplicationState initial_application_state,
+    script::EnvironmentSettings* settings, const ViewportSize& view_size,
+    float device_pixel_ratio, base::ApplicationState initial_application_state,
     cssom::CSSParser* css_parser, Parser* dom_parser,
     loader::FetcherFactory* fetcher_factory,
     loader::LoaderFactory* loader_factory,
@@ -130,7 +130,6 @@
     const ScreenshotManager::ProvideScreenshotFunctionCallback&
         screenshot_function_callback,
     base::WaitableEvent* synchronous_loader_interrupt,
-    const base::DebuggerHooks& debugger_hooks,
     const scoped_refptr<ui_navigation::NavItem>& ui_nav_root,
     int csp_insecure_allowed_token, int dom_max_element_depth,
     float video_playback_rate_multiplier, ClockType clock_type,
@@ -139,7 +138,7 @@
     bool log_tts)
     // 'window' object EventTargets require special handling for onerror events,
     // see EventTarget constructor for more details.
-    : EventTarget(kUnpackOnErrorEvents),
+    : EventTarget(settings, kUnpackOnErrorEvents),
       viewport_size_(view_size),
       device_pixel_ratio_(device_pixel_ratio),
       is_resize_event_pending_(false),
@@ -148,7 +147,7 @@
       test_runner_(new TestRunner()),
 #endif  // ENABLE_TEST_RUNNER
       html_element_context_(new HTMLElementContext(
-          fetcher_factory, loader_factory, css_parser, dom_parser,
+          settings, fetcher_factory, loader_factory, css_parser, dom_parser,
           can_play_type_handler, web_media_player_factory, script_runner,
           script_value_factory, media_source_registry, resource_provider,
           animated_image_tracker, image_cache,
@@ -169,17 +168,18 @@
               csp_insecure_allowed_token, dom_max_element_depth)))),
       document_loader_(nullptr),
       history_(new History()),
-      navigator_(new Navigator(user_agent, language, media_session, captions,
-                               script_value_factory)),
+      navigator_(new Navigator(settings, user_agent, language, media_session,
+                               captions, script_value_factory)),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           relay_on_load_event_(new RelayLoadEvent(this))),
       console_(new Console(execution_state)),
       ALLOW_THIS_IN_INITIALIZER_LIST(
-          window_timers_(new WindowTimers(this, debugger_hooks))),
+          window_timers_(new WindowTimers(this, debugger_hooks()))),
       ALLOW_THIS_IN_INITIALIZER_LIST(animation_frame_request_callback_list_(
-          new AnimationFrameRequestCallbackList(this))),
+          new AnimationFrameRequestCallbackList(this, debugger_hooks()))),
       crypto_(new Crypto()),
-      speech_synthesis_(new speech::SpeechSynthesis(navigator_, log_tts)),
+      speech_synthesis_(
+          new speech::SpeechSynthesis(settings, navigator_, log_tts)),
       ALLOW_THIS_IN_INITIALIZER_LIST(local_storage_(
           new Storage(this, Storage::kLocalStorage, local_storage_database))),
       ALLOW_THIS_IN_INITIALIZER_LIST(
@@ -193,13 +193,14 @@
       // We only have an on_screen_keyboard_bridge when the platform supports
       // it. Otherwise don't even expose it in the DOM.
       on_screen_keyboard_(on_screen_keyboard_bridge
-                              ? new OnScreenKeyboard(on_screen_keyboard_bridge,
+                              ? new OnScreenKeyboard(settings,
+                                                     on_screen_keyboard_bridge,
                                                      script_value_factory)
                               : 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),
-      screenshot_manager_(screenshot_function_callback),
+      screenshot_manager_(settings, screenshot_function_callback),
       ui_nav_root_(ui_nav_root) {
 #if !defined(ENABLE_TEST_RUNNER)
   SB_UNREFERENCED_PARAMETER(clock_type);
@@ -501,7 +502,7 @@
     // Then setup the Window's frame request callback list with a freshly
     // created and empty one.
     animation_frame_request_callback_list_.reset(
-        new AnimationFrameRequestCallbackList(this));
+        new AnimationFrameRequestCallbackList(this, debugger_hooks()));
 
     // Now, iterate through each of the callbacks and call them.
     frame_request_list->RunCallbacks(*document_->timeline()->current_time());
@@ -701,11 +702,6 @@
   tracer->Trace(on_screen_keyboard_);
 }
 
-void Window::SetEnvironmentSettings(script::EnvironmentSettings* settings) {
-  screenshot_manager_.SetEnvironmentSettings(settings);
-  navigator_->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 a824e37..27fa7a9 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -27,7 +27,6 @@
 #include "base/timer/timer.h"
 #include "cobalt/base/application_state.h"
 #include "cobalt/base/clock.h"
-#include "cobalt/base/debugger_hooks.h"
 #include "cobalt/cssom/css_parser.h"
 #include "cobalt/cssom/css_style_declaration.h"
 #include "cobalt/cssom/viewport_size.h"
@@ -132,6 +131,7 @@
   };
 
   Window(
+      script::EnvironmentSettings* settings,
       const cssom::ViewportSize& view_size, float device_pixel_ratio,
       base::ApplicationState initial_application_state,
       cssom::CSSParser* css_parser, Parser* dom_parser,
@@ -173,7 +173,6 @@
       const ScreenshotManager::ProvideScreenshotFunctionCallback&
           screenshot_function_callback,
       base::WaitableEvent* synchronous_loader_interrupt,
-      const base::DebuggerHooks& debugger_hooks,
       const scoped_refptr<ui_navigation::NavItem>& ui_nav_root = nullptr,
       int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0,
       float video_playback_rate_multiplier = 1.f,
@@ -398,8 +397,6 @@
 
   void TraceMembers(script::Tracer* tracer) override;
 
-  void SetEnvironmentSettings(script::EnvironmentSettings* settings);
-
   const scoped_refptr<ui_navigation::NavItem>& GetUiNavRoot() const {
     return ui_nav_root_;
   }
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index 80bc5c6..9a44ac4 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -26,6 +26,7 @@
 #include "cobalt/cssom/viewport_size.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/screen.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media_session/media_session.h"
@@ -50,7 +51,8 @@
 class WindowTest : public ::testing::Test {
  protected:
   WindowTest()
-      : message_loop_(base::MessageLoop::TYPE_DEFAULT),
+      : environment_settings_(new testing::StubEnvironmentSettings),
+        message_loop_(base::MessageLoop::TYPE_DEFAULT),
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser(mock_error_callback_)),
         fetcher_factory_(new loader::FetcherFactory(NULL)),
@@ -61,9 +63,10 @@
 
     ViewportSize view_size(1920, 1080);
     window_ = new Window(
-        view_size, 1.f, base::kApplicationStateStarted, css_parser_.get(),
-        dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
-        NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL,
+        environment_settings_.get(), view_size, 1.f,
+        base::kApplicationStateStarted, css_parser_.get(), dom_parser_.get(),
+        fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+        &local_storage_database_, NULL, NULL, NULL, NULL,
         global_environment_->script_value_factory(), NULL, NULL, url_, "",
         "en-US", "en", base::Callback<void(const GURL &)>(),
         base::Bind(&MockErrorCallback::Run,
@@ -75,12 +78,12 @@
         base::Closure() /* window_minimize */, NULL, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
-        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL,
-        null_debugger_hooks_);
+        dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
   }
 
   ~WindowTest() override {}
 
+  const std::unique_ptr<testing::StubEnvironmentSettings> environment_settings_;
   base::MessageLoop message_loop_;
   MockErrorCallback mock_error_callback_;
   std::unique_ptr<css_parser::Parser> css_parser_;
@@ -90,7 +93,6 @@
   std::unique_ptr<script::JavaScriptEngine> engine_;
   scoped_refptr<script::GlobalEnvironment> global_environment_;
   GURL url_;
-  base::NullDebuggerHooks null_debugger_hooks_;
   scoped_refptr<Window> window_;
 };
 
diff --git a/src/cobalt/dom/window_timers.cc b/src/cobalt/dom/window_timers.cc
index dd88196..be3bc87 100644
--- a/src/cobalt/dom/window_timers.cc
+++ b/src/cobalt/dom/window_timers.cc
@@ -43,7 +43,9 @@
                             base::Unretained(this), handle));
     timers_[handle] = new TimerInfo(
         owner_, std::unique_ptr<base::internal::TimerBase>(timer), handler);
-    debugger_hooks_.AsyncTaskScheduled(timers_[handle], "SetTimeout");
+    debugger_hooks_->AsyncTaskScheduled(
+        timers_[handle], "SetTimeout",
+        base::DebuggerHooks::AsyncTaskFrequency::kOneshot);
   } else {
     timers_[handle] = nullptr;
   }
@@ -69,7 +71,9 @@
                             base::Unretained(this), handle));
     timers_[handle] = new TimerInfo(
         owner_, std::unique_ptr<base::internal::TimerBase>(timer), handler);
-    debugger_hooks_.AsyncTaskScheduled(timers_[handle], "SetInterval");
+    debugger_hooks_->AsyncTaskScheduled(
+        timers_[handle], "SetInterval",
+        base::DebuggerHooks::AsyncTaskFrequency::kRecurring);
   } else {
     timers_[handle] = nullptr;
   }
@@ -80,14 +84,14 @@
 void WindowTimers::ClearInterval(int handle) {
   Timers::iterator timer = timers_.find(handle);
   if (timer != timers_.end()) {
-    debugger_hooks_.AsyncTaskCanceled(timer->second);
+    debugger_hooks_->AsyncTaskCanceled(timer->second);
     timers_.erase(timer);
   }
 }
 
 void WindowTimers::ClearAllIntervalsAndTimeouts() {
   for (auto& timer_entry : timers_) {
-    debugger_hooks_.AsyncTaskCanceled(timer_entry.second);
+    debugger_hooks_->AsyncTaskCanceled(timer_entry.second);
   }
   timers_.clear();
 }
@@ -96,7 +100,7 @@
   callbacks_active_ = false;
   // Immediately cancel any pending timers.
   for (auto& timer_entry : timers_) {
-    debugger_hooks_.AsyncTaskCanceled(timer_entry.second);
+    debugger_hooks_->AsyncTaskCanceled(timer_entry.second);
     timer_entry.second = nullptr;
   }
 }
@@ -145,7 +149,7 @@
   // If the timer is not deleted and is not running, it means it is an oneshot
   // timer and has just fired the shot, and it should be deleted now.
   if (timer != timers_.end() && !timer->second->timer()->IsRunning()) {
-    debugger_hooks_.AsyncTaskCanceled(timer->second);
+    debugger_hooks_->AsyncTaskCanceled(timer->second);
     timers_.erase(timer);
   }
 
diff --git a/src/cobalt/dom/window_timers.h b/src/cobalt/dom/window_timers.h
index 73bec1f..f17f0d8 100644
--- a/src/cobalt/dom/window_timers.h
+++ b/src/cobalt/dom/window_timers.h
@@ -33,10 +33,12 @@
   typedef script::CallbackFunction<void()> TimerCallback;
   typedef script::ScriptValue<TimerCallback> TimerCallbackArg;
   explicit WindowTimers(script::Wrappable* const owner,
-                        const base::DebuggerHooks& debugger_hooks)
+                        base::DebuggerHooks* debugger_hooks)
       : current_timer_index_(0),
         owner_(owner),
-        debugger_hooks_(debugger_hooks) {}
+        debugger_hooks_(debugger_hooks) {
+    DCHECK(debugger_hooks_);
+  }
   ~WindowTimers() {}
 
   int SetTimeout(const TimerCallbackArg& handler, int timeout);
@@ -86,7 +88,7 @@
   Timers timers_;
   int current_timer_index_;
   script::Wrappable* const owner_;
-  const base::DebuggerHooks& debugger_hooks_;
+  base::DebuggerHooks* debugger_hooks_;
 
   // Set to false when we're about to shutdown, to ensure that no new JavaScript
   // is fired as we are waiting for it to drain.
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index 80cfb29..646a077 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom_parser/html_decoder.h"
 
+#include <memory>
+
 #include "base/callback.h"
 #include "base/message_loop/message_loop.h"
 #include "base/optional.h"
@@ -28,6 +28,7 @@
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/dom/named_node_map.h"
 #include "cobalt/dom/testing/stub_css_parser.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_script_runner.h"
 #include "cobalt/dom/text.h"
 #include "cobalt/dom_parser/parser.h"
@@ -52,6 +53,7 @@
   HTMLDecoderTest();
   ~HTMLDecoderTest() override {}
 
+  dom::testing::StubEnvironmentSettings environment_settings_;
   loader::FetcherFactory fetcher_factory_;
   loader::LoaderFactory loader_factory_;
   std::unique_ptr<Parser> dom_parser_;
@@ -75,12 +77,12 @@
       dom_parser_(new Parser()),
       dom_stat_tracker_(new dom::DomStatTracker("HTMLDecoderTest")),
       html_element_context_(
-          &fetcher_factory_, &loader_factory_, &stub_css_parser_,
-          dom_parser_.get(), NULL /* can_play_type_handler */,
-          NULL /* web_media_player_factory */, &stub_script_runner_,
-          NULL /* script_value_factory */, NULL, NULL, NULL, NULL, NULL, NULL,
-          NULL, dom_stat_tracker_.get(), "", base::kApplicationStateStarted,
-          NULL),
+          &environment_settings_, &fetcher_factory_, &loader_factory_,
+          &stub_css_parser_, dom_parser_.get(),
+          NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
+          &stub_script_runner_, NULL /* script_value_factory */, NULL, NULL,
+          NULL, NULL, NULL, NULL, NULL, dom_stat_tracker_.get(), "",
+          base::kApplicationStateStarted, NULL),
       document_(new dom::Document(&html_element_context_)),
       root_(new dom::Element(document_, base::Token("element"))),
       source_location_(base::SourceLocation("[object HTMLDecoderTest]", 1, 1)) {
diff --git a/src/cobalt/extension/graphics.h b/src/cobalt/extension/graphics.h
index 1baa343..b68ac9f 100644
--- a/src/cobalt/extension/graphics.h
+++ b/src/cobalt/extension/graphics.h
@@ -44,6 +44,9 @@
   // is executed a little too early. Return a negative number if frames should
   // only be presented when something changes (i.e. there is no maximum frame
   // interval).
+  // NOTE: The gyp variable 'cobalt_minimum_frame_time_in_milliseconds' takes
+  // precedence over this. For example, if the minimum frame time is 8ms and
+  // the maximum frame interval is 0ms, then the renderer will target 125 fps.
   float (*GetMaximumFrameIntervalInMilliseconds)();
 } CobaltExtensionGraphicsApi;
 
diff --git a/src/cobalt/input/input_device_manager.h b/src/cobalt/input/input_device_manager.h
index 94a1f72..5045901 100644
--- a/src/cobalt/input/input_device_manager.h
+++ b/src/cobalt/input/input_device_manager.h
@@ -40,10 +40,12 @@
 typedef base::Callback<void(base::Token type, const dom::WheelEventInit&)>
     WheelEventCallback;
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 typedef base::Callback<void(base::Token type, const dom::InputEventInit&)>
     InputEventCallback;
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 // InputDeviceManager listens to events from platform-specific input devices
 // and maps them to platform-independent keyboard key events.
@@ -55,9 +57,11 @@
       const KeyboardEventCallback& keyboard_event_callback,
       const PointerEventCallback& pointer_event_callback,
       const WheelEventCallback& wheel_event_callback,
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
       const InputEventCallback& input_event_callback,
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
       system_window::SystemWindow* system_window);
 
   virtual ~InputDeviceManager() {}
diff --git a/src/cobalt/input/input_device_manager_desktop.cc b/src/cobalt/input/input_device_manager_desktop.cc
index 9c2e9e7..85ccbb1 100644
--- a/src/cobalt/input/input_device_manager_desktop.cc
+++ b/src/cobalt/input/input_device_manager_desktop.cc
@@ -52,17 +52,21 @@
     const KeyboardEventCallback& keyboard_event_callback,
     const PointerEventCallback& pointer_event_callback,
     const WheelEventCallback& wheel_event_callback,
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
     const InputEventCallback& input_event_callback,
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
     system_window::SystemWindow* system_window)
     : system_window_(system_window),
       system_window_input_event_callback_(
           base::Bind(&InputDeviceManagerDesktop::HandleSystemWindowInputEvent,
                      base::Unretained(this))),
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
       input_event_callback_(input_event_callback),
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
       keypress_generator_filter_(keyboard_event_callback),
       pointer_event_callback_(pointer_event_callback),
       wheel_event_callback_(wheel_event_callback) {
@@ -297,7 +301,8 @@
   wheel_event_callback_.Run(type, wheel_event);
 }
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
 void InputDeviceManagerDesktop::HandleInputEvent(
     const system_window::InputEvent* event) {
   // Note: we currently treat all dom::InputEvents as input (never beforeinput).
@@ -309,7 +314,8 @@
   input_event.set_is_composing(event->is_composing());
   input_event_callback_.Run(type, input_event);
 }
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
 void InputDeviceManagerDesktop::HandleSystemWindowInputEvent(
     const base::Event* event) {
@@ -369,10 +375,12 @@
       HandleWheelEvent(input_event);
       break;
     case system_window::InputEvent::kInput:
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
       HandleInputEvent(input_event);
       break;
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
     case system_window::InputEvent::kKeyMove:
       break;
   }
diff --git a/src/cobalt/input/input_device_manager_desktop.h b/src/cobalt/input/input_device_manager_desktop.h
index d52ac4a..c72ec67 100644
--- a/src/cobalt/input/input_device_manager_desktop.h
+++ b/src/cobalt/input/input_device_manager_desktop.h
@@ -28,9 +28,11 @@
       const KeyboardEventCallback& keyboard_event_callback,
       const PointerEventCallback& pointer_event_callback,
       const WheelEventCallback& wheel_event_callback,
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
       const InputEventCallback& input_event_callback,
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
       system_window::SystemWindow* system_window);
 
   ~InputDeviceManagerDesktop() override;
@@ -44,9 +46,11 @@
   void HandleKeyboardEvent(bool is_key_down,
                            const system_window::InputEvent* input_event,
                            int key_code);
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   void HandleInputEvent(const system_window::InputEvent* event);
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
   void HandlePointerEvent(base::Token type,
                           const system_window::InputEvent* input_event);
@@ -61,10 +65,12 @@
   // object is destroyed.
   base::EventCallback system_window_input_event_callback_;
 
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
   // Called to handle an input_event.
   InputEventCallback input_event_callback_;
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
 
   // Keyboard event filters to process the events generated.
   KeypressGeneratorFilter keypress_generator_filter_;
diff --git a/src/cobalt/input/input_device_manager_starboard.cc b/src/cobalt/input/input_device_manager_starboard.cc
index cbc9c47..5827d99 100644
--- a/src/cobalt/input/input_device_manager_starboard.cc
+++ b/src/cobalt/input/input_device_manager_starboard.cc
@@ -24,15 +24,19 @@
     const KeyboardEventCallback& keyboard_event_callback,
     const PointerEventCallback& pointer_event_callback,
     const WheelEventCallback& wheel_event_callback,
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
     const InputEventCallback& input_event_callback,
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
     system_window::SystemWindow* system_window) {
   return std::unique_ptr<InputDeviceManager>(new InputDeviceManagerDesktop(
       keyboard_event_callback, pointer_event_callback, wheel_event_callback,
-#if SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION || \
+    SB_HAS(ON_SCREEN_KEYBOARD)
       input_event_callback,
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#endif  // SB_API_VERSION >= SB_ON_SCREEN_KEYBOARD_REQUIRED_VERSION ||
+        // SB_HAS(ON_SCREEN_KEYBOARD)
       system_window));
 }
 
diff --git a/src/cobalt/layout/anonymous_block_box.cc b/src/cobalt/layout/anonymous_block_box.cc
index 55dd91e..709eda9 100644
--- a/src/cobalt/layout/anonymous_block_box.cc
+++ b/src/cobalt/layout/anonymous_block_box.cc
@@ -40,6 +40,19 @@
           css_computed_style_declaration->data()->font_weight())) {}
 
 Box::Level AnonymousBlockBox::GetLevel() const { return kBlockLevel; }
+Box::MarginCollapsingStatus AnonymousBlockBox::GetMarginCollapsingStatus()
+    const {
+  // If all enclosed boxes are absolutely-positioned, ignore it for
+  // margin-collapse.
+  if (std::all_of(child_boxes().begin(), child_boxes().end(),
+                  [](Box* b) { return b->IsAbsolutelyPositioned(); })) {
+    return kIgnore;
+  }
+
+  // If any enclosed block is inline-level, break collapsing model for
+  // parent/siblings.
+  return kSeparateAdjoiningMargins;
+}
 
 AnonymousBlockBox* AnonymousBlockBox::AsAnonymousBlockBox() { return this; }
 const AnonymousBlockBox* AnonymousBlockBox::AsAnonymousBlockBox() const {
diff --git a/src/cobalt/layout/anonymous_block_box.h b/src/cobalt/layout/anonymous_block_box.h
index c6787cd..a5a6b69 100644
--- a/src/cobalt/layout/anonymous_block_box.h
+++ b/src/cobalt/layout/anonymous_block_box.h
@@ -40,6 +40,7 @@
 
   // From |Box|.
   Level GetLevel() const override;
+  MarginCollapsingStatus GetMarginCollapsingStatus() const override;
   AnonymousBlockBox* AsAnonymousBlockBox() override;
   const AnonymousBlockBox* AsAnonymousBlockBox() const override;
 
diff --git a/src/cobalt/layout/block_container_box.cc b/src/cobalt/layout/block_container_box.cc
index 006baff..b5f1fbf 100644
--- a/src/cobalt/layout/block_container_box.cc
+++ b/src/cobalt/layout/block_container_box.cc
@@ -118,6 +118,10 @@
       child_layout_params.containing_block_size.set_height(LayoutUnit());
     }
   }
+  child_layout_params.maybe_margin_top = maybe_margin_top;
+  child_layout_params.maybe_margin_bottom = maybe_margin_bottom;
+  child_layout_params.maybe_height = maybe_height;
+
   std::unique_ptr<FormattingContext> formatting_context =
       UpdateRectOfInFlowChildBoxes(child_layout_params);
 
@@ -676,9 +680,20 @@
     const base::Optional<LayoutUnit>& maybe_margin_top,
     const base::Optional<LayoutUnit>& maybe_margin_bottom,
     const FormattingContext& formatting_context) {
-  // If "margin-top", or "margin-bottom" are "auto", their used value is 0.
-  set_margin_top(maybe_margin_top.value_or(LayoutUnit()));
-  set_margin_bottom(maybe_margin_bottom.value_or(LayoutUnit()));
+  if (collapsed_empty_margin_) {
+    // If empty box has a collapsed margin, only set top margin.
+    //   https://www.w3.org/TR/CSS22/box.html#collapsing-margins
+    set_margin_top(collapsed_empty_margin_.value());
+    set_margin_bottom(LayoutUnit());
+  } else {
+    // If "margin-top", or "margin-bottom" are "auto", their used value is 0.
+    LayoutUnit margin_top =
+        collapsed_margin_top_.value_or(maybe_margin_top.value_or(LayoutUnit()));
+    LayoutUnit margin_bottom = collapsed_margin_bottom_.value_or(
+        maybe_margin_bottom.value_or(LayoutUnit()));
+    set_margin_top(margin_top);
+    set_margin_bottom(margin_bottom);
+  }
 
   // If "height" is "auto", the used value is the distance from box's top
   // content edge to the first applicable of the following:
diff --git a/src/cobalt/layout/block_formatting_block_container_box.cc b/src/cobalt/layout/block_formatting_block_container_box.cc
index e85367d..a05e390 100644
--- a/src/cobalt/layout/block_formatting_block_container_box.cc
+++ b/src/cobalt/layout/block_formatting_block_container_box.cc
@@ -18,6 +18,7 @@
 #include <memory>
 
 #include "cobalt/cssom/computed_style.h"
+#include "cobalt/cssom/computed_style_utils.h"
 #include "cobalt/cssom/keyword_value.h"
 #include "cobalt/layout/anonymous_block_box.h"
 #include "cobalt/layout/block_formatting_context.h"
@@ -62,19 +63,33 @@
 std::unique_ptr<FormattingContext>
 BlockFormattingBlockContainerBox::UpdateRectOfInFlowChildBoxes(
     const LayoutParams& child_layout_params) {
+  // Only collapse in-flow, block-level boxes. Do not collapse root element and
+  // the initial containing block. Do not collapse boxes with overflow not equal
+  // to 'visible', because these create new formatting contexts.
+  bool is_collapsable =
+      !IsAbsolutelyPositioned() && GetLevel() == Box::kBlockLevel && parent() &&
+      parent()->parent() && !IsOverflowCropped(computed_style());
+
+  // Margins should only collapse if no padding or border separate them.
+  //   https://www.w3.org/TR/CSS22/box.html#collapsing-margins
+  bool top_margin_is_collapsable = is_collapsable &&
+                                   padding_top() == LayoutUnit() &&
+                                   border_top_width() == LayoutUnit();
   // Lay out child boxes in the normal flow.
   //   https://www.w3.org/TR/CSS21/visuren.html#normal-flow
   std::unique_ptr<BlockFormattingContext> block_formatting_context(
-      new BlockFormattingContext(child_layout_params));
+      new BlockFormattingContext(child_layout_params,
+                                 top_margin_is_collapsable));
   for (Boxes::const_iterator child_box_iterator = child_boxes().begin();
        child_box_iterator != child_boxes().end(); ++child_box_iterator) {
     Box* child_box = *child_box_iterator;
-    if (child_box->IsAbsolutelyPositioned()) {
-      block_formatting_context->EstimateStaticPosition(child_box);
-    } else {
-      block_formatting_context->UpdateRect(child_box);
-    }
+    block_formatting_context->UpdateRect(child_box);
   }
+
+  if (is_collapsable) {
+    block_formatting_context->CollapseContainingMargins(this);
+  }
+
   return std::unique_ptr<FormattingContext>(block_formatting_context.release());
 }
 
diff --git a/src/cobalt/layout/block_formatting_context.cc b/src/cobalt/layout/block_formatting_context.cc
index 8ecb79c..33b8510 100644
--- a/src/cobalt/layout/block_formatting_context.cc
+++ b/src/cobalt/layout/block_formatting_context.cc
@@ -23,8 +23,10 @@
 namespace layout {
 
 BlockFormattingContext::BlockFormattingContext(
-    const LayoutParams& layout_params)
-    : layout_params_(layout_params), collapsing_margin_(0) {}
+    const LayoutParams& layout_params, const bool is_margin_collapsable)
+    : layout_params_(layout_params),
+      margin_collapsing_params_(MarginCollapsingParams(is_margin_collapsable)) {
+}
 
 BlockFormattingContext::~BlockFormattingContext() {}
 
@@ -32,6 +34,11 @@
   DCHECK(!child_box->IsAbsolutelyPositioned());
 
   child_box->UpdateSize(layout_params_);
+
+  // In a block formatting context, each box's left outer edge touches
+  // the left edge of the containing block.
+  //   https://www.w3.org/TR/CSS21/visuren.html#block-formatting
+  child_box->set_left(LayoutUnit());
   UpdatePosition(child_box);
 
   // Shrink-to-fit width cannot be less than the width of the widest child.
@@ -45,7 +52,6 @@
   // child.
   //   https://www.w3.org/TR/CSS21/visudet.html#normal-block
   set_auto_height(child_box->GetMarginBoxBottomEdgeOffsetFromContainingBlock());
-  collapsing_margin_ = child_box->margin_bottom();
 
   // The baseline of an "inline-block" is the baseline of its last line box
   // in the normal flow, unless it has no in-flow line boxes.
@@ -56,23 +62,108 @@
   }
 }
 
-void BlockFormattingContext::EstimateStaticPosition(Box* child_box) {
-  DCHECK(child_box->IsAbsolutelyPositioned());
-
-  // The term "static position" (of an element) refers, roughly, to the position
-  // an element would have had in the normal flow.
-  //   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
-  UpdatePosition(child_box);
-}
-
 void BlockFormattingContext::UpdatePosition(Box* child_box) {
   DCHECK_EQ(Box::kBlockLevel, child_box->GetLevel());
 
-  // In a block formatting context, each box's left outer edge touches
-  // the left edge of the containing block.
-  //   https://www.w3.org/TR/CSS21/visuren.html#block-formatting
-  child_box->set_left(LayoutUnit());
+  switch (child_box->GetMarginCollapsingStatus()) {
+    case Box::kIgnore:
+      child_box->set_top(auto_height());
+      break;
+    case Box::kSeparateAdjoiningMargins:
+      child_box->set_top(auto_height());
+      margin_collapsing_params_.collapsing_margin = LayoutUnit();
+      margin_collapsing_params_.should_collapse_margin_bottom = false;
+      if (!margin_collapsing_params_.context_margin_top) {
+        margin_collapsing_params_.should_collapse_margin_top = false;
+      }
+      margin_collapsing_params_.should_collapse_own_margins_together = false;
+      break;
+    case Box::kCollapseMargins:
+      margin_collapsing_params_.should_collapse_margin_bottom = true;
 
+      // For first child, if top margin will collapse with parent's top margin,
+      // parent will handle margin positioning for both itself and the child.
+      if (!margin_collapsing_params_.context_margin_top &&
+          margin_collapsing_params_.should_collapse_margin_top) {
+        child_box->set_top(auto_height() - child_box->margin_top());
+        if (child_box->collapsed_empty_margin_) {
+          margin_collapsing_params_.should_collapse_margin_bottom = false;
+        }
+      } else {
+        // Collapse top margin with previous sibling's bottom margin.
+        LayoutUnit collapsed_margin =
+            CollapseMargins(child_box->margin_top(),
+                            margin_collapsing_params_.collapsing_margin);
+        LayoutUnit combined_margin =
+            margin_collapsing_params_.collapsing_margin +
+            child_box->margin_top();
+        LayoutUnit position_difference = combined_margin - collapsed_margin;
+        child_box->set_top(auto_height() - position_difference);
+      }
+
+      // Collapse margins for in-flow siblings.
+      margin_collapsing_params_.collapsing_margin =
+          child_box->collapsed_empty_margin_.value_or(
+              child_box->margin_bottom());
+      if (!margin_collapsing_params_.context_margin_top) {
+        margin_collapsing_params_.context_margin_top = child_box->margin_top();
+      }
+      break;
+  }
+}
+
+void BlockFormattingContext::CollapseContainingMargins(Box* containing_box) {
+  bool has_padding_top = containing_box->padding_top() != LayoutUnit();
+  bool has_border_top = containing_box->border_top_width() != LayoutUnit();
+  bool has_padding_bottom = containing_box->padding_bottom() != LayoutUnit();
+  bool has_border_bottom =
+      containing_box->border_bottom_width() != LayoutUnit();
+  LayoutUnit margin_top =
+      layout_params_.maybe_margin_top.value_or(LayoutUnit());
+  LayoutUnit margin_bottom =
+      layout_params_.maybe_margin_bottom.value_or(LayoutUnit());
+
+  // If no in-flow children, do not collapse margins with children.
+  if (!margin_collapsing_params_.context_margin_top) {
+    // Empty boxes with auto or 0 height collapse top/bottom margins together.
+    //   https://www.w3.org/TR/CSS22/box.html#collapsing-margins
+    if (!has_padding_top && !has_border_top && !has_padding_bottom &&
+        !has_border_bottom &&
+        layout_params_.containing_block_size.height() == LayoutUnit() &&
+        margin_collapsing_params_.should_collapse_own_margins_together) {
+      containing_box->collapsed_empty_margin_ =
+          CollapseMargins(margin_top, margin_bottom);
+      return;
+    }
+    // Reset in case min-height iteration reverses 0/auto height criteria.
+    containing_box->collapsed_empty_margin_.reset();
+    return;
+  }
+
+  // Collapse top margin with top margin of first in-flow child.
+  if (!has_padding_top && !has_border_top &&
+      margin_collapsing_params_.should_collapse_margin_top) {
+    LayoutUnit collapsed_margin_top = CollapseMargins(
+        margin_top,
+        margin_collapsing_params_.context_margin_top.value_or(LayoutUnit()));
+    containing_box->collapsed_margin_top_ = collapsed_margin_top;
+  }
+
+  // If height is auto, collapse bottom margin with bottom margin of last
+  // in-flow child.
+  if (!layout_params_.maybe_height && !has_padding_bottom &&
+      !has_border_bottom &&
+      margin_collapsing_params_.should_collapse_margin_bottom) {
+    LayoutUnit collapsed_margin_bottom = CollapseMargins(
+        margin_bottom, margin_collapsing_params_.collapsing_margin);
+    containing_box->collapsed_margin_bottom_ = collapsed_margin_bottom;
+    set_auto_height(auto_height() -
+                    margin_collapsing_params_.collapsing_margin);
+  }
+}
+
+LayoutUnit BlockFormattingContext::CollapseMargins(
+    const LayoutUnit box_margin, const LayoutUnit adjoining_margin) {
   // In a block formatting context, boxes are laid out one after the other,
   // vertically, beginning at the top of a containing block. The vertical
   // distance between two sibling boxes is determined by the "margin"
@@ -83,28 +174,25 @@
   // When two or more margins collapse, the resulting margin width is the
   // maximum of the collapsing margins' widths.
   //   https://www.w3.org/TR/CSS21/box.html#collapsing-margins
-  const LayoutUnit margin_top = child_box->margin_top();
   LayoutUnit collapsed_margin;
-  if ((margin_top >= LayoutUnit()) && (collapsing_margin_ >= LayoutUnit())) {
-    collapsed_margin = std::max(margin_top, collapsing_margin_);
-  } else if ((margin_top < LayoutUnit()) &&
-             (collapsing_margin_ < LayoutUnit())) {
+  if ((box_margin >= LayoutUnit()) && (adjoining_margin >= LayoutUnit())) {
+    collapsed_margin = std::max(box_margin, adjoining_margin);
+  } else if ((box_margin < LayoutUnit()) && (adjoining_margin < LayoutUnit())) {
     // If there are no positive margins, the maximum of the absolute values of
     // the adjoining margins is deducted from zero.
-    collapsed_margin = LayoutUnit() + std::min(margin_top, collapsing_margin_);
+    collapsed_margin = LayoutUnit() + std::min(box_margin, adjoining_margin);
   } else {
     // In the case of negative margins, the maximum of the absolute values of
     // the negative adjoining margins is deducted from the maximum of the
     // positive adjoining margins.
     // When there is only one negative and one positive margin, that translates
     // to: The margins are summed.
-    DCHECK(collapsing_margin_.GreaterEqualOrNaN(LayoutUnit()) ||
-           margin_top.GreaterEqualOrNaN(LayoutUnit()));
-    collapsed_margin = collapsing_margin_ + margin_top;
+    DCHECK(adjoining_margin.GreaterEqualOrNaN(LayoutUnit()) ||
+           box_margin.GreaterEqualOrNaN(LayoutUnit()));
+    collapsed_margin = adjoining_margin + box_margin;
   }
 
-  LayoutUnit combined_margin = collapsing_margin_ + margin_top;
-  child_box->set_top(auto_height() - combined_margin + collapsed_margin);
+  return collapsed_margin;
 }
 
 }  // namespace layout
diff --git a/src/cobalt/layout/block_formatting_context.h b/src/cobalt/layout/block_formatting_context.h
index 8fee091..2096453 100644
--- a/src/cobalt/layout/block_formatting_context.h
+++ b/src/cobalt/layout/block_formatting_context.h
@@ -24,6 +24,20 @@
 namespace cobalt {
 namespace layout {
 
+struct MarginCollapsingParams {
+  MarginCollapsingParams(const bool is_margin_collapsable)
+      : collapsing_margin(0),
+        should_collapse_own_margins_together(true),
+        should_collapse_margin_bottom(true),
+        should_collapse_margin_top(is_margin_collapsable) {}
+
+  LayoutUnit collapsing_margin;
+  base::Optional<LayoutUnit> context_margin_top;
+  bool should_collapse_own_margins_together;
+  bool should_collapse_margin_bottom;
+  bool should_collapse_margin_top;
+};
+
 // In a block formatting context, boxes are laid out one after the other,
 // vertically, beginning at the top of a containing block.
 //   https://www.w3.org/TR/CSS21/visuren.html#block-formatting
@@ -35,22 +49,24 @@
 // to update the position of the subsequent children passed to it.
 class BlockFormattingContext : public FormattingContext {
  public:
-  explicit BlockFormattingContext(const LayoutParams& layout_params);
+  explicit BlockFormattingContext(const LayoutParams& layout_params,
+                                  const bool is_margin_collapsable);
   ~BlockFormattingContext() override;
 
+  // Updates the top and bottom margins of the containing box after children
+  // have been processed.
+  void CollapseContainingMargins(Box* containing_box);
+
   // Calculates the position and size of the given child box and updates
   // the internal state in the preparation for the next child.
   void UpdateRect(Box* child_box);
 
-  // Estimates the static position of the given child box. In CSS 2.1 the static
-  // position is only defined for absolutely positioned boxes.
-  void EstimateStaticPosition(Box* child_box);
-
  private:
   void UpdatePosition(Box* child_box);
-
+  LayoutUnit CollapseMargins(const LayoutUnit box_margin,
+                             const LayoutUnit adjoining_margin);
   const LayoutParams layout_params_;
-  LayoutUnit collapsing_margin_;
+  MarginCollapsingParams margin_collapsing_params_;
 
   DISALLOW_COPY_AND_ASSIGN(BlockFormattingContext);
 };
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 3d661e2..8195c59 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -16,7 +16,7 @@
 #define COBALT_LAYOUT_BOX_H_
 
 #include <iosfwd>
-#include <iostream>
+#include <ostream>
 #include <string>
 #include <vector>
 
@@ -97,6 +97,10 @@
            freeze_height == rhs.freeze_height &&
            containing_block_size == rhs.containing_block_size;
   }
+
+  base::Optional<LayoutUnit> maybe_margin_top;
+  base::Optional<LayoutUnit> maybe_margin_bottom;
+  base::Optional<LayoutUnit> maybe_height;
 };
 
 inline std::ostream& operator<<(std::ostream& stream,
@@ -134,6 +138,12 @@
     kInlineLevel,
   };
 
+  enum MarginCollapsingStatus {
+    kCollapseMargins,
+    kIgnore,
+    kSeparateAdjoiningMargins,
+  };
+
   enum RelationshipToBox {
     kIsBoxAncestor,
     kIsBox,
@@ -215,6 +225,10 @@
   // Do not confuse with the formatting context that the element may establish.
   virtual Level GetLevel() const = 0;
 
+  virtual MarginCollapsingStatus GetMarginCollapsingStatus() const {
+    return Box::kCollapseMargins;
+  }
+
   // Returns true if the box is positioned (e.g. position is non-static or
   // transform is not None).  Intuitively, this is true if the element does
   // not follow standard layout flow rules for determining its position.
@@ -319,6 +333,21 @@
   LayoutUnit GetMarginBoxWidth() const;
   LayoutUnit GetMarginBoxHeight() const;
 
+  // Used values of "margin" properties are set by overriders
+  // of |UpdateContentSizeAndMargins| method.
+  void set_margin_left(LayoutUnit margin_left) {
+    margin_insets_.set_left(margin_left);
+  }
+  void set_margin_top(LayoutUnit margin_top) {
+    margin_insets_.set_top(margin_top);
+  }
+  void set_margin_right(LayoutUnit margin_right) {
+    margin_insets_.set_right(margin_right);
+  }
+  void set_margin_bottom(LayoutUnit margin_bottom) {
+    margin_insets_.set_bottom(margin_bottom);
+  }
+
   math::Matrix3F GetMarginBoxTransformFromContainingBlock(
       const ContainerBox* containing_block) const;
 
@@ -335,6 +364,11 @@
       BaseDirection base_direction) const;
 
   // Border box.
+  LayoutUnit border_left_width() const { return border_insets_.left(); }
+  LayoutUnit border_top_width() const { return border_insets_.top(); }
+  LayoutUnit border_right_width() const { return border_insets_.right(); }
+  LayoutUnit border_bottom_width() const { return border_insets_.bottom(); }
+
   RectLayoutUnit GetBorderBoxFromRoot(bool transform_forms_root) const;
 
   LayoutUnit GetBorderBoxWidth() const;
@@ -347,6 +381,10 @@
   Vector2dLayoutUnit GetBorderBoxOffsetFromMarginBox() const;
 
   // Padding box.
+  LayoutUnit padding_left() const { return padding_insets_.left(); }
+  LayoutUnit padding_top() const { return padding_insets_.top(); }
+  LayoutUnit padding_right() const { return padding_insets_.right(); }
+  LayoutUnit padding_bottom() const { return padding_insets_.bottom(); }
   LayoutUnit GetPaddingBoxWidth() const;
   LayoutUnit GetPaddingBoxHeight() const;
   SizeLayoutUnit GetClampedPaddingBoxSize() const;
@@ -657,6 +695,10 @@
       const scoped_refptr<IntersectionObserverRoot>& intersection_observer_root)
       const;
 
+  base::Optional<LayoutUnit> collapsed_margin_top_;
+  base::Optional<LayoutUnit> collapsed_margin_bottom_;
+  base::Optional<LayoutUnit> collapsed_empty_margin_;
+
  protected:
   UsedStyleProvider* used_style_provider() const {
     return used_style_provider_;
@@ -674,35 +716,6 @@
   virtual void UpdateContentSizeAndMargins(
       const LayoutParams& layout_params) = 0;
 
-  // Margin box accessors.
-  //
-  // Used values of "margin" properties are set by overriders
-  // of |UpdateContentSizeAndMargins| method.
-  void set_margin_left(LayoutUnit margin_left) {
-    margin_insets_.set_left(margin_left);
-  }
-  void set_margin_top(LayoutUnit margin_top) {
-    margin_insets_.set_top(margin_top);
-  }
-  void set_margin_right(LayoutUnit margin_right) {
-    margin_insets_.set_right(margin_right);
-  }
-  void set_margin_bottom(LayoutUnit margin_bottom) {
-    margin_insets_.set_bottom(margin_bottom);
-  }
-
-  // Border box read-only accessors.
-  LayoutUnit border_left_width() const { return border_insets_.left(); }
-  LayoutUnit border_top_width() const { return border_insets_.top(); }
-  LayoutUnit border_right_width() const { return border_insets_.right(); }
-  LayoutUnit border_bottom_width() const { return border_insets_.bottom(); }
-
-  // Padding box read-only accessors.
-  LayoutUnit padding_left() const { return padding_insets_.left(); }
-  LayoutUnit padding_top() const { return padding_insets_.top(); }
-  LayoutUnit padding_right() const { return padding_insets_.right(); }
-  LayoutUnit padding_bottom() const { return padding_insets_.bottom(); }
-
   // Content box setters.
   //
   // Used values of "width" and "height" properties are set by overriders
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index b9fe273..b286b23 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -406,6 +406,8 @@
 
 namespace {
 
+typedef dom::HTMLElement::DirState DirState;
+
 class ContainerBoxGenerator : public cssom::NotReachedPropertyValueVisitor {
  public:
   enum CloseParagraph {
@@ -413,12 +415,12 @@
     kCloseParagraph,
   };
 
-  ContainerBoxGenerator(dom::Directionality directionality,
+  ContainerBoxGenerator(DirState element_dir,
                         const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
                             css_computed_style_declaration,
                         scoped_refptr<Paragraph>* paragraph,
                         const BoxGenerator::Context* context)
-      : directionality_(directionality),
+      : element_dir_(element_dir),
         css_computed_style_declaration_(css_computed_style_declaration),
         context_(context),
         has_scoped_directional_embedding_(false),
@@ -433,7 +435,7 @@
  private:
   void CreateScopedParagraph(CloseParagraph close_prior_paragraph);
 
-  const dom::Directionality directionality_;
+  const DirState element_dir_;
   const scoped_refptr<cssom::CSSComputedStyleDeclaration>
       css_computed_style_declaration_;
   const BoxGenerator::Context* context_;
@@ -532,10 +534,11 @@
       // paragraph, when the ContainerBoxGenerator goes out of scope.
       // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
       // http://unicode.org/reports/tr9/#Explicit_Directional_Embeddings
-      if (directionality_ == dom::kLeftToRightDirectionality) {
+      // http://unicode.org/reports/tr9/#Markup_And_Formatting
+      if (element_dir_ == DirState::kDirLeftToRight) {
         has_scoped_directional_embedding_ = true;
         (*paragraph_)->AppendCodePoint(Paragraph::kLeftToRightEmbedCodePoint);
-      } else if (directionality_ == dom::kRightToLeftDirectionality) {
+      } else if (element_dir_ == DirState::kDirRightToLeft) {
         has_scoped_directional_embedding_ = true;
         (*paragraph_)->AppendCodePoint(Paragraph::kRightToLeftEmbedCodePoint);
       }
@@ -675,9 +678,9 @@
   // it is inherited from the parent element.
   // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
   BaseDirection base_direction;
-  if (directionality_ == dom::kLeftToRightDirectionality) {
+  if (element_dir_ == DirState::kDirLeftToRight) {
     base_direction = kLeftToRightBaseDirection;
-  } else if (directionality_ == dom::kRightToLeftDirectionality) {
+  } else if (element_dir_ == DirState::kDirRightToLeft) {
     base_direction = kRightToLeftBaseDirection;
   } else {
     base_direction = prior_paragraph_->GetDirectionalEmbeddingStackDirection();
@@ -880,7 +883,7 @@
   pseudo_element->reset_layout_boxes();
 
   ContainerBoxGenerator pseudo_element_box_generator(
-      dom::kNoExplicitDirectionality,
+      DirState::kDirNotDefined,
       pseudo_element->css_computed_style_declaration(), paragraph_, context_);
   pseudo_element->computed_style()->display()->Accept(
       &pseudo_element_box_generator);
@@ -966,7 +969,7 @@
       html_element->css_computed_style_declaration());
 
   ContainerBoxGenerator container_box_generator(
-      html_element->directionality(),
+      html_element->dir_state(),
       html_element == context_->ignore_background_element
           ? StripBackground(element_style)
           : element_style,
diff --git a/src/cobalt/layout/flex_container_box.cc b/src/cobalt/layout/flex_container_box.cc
index bacd234..8bb4127 100644
--- a/src/cobalt/layout/flex_container_box.cc
+++ b/src/cobalt/layout/flex_container_box.cc
@@ -311,8 +311,11 @@
   set_margin_right(maybe_margin_right.value_or(LayoutUnit()));
   set_margin_top(maybe_margin_top.value_or(LayoutUnit()));
   set_margin_bottom(maybe_margin_bottom.value_or(LayoutUnit()));
-  if (child_boxes().empty()) {
-    baseline_ = GetBorderBoxHeight();
+
+  UpdateRectOfPositionedChildBoxes(child_layout_params, layout_params);
+
+  if (items.empty()) {
+    baseline_ = GetPaddingBoxHeight() + border_bottom_width() + margin_bottom();
   } else {
     baseline_ = flex_formatting_context.GetBaseline();
   }
@@ -480,8 +483,7 @@
 }
 
 AnonymousBlockBox* FlexContainerBox::GetLastChildAsAnonymousBlockBox() {
-  return child_boxes().empty() ? NULL
-                               : child_boxes().back()->AsAnonymousBlockBox();
+  return NULL;
 }
 
 AnonymousBlockBox* FlexContainerBox::GetOrAddAnonymousBlockBox() {
diff --git a/src/cobalt/layout/flex_container_box.h b/src/cobalt/layout/flex_container_box.h
index a962d44..5e55ce2 100644
--- a/src/cobalt/layout/flex_container_box.h
+++ b/src/cobalt/layout/flex_container_box.h
@@ -15,6 +15,9 @@
 #ifndef COBALT_LAYOUT_FLEX_CONTAINER_BOX_H_
 #define COBALT_LAYOUT_FLEX_CONTAINER_BOX_H_
 
+#include <memory>
+
+#include "base/optional.h"
 #include "cobalt/cssom/css_computed_style_declaration.h"
 #include "cobalt/layout/base_direction.h"
 #include "cobalt/layout/block_container_box.h"
diff --git a/src/cobalt/layout/flex_line.cc b/src/cobalt/layout/flex_line.cc
index 124099a..a55819f 100644
--- a/src/cobalt/layout/flex_line.cc
+++ b/src/cobalt/layout/flex_line.cc
@@ -358,7 +358,7 @@
   // If the remaining free space is positive and at least one main-axis margin
   // on this line is auto, distribute the free space equally among these
   // margins.
-  std::vector<bool> auto_margins(items_.size());
+  std::vector<bool> auto_margins(items_.size() * 2);
   int auto_margin_count = 0;
   int margin_idx = 0;
   for (auto& item : items_) {
diff --git a/src/cobalt/layout/layout_boxes.cc b/src/cobalt/layout/layout_boxes.cc
index df06332..e387472 100644
--- a/src/cobalt/layout/layout_boxes.cc
+++ b/src/cobalt/layout/layout_boxes.cc
@@ -211,7 +211,6 @@
   float top = padding_area.y();
   float bottom = scroll_area.bottom();
   switch (dir) {
-    case dom::kNoExplicitDirectionality:
     case dom::kLeftToRightDirectionality:
       left = padding_area.x();
       break;
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index 3d2a447..ab107f5 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -366,8 +366,8 @@
   if (IsAbsolutelyPositioned()) {
     // TODO: Implement CSS section 10.3.8, see
     // https://www.w3.org/TR/CSS21/visudet.html#abs-replaced-width.
-    set_left(maybe_left.value_or(LayoutUnit()));
-    set_top(maybe_top.value_or(LayoutUnit()));
+    set_left(maybe_left.value_or(LayoutUnit(GetStaticPositionLeft())));
+    set_top(maybe_top.value_or(LayoutUnit(GetStaticPositionTop())));
   }
   // Note that computed height may be "auto", even if it is specified as a
   // percentage (depending on conditions of the containing block). See details
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child-expected.png
new file mode 100644
index 0000000..fba304f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child.html
new file mode 100644
index 0000000..35ccd52
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child.html
@@ -0,0 +1,31 @@
+<html>
+  <style>
+    div {
+      width: 50px;
+      height: 50px;
+    }
+    #blue {
+      background-color: blue;
+      margin-top: 50px;
+      overflow: hidden;
+      top: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      height: 60px;
+      margin-top: 30px;
+    }
+    #green {
+      background-color:  green;
+      height: 70px;
+      margin-top: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+      <div id=blue>
+	<div id=yellow></div>
+       </div>
+     </div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..3b77c41
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..2200a60
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin.html
@@ -0,0 +1,25 @@
+<html>
+  <style>
+    #green {
+      margin-top: 50px;
+      background-color: green;
+      margin-bottom: 60px;
+    }
+    #blue {
+      background-color: blue;
+      width: 50px;
+      height: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=blue></div>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..99cfaad
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..26bb5fa
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin.html
@@ -0,0 +1,20 @@
+<html>
+  <style>
+    #green {
+      margin-top: 50px;
+      background-color: green;
+      margin-bottom: 60px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<span>Hello World</span>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..b8e0a0c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..ae3204e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin.html
@@ -0,0 +1,29 @@
+<html>
+  <style>
+    #green {
+      margin-top: 50px;
+      background-color: green;
+      margin-bottom: 60px;
+      min-height: 20px;
+    }
+    #blue {
+      background-color: blue;
+      margin-top: 50px;
+      position: absolute;
+      top: 0;
+      width: 50px;
+      height: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=blue></div>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..efdf5a0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin.html
new file mode 100644
index 0000000..6a9767d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin.html
@@ -0,0 +1,28 @@
+<html>
+  <style>
+    #green {
+      margin-top: 50px;
+      background-color: green;
+      margin-bottom: 60px;
+    }
+    #blue {
+      background-color: blue;
+      margin-top: 50px;
+      position: absolute;
+      top: 0;
+      width: 50px;
+      height: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=blue></div>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin-expected.png
new file mode 100644
index 0000000..174669e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin.html
new file mode 100644
index 0000000..e9344e8
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin.html
@@ -0,0 +1,27 @@
+<html>
+  <style>
+    #green {
+      background-color: green;
+    }
+    #blue {
+      background-color: blue;
+      width: 50px;
+      height: 50px;
+    }
+    #red {
+      margin-top: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+       <div id=blue></div>
+    	<div id=red></div>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin-expected.png
new file mode 100644
index 0000000..cfa5749
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin.html
new file mode 100644
index 0000000..b7a8771
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin.html
@@ -0,0 +1,24 @@
+<html>
+  <style>
+    #green {
+      background-color: green;
+      min-height: 50px;
+      margin-bottom: 20px;
+      margin-top: 20px;
+    }
+    #red {
+      margin-bottom: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      height: 50px;
+      width: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=red></div>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..a73ea6d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin.html
new file mode 100644
index 0000000..ce51aee
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin.html
@@ -0,0 +1,28 @@
+<html>
+  <style>
+    div {
+      width: 50px;
+      height: 50px;
+    }
+    #green {
+      margin-top: 20px;
+      background-color: green;
+      margin-bottom: 20px;
+    }
+    #blue {
+      background-color: blue;
+      margin-top: 40px;
+      position: absolute;
+      top: 30px;
+    }
+    #yellow {
+      background-color: yellow;
+      margin-top: 50px;
+    }
+  </style>
+  <body>
+    <div id=green></div>
+    <div id=blue></div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..312a572
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..bd26cfe
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin.html
@@ -0,0 +1,27 @@
+<html>
+  <style>
+    body {
+      font-family: Roboto;
+      font-size: 50px;
+      font-weight: bold;
+      color: black;
+    }
+    div {
+      width: 50px;
+      height: 50px;
+    }
+    #green {
+      background-color: green;
+      margin-bottom: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      margin-top: 50px;
+    }
+  </style>
+  <body>
+    <div id=green></div>
+    <span>Hello world</span>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-should-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-should-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..65743d9
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-should-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-in-flow-siblings-should-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-should-form-collapsed-margin.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-in-flow-siblings-should-form-collapsed-margin.html
rename to src/cobalt/layout_tests/testdata/css-2-1/8-3-1-in-flow-siblings-should-form-collapsed-margin.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-inline-level-boxes-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-inline-level-boxes-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..f9cdb25
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-inline-level-boxes-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-inline-level-boxes-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-inline-level-boxes-should-not-form-collapsed-margin.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-inline-level-boxes-should-not-form-collapsed-margin.html
rename to src/cobalt/layout_tests/testdata/css-2-1/8-3-1-inline-level-boxes-should-not-form-collapsed-margin.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..5bc9961
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html
new file mode 100644
index 0000000..403127d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html
@@ -0,0 +1,28 @@
+<html>
+  <style>
+    div {
+      width: 50px;
+      height: 50px;
+    }
+    #red {
+      background-color: red;
+      margin-top: 50px;
+    }
+    #blue {
+      background-color: blue;
+      margin-top: 60px;
+      position: absolute;
+      top: 40px;
+    }
+    #yellow {
+      background-color: yellow;
+      margin-top: 50px;
+    }
+  </style>
+  <body>
+    <div id=red>
+    	<div id=blue></div>
+    	<div id=yellow></div>
+    </div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..2994923
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..648dac1
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html
@@ -0,0 +1,20 @@
+<html>
+  <style>
+    #green {
+      background-color: green;
+      margin-top: 50px;
+    }
+    #red {
+      background-color: red;
+      margin-top: 50px;
+      border-top: 10px solid blue;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=red></div>
+    </div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..84a4dd1
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..cdb72fd
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html
@@ -0,0 +1,26 @@
+<html>
+  <style>
+    body {
+      font-family: Roboto;
+      font-size: 50px;
+      font-weight: bold;
+      color: white;
+    }
+    #green {
+      background-color: green;
+      margin-top: 50px;
+    }
+    #blue {
+      background-color: blue;
+      margin-top: 50px;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<span>Hello World</span>
+    	<div id=blue></div>
+    </div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..5312214
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..9c0a24d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html
@@ -0,0 +1,20 @@
+<html>
+  <style>
+    #green {
+      background-color: green;
+      margin-top: 50px;
+    }
+    #red {
+      background-color: red;
+      margin-top: 50px;
+      padding-top: 20px;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=red></div>
+    </div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..f3f7314
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin.html
rename to src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..5a4fa3c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html
new file mode 100644
index 0000000..b493c12
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html
@@ -0,0 +1,34 @@
+<html>
+  <style>
+    div {
+      width: 50px;
+      height: 50px;
+    }
+    #red {
+      background-color: red;
+      margin-bottom: 50px;
+      margin-top: 10px;
+    }
+    #blue {
+      background-color: blue;
+      margin-bottom: 60px;
+      margin-top: 20px;
+      position: absolute;
+      top: 40px;
+    }
+    #green {
+      background-color: green;
+      margin-bottom: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+    }
+  </style>
+  <body>
+    <div id=red>
+    	<div id=green></div>
+    	<div id=blue></div>
+    </div>
+    <div id="yellow"></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..af12d2f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..3a1ce38
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html
@@ -0,0 +1,26 @@
+<html>
+  <style>
+    #green {
+      background-color: green;
+      margin-bottom: 50px;
+    }
+    #red {
+      background-color: red;
+      margin-bottom: 50px;
+      border-bottom: 10px solid blue;
+      width: 50px;
+      height: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=red></div>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..38157f0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..8c0d4e5
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html
@@ -0,0 +1,33 @@
+<html>
+  <style>
+    body {
+      font-family: Roboto;
+      font-size: 50px;
+      font-weight: bold;
+      color: white;
+    }
+    #green {
+      background-color: green;
+      margin-bottom: 50px;
+      margin-top: 10px;
+    }
+    #blue {
+      background-color: blue;
+      margin-bottom: 50px;
+      width: 50px;
+      height: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=blue></div>
+    	<span>Hello World</span>
+    </div>
+    <div id="yellow"></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..dbf48d6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..5652318
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html
@@ -0,0 +1,26 @@
+<html>
+  <style>
+    #green {
+      background-color: green;
+      margin-bottom: 50px;
+    }
+    #red {
+      background-color: red;
+      margin-bottom: 50px;
+      padding-bottom: 20px;
+      width: 50px;
+      height: 50px;
+    }
+    #yellow {
+      background-color: yellow;
+      width: 50px;
+      height: 50px;
+    }
+  </style>
+  <body>
+    <div id=green>
+    	<div id=red></div>
+    </div>
+    <div id=yellow></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..bafe0f6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin.html
rename to src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..3ec7148
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin.html
rename to src/cobalt/layout_tests/testdata/css-2-1/8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin-expected.png
new file mode 100644
index 0000000..9d70442
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin.html b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin.html
new file mode 100644
index 0000000..eff2c6b
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin.html
@@ -0,0 +1,34 @@
+<html>
+  <style>
+    div {
+      width: 50px;
+      height: 50px;
+    }
+    #green {
+      background-color: green;
+      margin-bottom: 10px;
+    }
+    #blue {
+      background-color: blue;
+      margin-top: 20px;
+      margin-bottom: 20px;
+      top: 30px;
+    }
+    #yellow {
+      background-color: yellow;
+      margin-top: 10px;
+      margin-bottom: 20px;
+    }
+    #purple {
+     background-color:  #673ab7;
+     margin-top: 20px;
+     position: absolute;
+    }
+  </style>
+  <body>
+    <div id=green></div>
+    <div id=blue></div>
+    <div id=yellow></div>
+    <div id=purple></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-top-and-bottom-margins-of-empty-element-should-collapse.html b/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-top-and-bottom-margins-of-empty-element-should-collapse.html
deleted file mode 100644
index 9602fdb..0000000
--- a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-8-3-1-top-and-bottom-margins-of-empty-element-should-collapse.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<!--
- | Adjoining vertical margins collapse if both belong to vertically-adjacent box
- | edges, i.e. top margin of a box and top margin of its first in-flow child.
- |   https://www.w3.org/TR/CSS21/box.html#collapsing-margins
- -->
-<html>
-<head>
-  <style>
-    .outer-blue {
-      background-color: #03a9f4;
-      height: 100px;
-      width: 100px;
-    }
-    .outer-green {
-      background-color: #00e676;
-      margin-top: 10px;
-    }
-    .inner-green {
-      background-color: #00c853;
-      height: 100px;
-      margin-top: 100px;
-      width: 100px;
-    }
-  </style>
-</head>
-<body>
-  <div class="outer-blue"></div>
-  <div class="outer-green">
-    <div class="inner-green"></div>
-  </div>
-</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 2d1ed62..0ec39c3 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
@@ -110,6 +110,29 @@
 18-4-outline-animation
 18-4-outline-overflow-hidden
 8-1-margin-should-be-transparent
+8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child
+8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin
+8-3-1-box-with-inline-child-should-not-form-collapsed-margin
+8-3-1-box-with-min-height-should-not-form-collapsed-margin
+8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin
+8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin
+8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin
+8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin
+8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin
+8-3-1-in-flow-siblings-should-form-collapsed-margin
+8-3-1-inline-level-boxes-should-not-form-collapsed-margin
+8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin
+8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin
+8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin
+8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin
+8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin
+8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin
+8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin
+8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin
+8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin
+8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin
+8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin
+8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin
 8-3-margin-percentage-should-refer-containing-block-width
 8-3-negative-margins-should-be-allowed
 8-3-negative_margins-should-be-allowed-to-produce_negative-box-widths
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/absolutely-positioned-children-expected.png b/src/cobalt/layout_tests/testdata/css3-flexbox/absolutely-positioned-children-expected.png
new file mode 100644
index 0000000..cb33c85
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/absolutely-positioned-children-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/absolutely-positioned-children.html b/src/cobalt/layout_tests/testdata/css3-flexbox/absolutely-positioned-children.html
new file mode 100644
index 0000000..52e896a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/absolutely-positioned-children.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<!--
+ | Tests for CSS Flexible Box Layout Module.
+ |   Testing absolutely positioned flex container children.
+ -->
+<html>
+<head>
+<style>
+  body {
+    margin: 0px;
+    font-family: Roboto;
+    background-color: gray;
+    font-size: 12px;
+  }
+  .flex {
+    display: inline-flex;
+    flex-flow: row wrap;
+    color: fuchsia;
+    background-color: yellow;
+    opacity: 0.75;
+    min-width: 400px;
+    min-height: 200px;
+    margin: 10px 20px 40px 30px;
+    border: solid white;
+    border-width: 10px 25px 25px 25px;
+    -no-position: relative;
+    -no-transform: translateX(0);
+  }
+  .absolute {
+    background-color: blue;
+    position: absolute;
+  }
+  .fixed {
+    background-color: purple;
+    position: fixed;
+  }
+  div > span {
+    padding: 1.25%;
+    border: 10px solid black;
+  }
+  div > video {
+    opacity: 1;
+    width: 5%;
+    height: 5%;
+    z-index: 1;
+  }
+</style>
+</head>
+<body>
+    -gh-
+    <div style="height: 5px; background-color: black;"></div>
+  <div style="display: inline-flex; width: 5px; height: 50px; background-color: black;"></div>
+  <span style="display: inline-block; width: 40px; background-color: lime;">-gh-</span>
+  <div class="flex">
+      <video class="absolute" style="top: 20%;"></video>
+      <video class="absolute" style="top: 30%; left: 0%;"></video>
+      <video class="absolute" style="left: 20%;"></video>
+      <video class="absolute" style="left: 30%; top: 0%;"></video>
+      <video class="fixed" style="top: 40%;"></video>
+      <video class="fixed" style="top: 50%; left: 0%;"></video>
+      <video class="fixed" style="left: 40%;"></video>
+      <video class="fixed" style="left: 50%; top: 0%;"></video>
+      <span class="absolute" style="top: 60%;"></span>
+      <span class="absolute" style="top: 70%; left: 0%;"></span>
+      <span class="absolute" style="left: 60%;"></span>
+      <span class="absolute" style="left: 70%; top: 0%;"></span>
+      <span class="fixed" style="top: 80%;"></span>
+      <span class="fixed" style="top: 90%; left: 0%;"></span>
+      <span class="fixed" style="left: 80%;"></span>
+      <span class="fixed" style="left: 90%; top: 0%"></span>
+  </div>
+  <div style="display: inline-flex; width: 5px; height: 50px; background-color: black;"></div>
+  <div style="height: 5px; background-color: black;"></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/combined-baseline-expected.png b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-baseline-expected.png
index 4afe5c9..b9b6c39 100644
--- a/src/cobalt/layout_tests/testdata/css3-flexbox/combined-baseline-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-baseline-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/empty_container_baseline-expected.png b/src/cobalt/layout_tests/testdata/css3-flexbox/empty_container_baseline-expected.png
new file mode 100644
index 0000000..8b26646
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/empty_container_baseline-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/empty_container_baseline.html b/src/cobalt/layout_tests/testdata/css3-flexbox/empty_container_baseline.html
new file mode 100644
index 0000000..e605155
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/empty_container_baseline.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<!--
+ | Tests for CSS Flexible Box Layout Module.
+ |   Testing absolutely positioned flex container children.
+ -->
+<html>
+<head>
+<style>
+  body {
+    margin: 0px;
+    font-family: Roboto;
+    background-color: gray;
+    font-size: 36px;
+  }
+  .flex {
+    display: inline-flex;
+    flex-flow: row wrap;
+    color: fuchsia;
+    background-color: yellow;
+    opacity: 0.75;
+    min-width: 10px;
+    min-height: 10px;
+    border: solid white;
+  }
+  .absolute {
+    background-color: blue;
+    position: absolute;
+  }
+  .fixed {
+    background-color: purple;
+    position: fixed;
+  }
+  div > span {
+    padding: 1.25%;
+    border: 10px solid black;
+  }
+  div > video {
+    opacity: 1;
+    width: 5%;
+    height: 5%;
+    z-index: 1;
+  }
+  .margin-a {
+    margin: 6px 12px 24px 16px;
+  }
+  .margin-b {
+    margin: 24px 16px 6px 12px;
+  }
+  .border-a {
+    border-width: 6px 12px 12px 12px;
+  }
+  .border-b {
+    border-width: 12px 12px 5px 12px;
+  }
+</style>
+</head>
+<body>
+  -gh-
+  <div style="height: 5px; background-color: black;"></div>
+  <div style="display: inline-flex; width: 5px; height: 50px; background-color: black;"></div>
+  <span style="display: inline-block; width: 60px; background-color: lime">-gh- -gh-</span>
+  <div class="flex"></div>
+  <div class="flex margin-a"></div>
+  <div class="flex margin-b"></div>
+  <div class="flex border-a"></div>
+  <div class="flex border-b"></div>
+  <div class="flex margin-a border-a"></div>
+  <div class="flex margin-a border-b""></div>
+  <div class="flex margin-b border-a"></div>
+  <div class="flex margin-b border-b""></div>
+  <div style="display: inline-flex; width: 5px; height: 50px; background-color: black;"></div>
+  <div style="height: 5px; background-color: black;"></div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt
index c8a34e8..26b3994 100644
--- a/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt
@@ -1,3 +1,4 @@
+absolutely-positioned-children
 combined-baseline
 combined-container-sizing-edge-cases
 combined-order-and-multiline
@@ -133,5 +134,6 @@
 csswg_flex-shrink-006
 csswg_flex-shrink-007
 csswg_flex-shrink-008
+empty_container_baseline
 flex-items-flexibility
 positioned-containers
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-001.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-001.html
new file mode 100644
index 0000000..f5c24ed
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-001.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: dir=rtl basic test</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-001-ref.html">
+<meta name="assert" content="If the element's dir attribute is in the rtl state, the directionality of the element is rtl.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test"><p dir="rtl">مكتب W3C הישראלי</p><p>left <span dir="rtl">مكتب W3C הישראלי</span> right</p></div>
+<div class="ref"><p style="text-align: right;">&#x202B;مكتب W3C הישראלי&#x202C;</p><p>left &#x202B;مكتب W3C הישראלי&#x202C; right</p></div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-002.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-002.html
new file mode 100644
index 0000000..a3f16fa
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-002.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: dir=ltr basic test</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-002-ref.html">
+<meta name="assert" content="If the element's dir attribute is in the ltr state, the directionality of the element is ltr.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test" dir="rtl"><p dir="ltr">مكتب W3C הישראלי</p><p>נכון <span dir="ltr">مكتب W3C הישראלי</span> שמאל</p></div>
+<div class="ref"><p>&#x202A;مكتب W3C הישראלי&#x202C;</p><p style="text-align: right;">שמאל &#x202A;مكتب W3C הישראלי&#x202C; נכון</p></div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-003.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-003.html
new file mode 100644
index 0000000..cf2e392
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-003.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: ltr context, rtl table</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-003-ref.html">
+<meta name="assert" content="[Exploratory] When dir='rtl' is added to a table in a ltr context, (a) directional runs in a table are ordered right-to-left, (b) columns run right-to-left, (c) text is right-aligned within cells, and (d) the table is left-aligned on the page.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+td { border: 1px dotted #ccc; } .ref table { align: right; } .ref td { text-align: right; }
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test">
+      <table dir="rtl">
+      <tr><td>1</td><td>2</td><td>3</td></tr>
+      <tr><td>مكتب W3C הישראלי</td><td>مكتب W3C הישראלי</td><td>مكتب W3C הישראלי</td></tr>
+      </table>
+      </div>
+<div class="ref" style="text-align:right;">
+      <table>
+      <tr><td>3</td><td>2</td><td>1</td></tr>
+      <tr><td>&#x202B;مكتب W3C הישראלי&#x202C;</td><td>&#x202B;مكتب W3C הישראלי&#x202C;</td><td>&#x202B;مكتب W3C הישראלי&#x202C;</td></tr>
+      </table>
+      </div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-004.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-004.html
new file mode 100644
index 0000000..c30e242
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-004.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: rtl context, ltr table</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-004-ref.html">
+<meta name="assert" content="[Exploratory] When dir='ltr' is added to a table in a rtl context, (a) directional runs in the table are ordered left-to-right, (b) columns run left-to-right, (c) text is left-aligned within cells, and (d) the table is right-aligned on the page.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+td { border: 1px dotted #ccc; } .ref table { align: left; }
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test" dir="rtl">
+      <table dir="ltr">
+      <tr><td>1</td><td>2</td><td>3</td></tr>
+      <tr><td>مكتب W3C הישראלי</td><td>مكتب W3C הישראלי</td><td>مكتب W3C הישראלי</td></tr>
+      </table>
+      </div>
+<div class="ref">
+      <table style="float:right;">
+      <tr><td>1</td><td>2</td><td>3</td></tr>
+      <tr><td>مكتب W3C הישראלי</td><td>مكتب W3C הישראלי</td><td>مكتب W3C הישראלי</td></tr>
+      </table>
+      <br style="clear:both;"/>
+      </div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-005.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-005.html
new file mode 100644
index 0000000..7e3addb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-005.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: ordered and unordered lists</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<meta name="assert" content="[Exploratory] In a rtl context, all list items should start from the right, regardless of the direction of the script in the list item.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if all bullets and numbers are to the right of the list item.</p>
+<div class="test" dir="rtl">
+      <ul>
+      <li>left right</li>
+      <li>حق غادر</li>
+      <li>נכון שמאל</li>
+      </ul>
+      <ol>
+      <li>left right</li>
+      <li>حق غادر</li>
+      <li>נכון שמאל</li>
+      </ol>
+      </div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-006.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-006.html
new file mode 100644
index 0000000..f500b8b
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-006.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: dl lists</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-006-ref.html">
+<meta name="assert" content="[Exploratory] In a rtl context, all list items should start from the right, regardless of the direction of the script in the list item.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+dt { font-weight: bold; margin:0; padding: 0; margin-top: 1em;  }
+      dd { margin:0; padding: 0; margin-right: 40px; }
+      .ref { text-align: right; }
+      .ref dd { margin:0; padding: 0; margin-right: 40px; }
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if you see no red characters.</p>
+<div class="test" dir="rtl">
+      <dl>
+      <dt>left right</dt>
+      <dd>left right</dd>
+      <dt>حق غادر</dt>
+      <dd>حق غادر</dd>
+      <dt>נכון שמאל</dt>
+      <dd>נכון שמאל</dd>
+      </dl>
+      </div>
+<div class="ref">
+      <dl>
+      <dt>left right</dt>
+      <dd>left right</dd>
+      <dt>حق غادر</dt>
+      <dd>حق غادر</dd>
+      <dt>נכון שמאל</dt>
+      <dd>נכון שמאל</dd>
+      </dl>
+      </div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-007.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-007.html
new file mode 100644
index 0000000..0fde1f8
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-007.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: inheritance</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-007-ref.html">
+<meta name="assert" content="If an element has no dir attribute, but has a parent element, the directionality of the element is the same as the parent element's directionality.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test">
+      <div dir="rtl"><p>مكتب W3C הישראלי</p><p>مكتب W3C הישראלי</p></div>
+      <div dir="rtl"><div><p>مكتب W3C הישראלי</p><p>مكتب W3C הישראלי</p></div></div>
+      <div dir="ltr"><p>مكتب W3C הישראלי</p><p>مكتب W3C הישראלי</p></div>
+      <div dir="ltr"><div><p>مكتب W3C הישראלי</p><p>مكتب W3C הישראלי</p></div></div>
+      </div>
+<div style="position: relative">
+<div class="test">
+      <div style="text-align: right;"><p>&#x202B;مكتب W3C הישראלי&#x202C;</p><p>&#x202B;مكتب W3C הישראלי&#x202C;</p></div>
+      <div style="text-align: right;"><p>&#x202B;مكتب W3C הישראלי&#x202C;</p><p>&#x202B;مكتب W3C הישראלי&#x202C;</p></div>
+      <div><p>&#x202A;مكتب W3C הישראלי&#x202C;</p><p>&#x202A;مكتب W3C הישראלי&#x202C;</p></div>
+      <div><p>&#x202A;مكتب W3C הישראלי&#x202C;</p><p>&#x202A;مكتب W3C הישראלי&#x202C;</p></div>
+      </div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-008.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-008.html
new file mode 100644
index 0000000..6183429
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-008.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: invalid value and inheritance</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-008-ref.html">
+<meta name="assert" content="If an element has a dir attribute with an invalid value ('foo' or 'bar'), and has a parent element, the directionality of the element is the same as the parent element's directionality.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test">
+      <div dir="rtl"><p dir="foo">مكتب W3C הישראלי</p></div>
+      <div dir="ltr"><p dir="bar">مكتب W3C הישראלי</p></div>
+      </div>
+<div style="position: relative">
+<div class="test">
+      <div style="text-align: right;"><p>&#x202B;مكتب W3C הישראלי&#x202C;</p></div>
+      <div><p>&#x202A;مكتب W3C הישראלי&#x202C;</p></div>
+      </div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-009.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-009.html
new file mode 100644
index 0000000..1fc1a3a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-009.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: invalid values left and right</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-009-ref.html">
+<meta name="assert" content="If an element has a dir attribute with an invalid value ('left' or 'right' or 'rl' or 'lr'), and has a parent element, the directionality of the element is the same as the parent element's directionality.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test">
+      <div dir="rtl"><p dir="left">مكتب W3C הישראלי</p></div>
+      <div dir="rtl"><p dir="lr">مكتب W3C הישראלי</p></div>
+      <div dir="ltr"><p dir="right">مكتب W3C הישראלי</p></div>
+      <div dir="ltr"><p dir="rl">مكتب W3C הישראלי</p></div>
+      </div>
+<div style="position: relative">
+<div class="test">
+      <div style="text-align: right;"><p>&#x202B;مكتب W3C הישראלי&#x202C;</p></div>
+      <div style="text-align: right;"><p>&#x202B;مكتب W3C הישראלי&#x202C;</p></div>
+      <div><p>&#x202A;مكتب W3C הישראלי&#x202C;</p></div>
+      <div><p>&#x202A;مكتب W3C הישראלי&#x202C;</p></div>
+      </div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-010.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-010.html
new file mode 100644
index 0000000..65e7fb3
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-010.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html    lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: default direction, basic test</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-010-ref.html">
+<meta name="assert" content="If the root element has no dir attribute, the directionality of an element is 'ltr'.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test"><p>مكتب W3C הישראלי</p></div>
+<div style="position: relative">
+<div class="test"><p>&#x202A;مكتب W3C הישראלי&#x202C;</p></div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-011.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-011.html
new file mode 100644
index 0000000..38e2053
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-011.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html  dir="right"   lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: default direction, invalid value 'right'</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-011-ref.html">
+<meta name="assert" content="If the root element has an invalid dir attribute ('right'), the directionality of an element is 'ltr'.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+
+</style>
+</head>
+<body>
+<p class="instructions" dir="ltr">Test passes if you see no red characters.</p>
+<div style="position: relative">
+<div class="test"><p>مكتب W3C הישראלי</p></div>
+<div style="position: relative">
+<div class="test"><p>&#x202A;مكتب W3C הישראלי&#x202C;</p></div>
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-012.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-012.html
new file mode 100644
index 0000000..570e674
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/the-dir-attribute-012.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html  dir="rl"   lang="en" >
+<head>
+<meta charset="utf-8"/>
+<title>The dir attribute: default direction, invalid value 'rl'</title>
+<link rel='author' title='HTML5 bidi test WG' href='mailto:public-i18n-bidi@w3.org'>
+<link rel='help' href='https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute'>
+<link rel="match" href="reference/the-dir-attribute-012-ref.html">
+<meta name="assert" content="If the root element has an invalid dir attribute ('rl'), the directionality of an element is 'ltr'.">
+<style type='text/css'>
+.test, .ref { width: 90%; margin: 0 4%; font-size: 2em; position: absolute; top: 0; }
+.ref { color: red; z-index: -100; }
+<