Import Cobalt 12.88774

Change-Id: Id685e20e1b1f8fec13607b39f552fad3cc76ebae
diff --git a/src/.gn b/src/.gn
new file mode 100644
index 0000000..13e4348
--- /dev/null
+++ b/src/.gn
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The location of the build configuration file.
+buildconfig = "//cobalt/build/config/BUILDCONFIG.gn"
+
+# TODO: add exec_script_whitelist
diff --git a/src/BUILD.gn b/src/BUILD.gn
new file mode 100644
index 0000000..aaf5d17
--- /dev/null
+++ b/src/BUILD.gn
@@ -0,0 +1,81 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO: uncomment when //cobalt/browser:cobalt is converted over. Then this
+# group will become the default target built when ninja is run, paralleling GYP
+# behavior.
+# group("default") {
+#   deps = [
+#     "//cobalt/browser:cobalt"
+#   ]
+# }
+
+# TODO: consider splitting test and non-test
+group("all") {
+  testonly = true
+  deps = [
+    # TODO: convert all of the targets below from GYP to GN
+    # Uncomment as each target is converted.
+    # See cobalt/build/all.gyp for the GYP names.
+    # In particular, each :all target (corresponding to GYP's foo/bar.gyp:*)
+    # mentioned here will have to be manually defined in the appropriate
+    # BUILD.gn file. Ditto for :all_test.
+    # "//base:unittests",
+    # "//cobalt/accessibility:all_test",
+    # "//cobalt/audio:all",
+    # "//cobalt/base:all",
+    # "//cobalt/bindings/testing:all",
+    # "//cobalt/browser:all",
+    # "//cobalt/browser:all_cobalt",
+    # "//cobalt/csp:all",
+    # "//cobalt/css_parser:all",
+    # "//cobalt/cssom:all",
+    # "//cobalt/cssom:all_test",
+    # "//cobalt/debug:all",
+    # "//cobalt/dom:all",
+    # "//cobalt/dom/dom_test.gyp:all",
+    # "//cobalt/dom_parser:all",
+    # "//cobalt/h5vcc:all",
+    # "//cobalt/input:all",
+    # "//cobalt/layout:all",
+    # "//cobalt/layout_tests:all",
+    # "//cobalt/loader/image/sandbox:all",
+    # "//cobalt/loader:all",
+    # "//cobalt/math:all",
+    # "//cobalt/media/sandbox:all",
+    # "//cobalt/media_session:all",
+    # "//cobalt/media_session:all_test",
+    # "//cobalt/network:all",
+    # "//cobalt/render_tree:all",
+    # "//cobalt/renderer:all",
+    # "//cobalt/renderer/sandbox:all",
+    # "//cobalt/samples/simple_example:all",
+    # "//cobalt/script:all",
+    # "//cobalt/script/engine:all",
+    # "//cobalt/speech/sandbox:all",
+    # "//cobalt/speech:all",
+    # "//cobalt/storage:all",
+    # "//cobalt/trace_event:all",
+    # "//cobalt/web_animations:all",
+    # "//cobalt/webdriver:all",
+    # "//cobalt/webdriver:all_test",
+    # "//cobalt/websocket:all",
+    # "//cobalt/xhr:all",
+    # "//crypto:crypto_unittests",
+    # "//sql:sql_unittests",
+    # "//nb/nb:test",
+    # "//nb:reuse_allocator_benchmark",
+    "//starboard:all",
+  ]
+}
diff --git a/src/base/allocator/allocator.gyp b/src/base/allocator/allocator.gyp
index 3c62f47..a2ed293 100644
--- a/src/base/allocator/allocator.gyp
+++ b/src/base/allocator/allocator.gyp
@@ -315,124 +315,11 @@
         },
       },
       'conditions': [
-        ['OS=="linux" and clang_type_profiler==1', {
-          'dependencies': [
-            'type_profiler_tcmalloc',
-          ],
-          # It is undoing dependencies and cflags_cc for type_profiler which
-          # build/common.gypi injects into all targets.
-          'dependencies!': [
-            'type_profiler',
-          ],
-          'cflags_cc!': [
-            '-fintercept-allocation-functions',
-          ],
-        }],
-        ['OS=="win"', {
-          'defines': [
-            'PERFTOOLS_DLL_DECL=',
-          ],
-          'defines!': [
-            # tcmalloc source files unconditionally define this, remove it from
-            # the list of defines that common.gypi defines globally.
-            'NOMINMAX',
-          ],
-          'dependencies': [
-            'libcmt',
-          ],
-          'include_dirs': [
-            '<(jemalloc_dir)',
-            '<(tcmalloc_dir)/src/windows',
-          ],
-          'sources!': [
-            '<(tcmalloc_dir)/src/base/elf_mem_image.cc',
-            '<(tcmalloc_dir)/src/base/elf_mem_image.h',
-            '<(tcmalloc_dir)/src/base/linuxthreads.cc',
-            '<(tcmalloc_dir)/src/base/linuxthreads.h',
-            '<(tcmalloc_dir)/src/base/vdso_support.cc',
-            '<(tcmalloc_dir)/src/base/vdso_support.h',
-            '<(tcmalloc_dir)/src/maybe_threads.cc',
-            '<(tcmalloc_dir)/src/maybe_threads.h',
-            '<(tcmalloc_dir)/src/symbolize.h',
-            '<(tcmalloc_dir)/src/system-alloc.cc',
-            '<(tcmalloc_dir)/src/system-alloc.h',
-
-            # included by allocator_shim.cc
-            'debugallocation_shim.cc',
-
-            # heap-profiler/checker/cpuprofiler
-            '<(tcmalloc_dir)/src/base/thread_lister.c',
-            '<(tcmalloc_dir)/src/base/thread_lister.h',
-            '<(tcmalloc_dir)/src/deep-heap-profile.cc',
-            '<(tcmalloc_dir)/src/deep-heap-profile.h',
-            '<(tcmalloc_dir)/src/heap-profiler.cc',
-            '<(tcmalloc_dir)/src/heap-profile-table.cc',
-            '<(tcmalloc_dir)/src/heap-profile-table.h',
-            '<(tcmalloc_dir)/src/memory_region_map.cc',
-            '<(tcmalloc_dir)/src/memory_region_map.h',
-            '<(tcmalloc_dir)/src/profiledata.cc',
-            '<(tcmalloc_dir)/src/profiledata.h',
-            '<(tcmalloc_dir)/src/profile-handler.cc',
-            '<(tcmalloc_dir)/src/profile-handler.h',
-            '<(tcmalloc_dir)/src/profiler.cc',
-          ],
-        }],
-        ['OS=="linux" or OS=="freebsd" or OS=="solaris"', {
-          'sources!': [
-            '<(tcmalloc_dir)/src/system-alloc.h',
-            '<(tcmalloc_dir)/src/windows/port.cc',
-            '<(tcmalloc_dir)/src/windows/port.h',
-
-            # TODO(willchan): Support allocator shim later on.
-            'allocator_shim.cc',
-
-            # TODO(willchan): support jemalloc on other platforms
-            # jemalloc files
-            '<(jemalloc_dir)/jemalloc.c',
-            '<(jemalloc_dir)/jemalloc.h',
-            '<(jemalloc_dir)/ql.h',
-            '<(jemalloc_dir)/qr.h',
-            '<(jemalloc_dir)/rb.h',
-
-          ],
-          # We enable all warnings by default, but upstream disables a few.
-          # Keep "-Wno-*" flags in sync with upstream by comparing against:
-          # http://code.google.com/p/google-perftools/source/browse/trunk/Makefile.am
-          'cflags': [
-            '-Wno-sign-compare',
-            '-Wno-unused-result',
-          ],
-          'cflags!': [
-            '-fvisibility=hidden',
-          ],
-          'link_settings': {
-            'ldflags': [
-              # Don't let linker rip this symbol out, otherwise the heap&cpu
-              # profilers will not initialize properly on startup.
-              '-Wl,-uIsHeapProfilerRunning,-uProfilerStart',
-              # Do the same for heap leak checker.
-              '-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi',
-              '-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl',
-              '-Wl,-u_ZN15HeapLeakChecker12IgnoreObjectEPKv,-u_ZN15HeapLeakChecker14UnIgnoreObjectEPKv',
-          ]},
-        }],
-        [ 'use_vtable_verify==1', {
-          'cflags': [
-            '-fvtable-verify=preinit',
-          ],
-        }],
         [ 'clang==1', {
           'cflags': [
             '-Wno-non-literal-null-conversion',
           ],
         }],
-        ['order_profiling != 0', {
-          'target_conditions' : [
-            ['_toolset=="target"', {
-              'cflags!': [ '-finstrument-functions' ],
-            }],
-          ],
-        }],
       ],
     },
     {
@@ -449,18 +336,6 @@
       'include_dirs': [
         '../../'
       ],
-      'conditions': [
-        ['OS=="linux" and clang_type_profiler==1', {
-          # It is undoing dependencies and cflags_cc for type_profiler which
-          # build/common.gypi injects into all targets.
-          'dependencies!': [
-            'type_profiler',
-          ],
-          'cflags_cc!': [
-            '-fintercept-allocation-functions',
-          ],
-        }],
-      ],
     },
    ],
   'conditions': [
diff --git a/src/base/base.gyp b/src/base/base.gyp
index d9c7764..56a0c9a 100644
--- a/src/base/base.gyp
+++ b/src/base/base.gyp
@@ -239,7 +239,6 @@
         'metrics/statistics_recorder_unittest.cc',
         'object_tracker.h',
         'observer_list_unittest.cc',
-        'optional_unittest.cc',
         'path_service_unittest.cc',
         'pickle_unittest.cc',
         'platform_file_unittest.cc',
@@ -248,7 +247,6 @@
         'rand_util_unittest.cc',
         'scoped_observer.h',
         'sha1_unittest.cc',
-        'state_machine_shell_unittest.cc',
         'stl_util_unittest.cc',
         'string16_unittest.cc',
         'string_number_conversions_unittest.cc',
diff --git a/src/base/base.gypi b/src/base/base.gypi
index dc08b0c..fe57218 100644
--- a/src/base/base.gypi
+++ b/src/base/base.gypi
@@ -183,9 +183,7 @@
           'native_library.h',
           'observer_list.h',
           'observer_list_threadsafe.h',
-          'optional.cc',
           'optional.h',
-          'optional_internal.h',
           'path_service.cc',
           'path_service.h',
           'pending_task.cc',
@@ -218,7 +216,6 @@
           'sha1.h',
           'sha1_portable.cc',
           'single_thread_task_runner.h',
-          'state_machine_shell.cc',
           'state_machine_shell.h',
           'stl_util.h',
           'string16.cc',
diff --git a/src/base/optional.cc b/src/base/optional.cc
deleted file mode 100644
index 59100ad..0000000
--- a/src/base/optional.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "optional.h"
-
-namespace base {
-
-const nullopt_t nullopt;
-const in_place_t in_place;
-
-}  // namespace base
diff --git a/src/base/optional.h b/src/base/optional.h
index 66bc53f..ce973ea 100644
--- a/src/base/optional.h
+++ b/src/base/optional.h
@@ -17,377 +17,17 @@
 #ifndef BASE_OPTIONAL_H_
 #define BASE_OPTIONAL_H_
 
-#include <iosfwd>
-
-#include "base/base_export.h"
-#include "base/hash_tables.h"
 #include "base/logging.h"
 #include "base/memory/aligned_memory.h"
+#include "starboard/common/optional.h"
 
 namespace base {
-
-// This class is based off of std::experimental::optional:
-//   http://en.cppreference.com/w/cpp/experimental/optional
-//
-// It is a template class where instances parameterized by type T contain
-// memory for an instance of type T, but it may or may not be constructed.
-// If it is not constructed, it cannot be accessed, and if it is, it can
-// be accessed.  This allows one to check if the inner object exists or not
-// before using it, and is useful for functions that may or may not return
-// a value.  Note that the memory for the object is stored internally, so
-// no heap allocations are made over the course of construction and destruction
-// of the internal object (unless the internal object allocates memory within
-// its constructor).
-//
-// Some functionality is left out.  For example, most C++11 functionality
-// is not implemented, since we would like this to be friendly to non-C++11
-// compilers.
-//
-// In the future, if C++11 functionality is needed, it can be implemented
-// and surrounded by preprocessor guards to maintain compatibility with non
-// C++11 compilers.
-//
-
-// The nullopt_t type is used as a signal for an empty optional.  If any
-// optional is assigned the value of nullopt, it will be disengaged.
-// For example,
-//   base::optional<int> my_int_optional(5);
-//   EXPECT_FALSE(!my_int_optional);
-//   my_int_optional = base::nullopt;
-//   EXPECT_TRUE(!my_int_optional);
-//
-struct nullopt_t {
-  nullopt_t() {}
-};
-extern const nullopt_t nullopt;
-
-// The in_place_t type is used to signal in-place construction of the internal
-// object.  This is used by the in place constructor of optional, which forwards
-// its parameters to the internal object's constructor.
-// For example,
-//   class Foo {
-//    public:
-//     Foo(int x, int y) { x_ = x; y_ = y; }
-//     int x() const { return x_; }
-//     int y() const { return y_; }
-//
-//    private:
-//     int x_;
-//     int y_;
-//   };
-//
-//   ...
-//
-//   base::optional<Foo> my_foo(base::in_place, 2, 3);
-//   EXPECT_FALSE(!my_foo);
-//   EXPECT_EQ(2, my_foo->x());
-//   EXPECT_EQ(3, my_foo->y());
-//
-struct in_place_t {
-  in_place_t() {}
-};
-extern const in_place_t in_place;
-
-template <typename T>
-class BASE_EXPORT optional {
- public:
-  // Construction via the default constructor results in an optional that is
-  // not engaged.
-  optional() { InitializeAsDisengaged(); }
-
-  optional(nullopt_t) { InitializeAsDisengaged(); }
-
-  // This non-explicit singleton constructor is provided so users can pass in a
-  // T wherever a optional<T> is expected.
-  optional(const T& value) { SetValue(value); }
-
-  optional(const optional<T>& other) {
-    if (other.engaged_) {
-      SetValue(other.value());
-    } else {
-      InitializeAsDisengaged();
-    }
-  }
-
-  // Destruct contained object upon optional's destruction.
-  ~optional() { EnsureDisengaged(); }
-
-  // Disengages the optional, calling the destructor of the contained object
-  // if it is engaged.
-  optional<T>& operator=(nullopt_t) {
-    EnsureDisengaged();
-    return *this;
-  }
-
-  // Reassigns the underlying optional to value passed in on the right hand
-  // side.  This will destruct the lhs contained object first if it exists.
-  template <typename U>
-  optional<T>& operator=(const U& other) {
-    if (engaged_) {
-      value() = other;
-    } else {
-      SetValue(other);
-    }
-    return *this;
-  }
-
-  // Copy assignment
-  optional<T>& operator=(const optional<T>& other) {
-    if (engaged_ && other.engaged_) {
-      value() = other.value();
-    } else if (!engaged_ && other.engaged_) {
-      SetValue(other.value());
-    } else if (engaged_ && !other.engaged_) {
-      EnsureDisengaged();
-    }
-    // Do nothing if lhs and rhs are both not engaged.
-    return *this;
-  }
-
-  // Overloaded conversion to bool operator for determining whether the optional
-  // is engaged or not.  It returns true if the optional is engaged, and false
-  // otherwise.
-#if (defined(_MSC_VER) && (_MSC_VER < 1800)) || \
-    (defined(__GNUC__) &&                       \
-     (__GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 5))))
-  // MSVC 2012 does not support explicit cast operators.
-  // http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
-
-  // For any compiler that doesn't support explicit bool operators, we instead
-  // use the Safe Bool Idiom: http://www.artima.com/cppsource/safebool.html
- private:
-  // The type of SafeBoolIdiomType (pointer to data member of a private type) is
-  // limited in functionality so much that the only thing a user can do with it
-  // is test for null, or apply to operator==/operator!=.  Since both operators
-  // == and != are already overloaded for optional, this leaves null tests,
-  // which we use for boolean testing.
-  class PrivateSafeBoolIdiomFakeMemberType;
-  typedef PrivateSafeBoolIdiomFakeMemberType optional::*SafeBoolIdiomType;
- public:
-  operator const SafeBoolIdiomType() const {
-    // If we wish to return true, we cast engaged_ to our private type giving
-    // a non-null pointer to data member.  Otherwise, we return NULL.  The
-    // only thing the user can do with the return type is test for NULL.
-    return engaged_ ?
-        reinterpret_cast<const SafeBoolIdiomType>(&optional::engaged_) :
-        NULL;
-  }
-#else
-  explicit operator bool() const { return engaged_; }
-#endif
-
-  // Dereferences the internal object.
-  const T* operator->() const { return &(value()); }
-
-  T* operator->() { return &(value()); }
-
-  const T& operator*() const { return value(); }
-
-  T& operator*() { return value(); }
-
-  // Dereferences and returns the internal object.
-  const T& value() const {
-    DCHECK(engaged_) << "Attempted to access object in a disengaged optional.";
-    return *static_cast<const T*>(value_memory_.void_data());
-  }
-
-  T& value() {
-    DCHECK(engaged_) << "Attempted to access object in a disengaged optional.";
-    return *static_cast<T*>(value_memory_.void_data());
-  }
-
-  template <typename U>
-  T value_or(const U& value) const {
-    if (engaged_) {
-      return this->value();
-    } else {
-      return value;
-    }
-  }
-
-  // Swaps the values of two optionals.
-  void swap(optional<T>& other) {
-    if (engaged_ && other.engaged_) {
-      // Swap the value contents with each other.
-      std::swap(value(), other.value());
-    } else if (engaged_) {
-      other.SetValue(value());
-      EnsureDisengaged();
-    } else if (other.engaged_) {
-      SetValue(other.value());
-      other.EnsureDisengaged();
-    }
-    // If both the lhs and rhs are not engaged, we do nothing.
-  }
-
-// include the pump.py-generated declaration and impelmentation for
-// forwarding constructor and emplace.
-#include "optional_internal.h"
-
- private:
-  // Sets a non-engaged optional to a specified value, and marks it as engaged.
-  template <typename U>
-  void SetValue(const U& value) {
-    new (value_memory_.void_data()) T(value);
-    engaged_ = true;
-#if !defined(NDEBUG)
-    value_ptr_ = static_cast<const T*>(value_memory_.void_data());
-#endif
-  }
-
-  // If an optional is engaged, it destructs the wrapped value and marks the
-  // optional as disengaged.  Does nothing to a disengaged optional.
-  void EnsureDisengaged() {
-    if (engaged_) {
-      static_cast<T*>(value_memory_.void_data())->~T();
-      engaged_ = false;
-#if !defined(NDEBUG)
-      value_ptr_ = NULL;
-#endif
-    }
-  }
-
-  // Called upon object construction to initialize the object into a disengaged
-  // state.
-  void InitializeAsDisengaged() {
-    engaged_ = false;
-#if !defined(NDEBUG)
-    value_ptr_ = NULL;
-#endif
-  }
-
-  // The actual memory reserved for the object that may or may not exist.
-  base::AlignedMemory<sizeof(T), ALIGNOF(T)> value_memory_;
-  // This boolean tracks whether or not the object is constructed yet or not.
-  bool engaged_;
-#if !defined(NDEBUG)
-  // In debug builds, this member makes it easy to inspect the value contained
-  // in the optional via a debugger.
-  const T* value_ptr_;
-#endif
-};
-
-// Comparison between 2 optionals
-template <typename T>
-inline bool operator==(const optional<T>& lhs, const optional<T>& rhs) {
-  if (!lhs) {
-    return !rhs;
-  }
-
-  return rhs == lhs.value();
-}
-
-template <typename T>
-inline bool operator<(const optional<T>& lhs, const optional<T>& rhs) {
-  if (lhs && rhs) {
-    return lhs.value() < rhs.value();
-  } else {
-    // Handle all other cases simply in terms of whether the optionals are
-    // engaged or not.
-    return static_cast<bool>(lhs) < static_cast<bool>(rhs);
-  }
-}
-
-// Comparison with nullopt_t
-template <typename T>
-inline bool operator==(nullopt_t, const optional<T>& rhs) {
-  return !rhs;
-}
-
-template <typename T>
-inline bool operator==(const optional<T>& lhs, nullopt_t rhs) {
-  return rhs == lhs;
-}
-
-template <typename T>
-inline bool operator<(const optional<T>& /* lhs */, nullopt_t) {
-  return false;
-}
-
-template <typename T>
-inline bool operator<(nullopt_t, const optional<T>& rhs) {
-  return static_cast<bool>(rhs);
-}
-
-// Comparison between an optional and a value
-template <typename T>
-inline bool operator==(const optional<T>& lhs, const T& rhs) {
-  return (!lhs ? false : lhs.value() == rhs);
-}
-
-template <typename T>
-inline bool operator==(const T& lhs, const optional<T>& rhs) {
-  return rhs == lhs;
-}
-
-template <typename T>
-inline bool operator<(const T& lhs, const optional<T>& rhs) {
-  return rhs && lhs < rhs.value();
-}
-
-template <typename T>
-inline bool operator<(const optional<T>& lhs, const T& rhs) {
-  return !lhs || lhs.value() < rhs;
-}
-
-// This is a convenient but non-standard method, do not rely on it if you expect
-// the compatibility with upcoming C++ versions.
-template <typename T>
-inline std::ostream& operator<<(std::ostream& stream,
-                                const optional<T>& maybe_value) {
-  if (maybe_value) {
-    stream << *maybe_value;
-  } else {
-    stream << "nullopt";
-  }
-  return stream;
-}
-
-template <typename T>
-optional<T> make_optional(const T& value) {
-  return optional<T>(value);
-}
-
+using starboard::nullopt_t;
+using starboard::nullopt;
+using starboard::in_place_t;
+using starboard::in_place;
+using starboard::optional;
+using starboard::make_optional;
 }  // namespace base
 
-namespace BASE_HASH_NAMESPACE {
-
-#if defined(BASE_HASH_USE_HASH_STRUCT)
-template <typename T>
-struct hash<base::optional<T> > {
- public:
-  size_t operator()(const base::optional<T>& value) const {
-    if (!value) {
-      return 0;
-    } else {
-      return value_hash_(value.value());
-    }
-  }
-
- private:
-  hash<T> value_hash_;
-};
-
-#else
-template <typename T>
-inline size_t hash_value(const base::optional<T>& value) {
-  if (!value) {
-    return 0;
-  } else {
-    return hash_value(value.value());
-  }
-}
-
-#endif  // defined(BASE_HASH_USE_HASH_STRUCT)
-}  // namespace BASE_HASH_NAMESPACE
-
-namespace std {
-
-template <typename T>
-void swap(base::optional<T>& lhs, base::optional<T>& rhs) {
-  lhs.swap(rhs);
-}
-
-}  // namespace std
-
 #endif  // BASE_OPTIONAL_H_
diff --git a/src/base/optional_unittest.cc b/src/base/optional_unittest.cc
deleted file mode 100644
index 3fe99c8..0000000
--- a/src/base/optional_unittest.cc
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/hash_tables.h"
-#include "base/optional.h"
-
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::InSequence;
-
-typedef base::optional<int> OptionalInt;
-
-TEST(OptionalTest, EnsureDefaultConstructorGivesDisengagedOptional) {
-  OptionalInt test;
-  EXPECT_TRUE(!test);
-}
-
-TEST(OptionalTest, EnsureNullOptConstructorGivesDisengagedOptional) {
-  OptionalInt test(base::nullopt);
-  EXPECT_TRUE(!test);
-}
-
-TEST(OptionalTest, InitializeConstructor) {
-  OptionalInt test(2);
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(2, test.value());
-}
-
-TEST(OptionalTest, BoolCastOperator) {
-  OptionalInt test;
-  EXPECT_FALSE(static_cast<bool>(test));
-
-  test = 5;
-  EXPECT_TRUE(static_cast<bool>(test));
-}
-
-TEST(OptionalTest, InitializeAssign) {
-  OptionalInt test = 2;
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(2, test.value());
-}
-
-TEST(OptionalTest, ReassignValue) {
-  OptionalInt test(2);
-  test = 5;
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(5, test.value());
-}
-
-TEST(OptionalTest, CopyAssignment) {
-  OptionalInt a;
-  OptionalInt b = 2;
-  OptionalInt c = 3;
-  a = b;
-  EXPECT_FALSE(!a);
-  EXPECT_EQ(2, a.value());
-
-  a = c;
-  EXPECT_FALSE(!a);
-  EXPECT_EQ(3, a.value());
-}
-
-TEST(OptionalTest, ClearAssignment) {
-  OptionalInt test(2);
-  test = OptionalInt();
-  EXPECT_FALSE(test);
-}
-
-TEST(OptionalTest, EnsureAssignmentOfNullOptResultsInDisengagement) {
-  OptionalInt test(2);
-  test = base::nullopt;
-  EXPECT_FALSE(test);
-}
-
-TEST(OptionalTest, EnsureAssignmentOfDefaultOptionalResultsInDisengagement) {
-  OptionalInt test(2);
-  test = OptionalInt();
-  EXPECT_FALSE(test);
-}
-
-TEST(OptionalTest, CopyConstruction) {
-  OptionalInt test1(2);
-  OptionalInt test2(test1);
-
-  EXPECT_FALSE(!test2);
-  EXPECT_EQ(2, test2.value());
-}
-
-TEST(OptionalTest, Swap) {
-  OptionalInt test1(1);
-  OptionalInt test2(2);
-
-  // Swap two engaged optionals.
-  test1.swap(test2);
-  EXPECT_FALSE(!test1);
-  EXPECT_EQ(2, test1.value());
-  EXPECT_EQ(1, test2.value());
-
-  // Swap two optionals where only one is engaged.
-  test1 = base::nullopt;
-  test1.swap(test2);
-  EXPECT_FALSE(test2);
-  EXPECT_FALSE(!test1);
-  EXPECT_EQ(1, test1.value());
-
-  // Swap two optionals where only one is engaged, except the other way around.
-  test1.swap(test2);
-  EXPECT_FALSE(test1);
-  EXPECT_FALSE(!test2);
-  EXPECT_EQ(1, test2.value());
-
-  // Swap two disengaged optionals.
-  test2 = base::nullopt;
-  test1.swap(test2);
-  EXPECT_FALSE(test1);
-  EXPECT_FALSE(test2);
-}
-
-TEST(OptionalTest, StdSwap) {
-  OptionalInt test1(1);
-  OptionalInt test2(2);
-
-  // Swap two engaged optionals.
-  std::swap(test1, test2);
-  EXPECT_FALSE(!test1);
-  EXPECT_EQ(2, test1.value());
-  EXPECT_EQ(1, test2.value());
-
-  // Swap two optionals where only one is engaged.
-  test1 = base::nullopt;
-  std::swap(test1, test2);
-  EXPECT_FALSE(test2);
-  EXPECT_FALSE(!test1);
-  EXPECT_EQ(1, test1.value());
-
-  // Swap two optionals where only one is engaged, except the other way around.
-  std::swap(test1, test2);
-  EXPECT_FALSE(test1);
-  EXPECT_FALSE(!test2);
-  EXPECT_EQ(1, test2.value());
-
-  // Swap two disengaged optionals.
-  test2 = base::nullopt;
-  std::swap(test1, test2);
-  EXPECT_FALSE(test1);
-  EXPECT_FALSE(test2);
-}
-
-TEST(OptionalTest, SwapWithSelf) {
-  OptionalInt test(1);
-
-  test.swap(test);
-  EXPECT_EQ(1, test.value());
-
-  std::swap(test, test);
-  EXPECT_EQ(1, test.value());
-}
-
-TEST(OptionalTest, AsteriskDereference) {
-  OptionalInt test(2);
-  EXPECT_EQ(2, *test);
-
-  *test = 5;
-  EXPECT_EQ(5, test.value());
-}
-
-namespace {
-struct TestStruct {
-  int foobar;
-};
-}  // namespace
-
-TEST(OptionalTest, ArrowDereference) {
-  TestStruct a;
-  a.foobar = 2;
-
-  base::optional<TestStruct> test(a);
-  EXPECT_EQ(2, test->foobar);
-
-  test->foobar = 5;
-  EXPECT_EQ(5, test.value().foobar);
-
-  // Test const arrow dereference.
-  const base::optional<TestStruct> test_const(a);
-  EXPECT_EQ(2, test_const->foobar);
-}
-
-namespace {
-class NoDefaultTest {
- public:
-  explicit NoDefaultTest(int i) { foobar_ = i; }
-  int foobar() const { return foobar_; }
-
- private:
-  NoDefaultTest();
-
-  int foobar_;
-};
-}  // namespace
-
-TEST(OptionalTest, NoDefaultConstructorIsSupported) {
-  // First test with an object passed in upon construction
-  base::optional<NoDefaultTest> test1(NoDefaultTest(2));
-  EXPECT_EQ(2, test1.value().foobar());
-
-  // Now test with an object assignment after construction
-  base::optional<NoDefaultTest> test2;
-  test2 = NoDefaultTest(5);
-  EXPECT_EQ(5, test2.value().foobar());
-}
-
-TEST(OptionalTest, TestEquivalenceComparisons) {
-  OptionalInt test1 = 1;
-  OptionalInt test2 = 2;
-  OptionalInt test3;
-  OptionalInt test4 = 2;
-
-  EXPECT_FALSE(test1 == test2);
-  EXPECT_FALSE(test2 == test1);
-  EXPECT_FALSE(test1 == test3);
-  EXPECT_FALSE(test3 == test1);
-  EXPECT_FALSE(2 == test1);
-  EXPECT_FALSE(test1 == 2);
-  EXPECT_EQ(1, test1);
-  EXPECT_EQ(test1, 1);
-  EXPECT_EQ(test4, test2);
-  EXPECT_EQ(test2, test4);
-
-  // Test nullopt comparisons
-  EXPECT_TRUE(test3 == base::nullopt);
-  EXPECT_TRUE(base::nullopt == test3);
-  EXPECT_FALSE(test1 == base::nullopt);
-  EXPECT_FALSE(base::nullopt == test1);
-}
-
-TEST(OptionalTest, TestLessThanComparisons) {
-  OptionalInt test1 = 1;
-  OptionalInt test2 = 2;
-  OptionalInt test3;
-  OptionalInt test4 = 2;
-
-  EXPECT_TRUE(test1 < test2);
-  EXPECT_FALSE(test2 < test1);
-  EXPECT_FALSE(test1 < test1);
-  EXPECT_TRUE(test3 < test1);
-  EXPECT_FALSE(test1 < test3);
-  EXPECT_FALSE(test3 < test3);
-
-  EXPECT_TRUE(base::nullopt < test1);
-  EXPECT_FALSE(test1 < base::nullopt);
-
-  EXPECT_TRUE(test1 < 2);
-  EXPECT_FALSE(2 < test1);
-}
-
-TEST(OptionalTest, EnsureOptionalWorksWithFunnyAlignments) {
-  struct {
-    char c;
-    base::optional<uint64_t> number;
-  } foo;
-
-  EXPECT_FALSE(!!foo.number);
-  foo.number = 1;
-  EXPECT_TRUE(!!foo.number);
-  EXPECT_EQ(1, foo.number.value());
-}
-
-namespace {
-
-class DestructorCallTester {
- public:
-  DestructorCallTester() {};
-  DestructorCallTester(const DestructorCallTester&) {};
-  ~DestructorCallTester() { Die(); }
-  MOCK_METHOD0(Die, void());
-};
-
-}  // namespace
-
-TEST(OptionalTest, EnsureDestructorIsCalled) {
-  {
-    // Ensure destructor is called upon optional destruction
-    base::optional<DestructorCallTester> test(base::in_place);
-    EXPECT_CALL(test.value(), Die());
-  }
-
-  {
-    // Ensure destructor is called upon assignment to null
-    base::optional<DestructorCallTester> test1(base::in_place);
-    base::optional<DestructorCallTester> test2(base::in_place);
-    {
-      InSequence s;
-      EXPECT_CALL(test1.value(), Die());
-      EXPECT_CALL(test2.value(), Die());
-    }
-    test1 = base::nullopt;
-  }
-}
-
-namespace {
-
-// This class counts all calls to the set of methods declared in the class.
-// It can be used to verify that certain methods are indeed called.
-class MethodCallCounter {
- public:
-  MethodCallCounter() {
-    ResetCounts();
-    ++default_constructor_calls_;
-  }
-
-  MethodCallCounter(const MethodCallCounter& other) {
-    // A very non-standard copy constructor, since this is a test object
-    // intended to count method calls on this object and only this object.
-    ResetCounts();
-    ++copy_constructor_calls_;
-  }
-
-  MethodCallCounter& operator=(const MethodCallCounter& other) {
-    ++operator_equals_calls_;
-    return *this;
-  }
-
-  int default_constructor_calls() const { return default_constructor_calls_; }
-  int copy_constructor_calls() const { return copy_constructor_calls_; }
-  int operator_equals_calls() const { return operator_equals_calls_; }
-
- private:
-  void ResetCounts() {
-    default_constructor_calls_ = 0;
-    copy_constructor_calls_ = 0;
-    operator_equals_calls_ = 0;
-  }
-
-  int default_constructor_calls_;
-  int copy_constructor_calls_;
-  int operator_equals_calls_;
-};
-
-}  // namespace
-
-TEST(OptionalTest, CopyConstructorIsCalledByValueCopyConstructor) {
-  MethodCallCounter original_counter;
-  base::optional<MethodCallCounter> test(original_counter);
-  EXPECT_EQ(0, test->default_constructor_calls());
-  EXPECT_EQ(1, test->copy_constructor_calls());
-  EXPECT_EQ(0, test->operator_equals_calls());
-}
-
-TEST(OptionalTest, CopyConstructorIsCalledByOptionalCopyConstructor) {
-  MethodCallCounter original_counter;
-  base::optional<MethodCallCounter> test1(original_counter);
-  base::optional<MethodCallCounter> test2(test1);
-  EXPECT_EQ(0, test2->default_constructor_calls());
-  EXPECT_EQ(1, test2->copy_constructor_calls());
-  EXPECT_EQ(0, test2->operator_equals_calls());
-}
-
-TEST(OptionalTest, CopyConstructorIsCalledByValueAssignment) {
-  MethodCallCounter original_counter;
-  base::optional<MethodCallCounter> test;
-  test = original_counter;
-  EXPECT_EQ(0, test->default_constructor_calls());
-  EXPECT_EQ(1, test->copy_constructor_calls());
-  EXPECT_EQ(0, test->operator_equals_calls());
-}
-
-TEST(OptionalTest, CopyConstructorIsCalledByOptionalAssignment) {
-  MethodCallCounter original_counter;
-  base::optional<MethodCallCounter> test1(original_counter);
-  base::optional<MethodCallCounter> test2;
-  test2 = test1;
-  EXPECT_EQ(0, test2->default_constructor_calls());
-  EXPECT_EQ(1, test2->copy_constructor_calls());
-  EXPECT_EQ(0, test2->operator_equals_calls());
-}
-
-TEST(OptionalTest, AssignmentIsCalledByValueAssignment) {
-  MethodCallCounter original_counter;
-  base::optional<MethodCallCounter> test(original_counter);
-  test = original_counter;
-  EXPECT_EQ(0, test->default_constructor_calls());
-  EXPECT_EQ(1, test->copy_constructor_calls());
-  EXPECT_EQ(1, test->operator_equals_calls());
-}
-
-TEST(OptionalTest, AssignmentIsCalledByOptionalAssignment) {
-  MethodCallCounter original_counter;
-  base::optional<MethodCallCounter> test1(original_counter);
-  base::optional<MethodCallCounter> test2(original_counter);
-  test2 = test1;
-  EXPECT_EQ(0, test2->default_constructor_calls());
-  EXPECT_EQ(1, test2->copy_constructor_calls());
-  EXPECT_EQ(1, test2->operator_equals_calls());
-}
-
-TEST(OptionalTest, EnsureSelfAssignmentIsOkay) {
-  // Ensure that values are as we expect them to be.
-  OptionalInt test1(5);
-
-  test1 = test1;
-
-  EXPECT_FALSE(!test1);
-  EXPECT_EQ(5, test1.value());
-
-  // Ensure that the methods we expect to be called are actually called.
-  base::optional<MethodCallCounter> test2(base::in_place);
-
-  test2 = test2;
-
-  EXPECT_EQ(1, test2->default_constructor_calls());
-  EXPECT_EQ(0, test2->copy_constructor_calls());
-  EXPECT_EQ(1, test2->operator_equals_calls());
-}
-
-namespace {
-
-// Helper classes to ensure that we can assign different values to optionals
-// so long as the wrapped value can be assigned and constructed from the other.
-struct XType {
-  XType(int number) { number_ = number; }
-
-  int number_;
-};
-
-struct YType {
-  explicit YType(int number) { number_ = number; }
-
-  explicit YType(const XType& x_type) { number_ = x_type.number_; }
-
-  YType& operator=(const XType& x_type) {
-    number_ = x_type.number_;
-    return *this;
-  }
-
-  int number_;
-};
-
-}  // namespace
-
-TEST(OptionalTest, CopyConstructorIsCalledByValueAssignmentFromOtherType) {
-  XType x_type(1);
-  base::optional<YType> test;
-  test = x_type;
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(1, test->number_);
-}
-
-TEST(OptionalTest, AssignmentIsCalledByValueAssignmentFromOtherType) {
-  XType x_type(1);
-  base::optional<YType> test(base::in_place, 2);
-  test = x_type;
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(1, test->number_);
-}
-
-TEST(OptionalTest, TestMakeOptional) {
-  OptionalInt test = base::make_optional(5);
-
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(5, test.value());
-}
-
-TEST(OptionalTest, ValueOrTest) {
-  OptionalInt test;
-
-  EXPECT_EQ(4, test.value_or(4));
-
-  test = 2;
-
-  EXPECT_EQ(2, test.value_or(4));
-}
-
-TEST(OptionalTest, ConstOptionalsTest) {
-  const OptionalInt test1(5);
-
-  EXPECT_FALSE(!test1);
-  EXPECT_EQ(5, test1.value());
-  EXPECT_EQ(5, *test1);
-
-  const OptionalInt test2(test1);
-
-  EXPECT_FALSE(!test2);
-  EXPECT_EQ(5, test2.value());
-  EXPECT_EQ(5, *test2);
-
-  OptionalInt test3;
-  test3 = test2;
-
-  EXPECT_FALSE(!test3);
-  EXPECT_EQ(5, test3.value());
-  EXPECT_EQ(5, *test3);
-}
-
-TEST(OptionalTest, EmplaceInt) {
-  OptionalInt test;
-
-  test.emplace(5);
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(5, test.value());
-}
-
-TEST(OptionalTest, EmplaceWithDefaultConstructor) {
-  base::optional<MethodCallCounter> test;
-  test.emplace();
-
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(1, test->default_constructor_calls());
-  EXPECT_EQ(0, test->copy_constructor_calls());
-  EXPECT_EQ(0, test->operator_equals_calls());
-}
-
-namespace {
-
-class NoDefaultOrCopyConstructor {
- public:
-  NoDefaultOrCopyConstructor(int x, int y) {
-    x_ = x;
-    y_ = y;
-  }
-
-  int x() const { return x_; }
-  int y() const { return y_; }
-
- private:
-  NoDefaultOrCopyConstructor();
-  NoDefaultOrCopyConstructor(const NoDefaultOrCopyConstructor&);
-
-  int x_;
-  int y_;
-};
-
-}  // namespace
-
-TEST(OptionalTest, EmplaceObjectWithNoDefaultOrCopyConstructor) {
-  base::optional<NoDefaultOrCopyConstructor> test;
-  test.emplace(1, 2);
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(1, test->x());
-  EXPECT_EQ(2, test->y());
-}
-
-namespace {
-
-class NonConstPointerInConstructor {
- public:
-  explicit NonConstPointerInConstructor(int* param) { param_ = param; }
-
-  void SetParam(int value) { *param_ = value; }
-
- private:
-  int* param_;
-};
-
-}  // namespace
-
-TEST(OptionalTest, EmplaceObjectWithNonConstPointer) {
-  int value = 0;
-  base::optional<NonConstPointerInConstructor> test;
-  test.emplace(&value);
-  test->SetParam(5);
-  EXPECT_EQ(5, value);
-}
-
-TEST(OptionalTest, ForwardingConstructorInt) {
-  OptionalInt test(base::in_place, 5);
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(5, test.value());
-}
-
-TEST(OptionalTest, ForwardingConstructorWithDefaultConstructor) {
-  base::optional<MethodCallCounter> test(base::in_place);
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(1, test->default_constructor_calls());
-  EXPECT_EQ(0, test->copy_constructor_calls());
-  EXPECT_EQ(0, test->operator_equals_calls());
-}
-
-TEST(OptionalTest, ForwardingConstructorObjectWithNoDefaultOrCopyConstructor) {
-  base::optional<NoDefaultOrCopyConstructor> test(base::in_place, 1, 2);
-  EXPECT_FALSE(!test);
-  EXPECT_EQ(1, test->x());
-  EXPECT_EQ(2, test->y());
-}
-
-TEST(OptionalTest, ForwardingConstructorObjectWithNonConstPointer) {
-  int value = 0;
-  base::optional<NonConstPointerInConstructor> test(base::in_place, &value);
-  test->SetParam(5);
-  EXPECT_EQ(5, value);
-}
-
-namespace {
-typedef base::optional<std::string> OptionalString;
-}  // namespace
-
-TEST(OptionalTest, OptionalStringTest) {
-  {
-    OptionalString test;
-    EXPECT_TRUE(!test);
-
-    test = std::string("foo");
-    EXPECT_STREQ("foo", test->c_str());
-  }
-
-  {
-    OptionalString test(std::string("foo"));
-    EXPECT_FALSE(!test);
-
-    EXPECT_STREQ("foo", test->c_str());
-    OptionalString test2(test);
-    EXPECT_STREQ("foo", test2->c_str());
-  }
-}
-
-TEST(OptionalTest, OptionalStringInSetTest) {
-  // Optional strings in map test
-  std::set<OptionalString> optional_string_set;
-
-  optional_string_set.insert(std::string("foo"));
-  optional_string_set.insert(std::string("bar"));
-
-  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
-              optional_string_set.end());
-  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
-              optional_string_set.end());
-  EXPECT_FALSE(optional_string_set.find(OptionalString()) !=
-               optional_string_set.end());
-
-  optional_string_set.insert(OptionalString());
-
-  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
-              optional_string_set.end());
-  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
-              optional_string_set.end());
-  EXPECT_TRUE(optional_string_set.find(OptionalString()) !=
-              optional_string_set.end());
-}
-
-TEST(OptionalTest, StdVectorOfOptionalsWorksFine) {
-  std::vector<OptionalInt> test_vector;
-
-  test_vector.reserve(test_vector.capacity() + 1);
-  test_vector.resize(test_vector.capacity());
-
-  // Make sure all current optionals are disengaged.
-  for (std::vector<OptionalInt>::const_iterator iter = test_vector.begin();
-       iter != test_vector.end();
-       ++iter) {
-    EXPECT_TRUE(!*iter);
-  }
-  EXPECT_TRUE(!test_vector[0]);
-
-  test_vector.clear();
-  test_vector.resize(test_vector.capacity() + 1, 5);
-  for (std::vector<OptionalInt>::const_iterator iter = test_vector.begin();
-       iter != test_vector.end();
-       ++iter) {
-    ASSERT_FALSE(!*iter);
-    EXPECT_EQ(5, **iter);
-  }
-  ASSERT_FALSE(!test_vector[0]);
-  EXPECT_EQ(5, *test_vector[0]);
-
-  test_vector.push_back(8);
-  ASSERT_FALSE(!test_vector.back());
-  EXPECT_EQ(8, *test_vector.back());
-}
-
-TEST(OptionalTest, OptionalStringInHashSetTest) {
-  // Optional strings in map test
-  base::hash_set<OptionalString> optional_string_set;
-
-  optional_string_set.insert(std::string("foo"));
-  optional_string_set.insert(std::string("bar"));
-
-  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
-              optional_string_set.end());
-  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
-              optional_string_set.end());
-  EXPECT_FALSE(optional_string_set.find(OptionalString()) !=
-               optional_string_set.end());
-
-  optional_string_set.insert(OptionalString());
-
-  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
-              optional_string_set.end());
-  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
-              optional_string_set.end());
-  EXPECT_TRUE(optional_string_set.find(OptionalString()) !=
-              optional_string_set.end());
-}
-
-namespace {
-
-OptionalInt ConditionallyMakeOptional(bool should_make, int value) {
-  if (should_make) {
-    return OptionalInt(value);
-  } else {
-    return base::nullopt;
-  }
-}
-
-}  // namespace
-
-TEST(OptionalTest, OptionalCanBeReturnedFromFunction) {
-  OptionalInt test1 = ConditionallyMakeOptional(true, 5);
-  ASSERT_FALSE(!test1);
-  EXPECT_EQ(5, test1.value());
-
-  OptionalInt test2 = ConditionallyMakeOptional(false, 5);
-  EXPECT_TRUE(!test2);
-}
diff --git a/src/base/state_machine_shell.h b/src/base/state_machine_shell.h
index 8d3200f..38371d3 100644
--- a/src/base/state_machine_shell.h
+++ b/src/base/state_machine_shell.h
@@ -5,547 +5,14 @@
 #ifndef BASE_STATE_MACHINE_SHELL_H_
 #define BASE_STATE_MACHINE_SHELL_H_
 
-#include <stdint.h>
-
-#include <algorithm>
-#include <climits>
-#include <queue>
-#include <string>
-#include <vector>
-
-#include "base/base_export.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/logging.h"
-#include "base/optional.h"
+#include "starboard/common/state_machine.h"
 
 namespace base {
+using ::starboard::StateMachineBase;
+using ::starboard::StateMachine;
 
-// An approximate implementation of a run-to-completion (RTC) Hierarchical State
-// Machine (HSM), supporting most UML Statechart semantics.
-//
-// Some good background information on UML Statecharts, mostly written by Miro
-// Samek:
-// http://en.wikipedia.org/wiki/UML_state_machine
-//
-// And Miro Samek's 3-part article on practical UML Statecharts:
-// http://www.embedded.com/design/prototyping-and-development/4008247/A-crash-course-in-UML-state-machines-Part-1
-//
-// This class does not provide any intrinsic support for "orthogonal regions,"
-// "extended state," or "deferred events." "Guard conditions" are easily
-// implemented by the client directly in the event handler. It also minorly
-// deviates from the UML Statechart specification by calling transition handlers
-// before exiting the current states. This is because this implementation uses a
-// simplification which combines the transition handlers and the
-// state-event-transition map into a single function (HandleUserStateEvent).
-//
-// This class is not thread-safe. It is the user's responsibility to synchronize
-// calls into the state machine, either with locks or by simply using from a
-// single thread.
-//
-// Terse suggestions for using this class:
-//   * Use the templated StateMachineShell wrapper class instead of this class
-//     directly.
-//   * Define two enums, one for your states, and one for your events.
-//   * Subclass to define your state machine and event handlers.
-//   * Avoid directly exposing or passing around state machines (wrap instead).
-//     Handle() is not a great public interface.
-//   * Include your state machine in another class as a private by-value member.
-//   * Synchronize access to the state machine.
-//   * Prefer writing state machine event handlers over checking if the machine
-//     IsIn() a particular state.
-//   * Convert public methods into events, get into the state machine as quickly
-//     as possible.
-//   * You only need to define a state when you are going to leave the state
-//     machine in a particular condition where events can occur.
-//   * Create a superstate when you have an event you want to handle the same
-//     way for a collection of states.
-//   * When managing resources, create a state or superstate that represents the
-//     acquisition of that resource, and release the resource in the Exit
-//     handler.
-//
-// Some Definitions:
-//   Simple State      - A State with no substates. The state machine is always
-//                       left in exactly one Simple State.
-//   Composite State   - A State with at least one substate.
-//   Guard Condition   - An external condition on which an event handler
-//                       branches.
-//   Run-To-Completion - A property specifying that the state machine handles
-//                       one event at a time, and no events are handled until
-//                       the previous event is done being handled.
-//
-// See the unittests for this class for a contrived example state machine
-// implementation.
-class BASE_EXPORT StateMachineBaseShell {
- public:
-  // --- Nested Types and Constants ---
-
-  typedef uint32_t State;
-  typedef uint32_t Event;
-
-  typedef optional<State> StateN;
-  typedef optional<Event> EventN;
-
-  // --- Public Methods ---
-
-  // Constructor with name. The name is helpful for debugging.
-  explicit StateMachineBaseShell(const std::string &name);
-  virtual ~StateMachineBaseShell() { }
-
-  // Enters the initial state, as specified by |GetUserInitialState()| and
-  // follows the initial substates down to the first simple (childless) state
-  // found. Idempotent. Will happen automatically on the first |Handle()| call,
-  // if not done explicitly.
-  void Initialize();
-
-  // Gets the name of this state machine, for logging purposes.
-  const char *name() const {
-    return name_.c_str();
-  }
-
-  // Gets a version number that increases monotonically with each state
-  // transition (unless it overflows |uint64_t|). This can be useful for timers
-  // and asynchronous events that only want to do their action if the state has
-  // not changed since they were queued.
-  //
-  // The state version changes exactly once per transition. In other words, it
-  // doesn't change for every Enter and Exit for a transition.
-  uint64_t version() const {
-    return version_;
-  }
-
-  // Gets the simplest current state that this state machine is in. To check if
-  // the state machine is in a particular composite state, use |IsIn()|. Returns
-  // null if uninitialized.
-  StateN state() const {
-    return state_;
-  }
-
-  // Reports whether the state machine is currently in the given state. A state
-  // machine is considered to be "in" the current simple state, but also "in"
-  // all superstates of the current simple state.
-  bool IsIn(State state) const;
-
-  // Gets a printable string for the given state.
-  const char *GetStateString(StateN state) const;
-
-  // Gets a printable string for the given event.
-  const char *GetEventString(EventN event) const;
-
-  // Handles the given event in the context of the state machine's current
-  // state, executing the appropriate event handlers and performing any
-  // precipitate state transitions. If called reentrantly, the event will be
-  // queued until the current event has run to completion.
-  //
-  // |data| is a way to pass auxiliary data to the event handler. No retention
-  // or ref-counting is done on that data, it is simply passed through to the
-  // event handler. Since |Handle()| will queue the event if (and only if)
-  // called from an event handler, it is up to the caller to ensure the lifetime
-  // of whatever is passed in here.
-  void Handle(Event event, void *data = NULL);
-
-
- protected:
-  // --- Protected Nested Types ---
-
-  // A type that can be returned from a state-event handler, which will be
-  // coerced into the appropriate Result structure.
-  enum HandledState {
-    // The event handler returns this to say that the handler consume the event,
-    // but caused no state transition.
-    kHandled,
-
-    // The event handler returns this to say that the handler did not consume
-    // the event, and it should bubble up to the parent state, if any.
-    kNotHandled,
-  };
-
-  // Structure that handlers return, allowing them to cause state transitions or
-  // prevent the event from bubbling. State-event handlers may just return a
-  // HandledState or a state to transition to, and it will be coerced into this
-  // structure.
-  struct Result {
-    // Default constructor is unhandled.
-    Result()
-        : is_transition(false),
-          is_external(false),
-          is_handled(false) { }
-
-    // The no-transition constructor. Non-explicit so that the implementor of
-    // |HandleUserStateEvent()| just needs to return a |HandledState| and it
-    // does the right thing.
-    Result(HandledState handled)
-        : is_transition(false),
-          is_external(false),
-          is_handled(handled == kHandled) { }
-
-    // The state transition constructor. This implies that the event was
-    // handled. Non-explicit so that the implementor of |HandleUserStateEvent()|
-    // just needs to return a State and it does a non-external transition.
-    Result(State transition_target, bool external = false)
-        : target(transition_target),
-          is_transition(true),
-          is_external(external),
-          is_handled(true) { }
-
-    Result &operator=(HandledState rhs) {
-      target = base::nullopt;
-      is_transition = false;
-      is_external = false;
-      is_handled = (rhs == kHandled);
-      return *this;
-    }
-
-    Result &operator=(State transition_target) {
-      target = transition_target;
-      is_transition = true;
-      is_external = false;
-      is_handled = true;
-      return *this;
-    }
-
-    // State to transition to. Only valid if is_transition is true.
-    StateN target;
-
-    // Whether this result indicates a state transition.
-    bool is_transition;
-
-    // Whether the specified transition is external. Only meaningful if
-    // is_transition is true.
-    //
-    // For more on state transitions, see:
-    // http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
-    bool is_external;
-
-    // Whether the event was handled by the handler. If true, consumes the
-    // event, and prevents bubbling up to superstates. False propagates the
-    // event to superstates.
-    bool is_handled;
-  };
-
-
-  // --- Implementation Interface for Subclasses ---
-
-  // Abstract method for subclasses to define the state hierarchy. It is
-  // recommended that all user-defined states be descendents of a single "top"
-  // state.
-  virtual StateN GetUserParentState(State state) const = 0;
-
-  // Abstract method for subclasses to define the initial substate of their
-  // states, which is required for all complex states. If a state is a simple
-  // state (no substates), returns null.
-  virtual StateN GetUserInitialSubstate(State state) const = 0;
-
-  // Abstract method for subclasses to define the initial (top) state of their
-  // state machine. This must be a state that has no parent. This state will be
-  // entered upon calling |Initialize()|.
-  virtual State GetUserInitialState() const = 0;
-
-  // Optional method for subclasses to define strings for their states, solely
-  // used for debugging purposes.
-  virtual const char *GetUserStateString(State state) const { return NULL; }
-
-  // Optional method for subclasses to define strings for their events, solely
-  // used for debugging purposes.
-  virtual const char *GetUserEventString(Event event) const { return NULL; }
-
-  // Abstract method for subclasses to define handlers for events in given
-  // states. This method must return either:
-  //   a) a state to transition to, meaning the event was handled and caused
-  //      a transition
-  //   b) |kHandled| meaning the event was handled but no transition occurred
-  //   c) |kNotHandled|, meaning the event should bubble up to the parent state
-  //   d) an explicit Result structure, mainly for external transitions.
-  //
-  // Implementations wishing to catch all unhandled events may do so in their
-  // top state.
-  //
-  // This method is generally implemented as a nested switch statement, with the
-  // outer switch on |state| and the inner switch on |event|. You may want to
-  // break this apart into per-state or per-state-event functions for
-  // readability and maintainability.
-  virtual Result HandleUserStateEvent(State state, Event event, void *data) = 0;
-
-  // Abstract method for subclasses to define state entry behaviors.
-  virtual void HandleUserStateEnter(State state) = 0;
-
-  // Abstract method for subclasses to define state exit behaviors.
-  virtual void HandleUserStateExit(State state) = 0;
-
-
-  // --- Helper Methods for Subclasses ---
-
-  // Subclasses can call this method to turn on logging. Logging is opt-in,
-  // because it can be very verbose, and is likely only useful during
-  // development of a particular state machine.
-  void EnableLogging() {
-    should_log_ = true;
-  }
-
-
- private:
-  // --- Private Helper Methods ---
-
-  // Gets the parent state of the given state.
-  StateN GetParentState(StateN state) const;
-
-  // Gets the initial substate of given state.
-  StateN GetInitialSubstate(StateN state) const;
-
-  // Handles all queued events until there are no more to run. Event handlers
-  // may queue more events by calling |Handle()|, and this method will also
-  // process all precipitate events.
-  void HandleQueuedEvents();
-
-  // Workhorse method for handling a single event. Event handling is queued by
-  // binding the parameters to this method and queueing the resulting Closure.
-  void HandleOneEvent(Event event, void *data);
-
-  // Gets the path from the Top state to the given |state|, storing it in the
-  // given |out_path| array, up to |max_depth| entries. If specified,
-  // |out_depth| will be set to the number of valid states that fit in the given
-  // array.
-  void GetPath(State state, size_t max_depth, State *out_path,
-               size_t *out_depth) const;
-
-  // Transitions between the given source and target states, assuming that the
-  // source state is in the current state path to the Top state. The source
-  // state is the state whose handler generated the transition.
-  //
-  // See: http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
-  void Transition(Event event, State source, State target, bool is_external);
-
-  // Follows the initial substates from the current state until it reaches a
-  // simple state.
-  void FollowInitialSubstates();
-
-  // Enters the given state.
-  void EnterState(State state);
-
-  // Exits the current state to its parent.
-  void ExitCurrentState();
-
-
-  // --- Members ---
-
-  // The name of this state machine, for debugging purposes.
-  const std::string name_;
-
-  // The current state of this state machine. Null until initialized.
-  StateN state_;
-
-  // The unique version of this state machine's state, updated on every
-  // transition.
-  uint64_t version_;
-
-  // Queue of events to handle once the current event is done being
-  // handled. Should always be empty unless |is_handling_| is true.
-  std::queue<Closure> event_queue_;
-
-  // Whether this state machine is actively handling an event. Used to detect
-  // reentrant calls to |Handle()|.
-  bool is_handling_;
-
-  // Whether the state machine has been initialized into its initial state
-  // yet. Used to make |Initialize()| idempotent.
-  bool is_initialized_;
-
-  // Whether the state machine should DLOG information about state transitions.
-  bool should_log_;
-};
-
-
-// A convenience template wrapper for StateMachineBaseShell. See the above class
-// for complete documentation. Basically, you define your states and events as
-// two enums, and then pass them as template args to this template class. Your
-// state machine should then subclass this template class. It then does the work
-// of casting and converting back and forth from your enums to
-// StateMachineBaseShell's numeric State and Event definitions.
-//
-// All the methods in this class, protected and public, match the description
-// and behavioral contracts of the equivalently named method in
-// StateMachineBaseShell.
 template <typename StateEnum, typename EventEnum>
-class BASE_EXPORT StateMachineShell {
- public:
-  // --- Nested Types and Constants ---
-
-  typedef optional<StateEnum> StateEnumN;
-  typedef optional<EventEnum> EventEnumN;
-
-  explicit StateMachineShell(const std::string &name)
-      : machine_(this, name) { }
-  virtual ~StateMachineShell() { }
-
-  void Initialize() {
-    machine_.Initialize();
-  }
-
-  const char *name() const {
-    return machine_.name();
-  }
-
-  uint64_t version() const {
-    return machine_.version();
-  }
-
-  StateEnumN state() const {
-    BaseStateN wrappedState = machine_.state();
-    return (wrappedState ? static_cast<StateEnum>(*wrappedState)
-                         : StateEnumN());
-  }
-
-  bool IsIn(StateEnum state) const {
-    return machine_.IsIn(static_cast<BaseState>(state));
-  }
-
-  const char *GetStateString(StateEnumN state) const {
-    return machine_.GetStateString(state ? static_cast<BaseState>(*state)
-                                         : BaseStateN());
-  }
-
-  const char *GetEventString(EventEnumN event) const {
-    return machine_.GetEventString(event ? static_cast<BaseEvent>(*event)
-                                         : BaseEventN());
-  }
-
-  void Handle(EventEnum event, void *data = NULL) {
-    machine_.Handle(static_cast<BaseEvent>(event), data);
-  }
-
- protected:
-  // See the other HandledState in StateMachineBaseShell.
-  enum HandledState {
-    kHandled,
-    kNotHandled,
-  };
-
-  // See the other Result in StateMachineBaseShell.
-  struct Result {
-    // Not explicit on purpose, please see the other Result for justification.
-    Result(HandledState handled)
-        : target(),
-          is_transition(false),
-          is_external(false),
-          is_handled(handled == kHandled) { }
-
-    // Not explicit on purpose, please see the other Result for justification.
-    Result(StateEnum transition_target, bool external = false)
-        : target(transition_target),
-          is_transition(true),
-          is_external(external),
-          is_handled(true) { }
-
-    Result &operator=(HandledState rhs) {
-      target = base::nullopt;
-      is_transition = false;
-      is_external = false;
-      is_handled = (rhs == kHandled);
-      return *this;
-    }
-
-    Result &operator=(StateEnum transition_target) {
-      target = transition_target;
-      is_transition = true;
-      is_external = false;
-      is_handled = true;
-      return *this;
-    }
-
-    StateEnumN target;
-    bool is_transition;
-    bool is_external;
-    bool is_handled;
-  };
-
-  virtual StateEnumN GetUserParentState(StateEnum state) const = 0;
-  virtual StateEnumN GetUserInitialSubstate(StateEnum state) const = 0;
-  virtual StateEnum GetUserInitialState() const = 0;
-  virtual const char *GetUserStateString(StateEnum state) const { return NULL; }
-  virtual const char *GetUserEventString(EventEnum event) const { return NULL; }
-  virtual Result HandleUserStateEvent(StateEnum state,
-                                      EventEnum event,
-                                      void *data) = 0;
-  virtual void HandleUserStateEnter(StateEnum state) = 0;
-  virtual void HandleUserStateExit(StateEnum state) = 0;
-
-  void EnableLogging() {
-    machine_.EnableLoggingPublic();
-  }
-
- private:
-  typedef StateMachineBaseShell::State BaseState;
-  typedef StateMachineBaseShell::Event BaseEvent;
-  typedef StateMachineBaseShell::StateN BaseStateN;
-  typedef StateMachineBaseShell::EventN BaseEventN;
-
-  // Private contained subclass that forwards and adapts all virtual methods
-  // into this class's equivalent virtual methods.
-  class WrappedMachine : public StateMachineBaseShell {
-   public:
-    WrappedMachine(StateMachineShell<StateEnum, EventEnum> *wrapper,
-                   const std::string &name)
-        : StateMachineBaseShell(name),
-          wrapper_(wrapper) {
-    }
-
-    StateN GetUserParentState(State state) const OVERRIDE {
-      StateEnumN result =
-          wrapper_->GetUserParentState(static_cast<StateEnum>(state));
-      return (result ? static_cast<State>(*result) : StateN());
-    }
-
-    StateN GetUserInitialSubstate(State state) const OVERRIDE {
-      StateEnumN result =
-          wrapper_->GetUserInitialSubstate(static_cast<StateEnum>(state));
-      return (result ? static_cast<State>(*result) : StateN());
-    }
-
-    State GetUserInitialState() const OVERRIDE {
-      return static_cast<State>(wrapper_->GetUserInitialState());
-    }
-
-    const char *GetUserStateString(State state) const OVERRIDE {
-      return wrapper_->GetUserStateString(static_cast<StateEnum>(state));
-    }
-
-    const char *GetUserEventString(Event event) const OVERRIDE {
-      return wrapper_->GetUserEventString(static_cast<EventEnum>(event));
-    }
-
-    Result HandleUserStateEvent(State state, Event event, void *data) OVERRIDE {
-      typename StateMachineShell<StateEnum, EventEnum>::Result result =
-          wrapper_->HandleUserStateEvent(static_cast<StateEnum>(state),
-                                         static_cast<EventEnum>(event),
-                                         data);
-      if (result.is_transition) {
-        return Result(static_cast<State>(*result.target),
-                      result.is_external);
-      }
-
-      return result.is_handled ? kHandled : kNotHandled;
-    }
-
-    void HandleUserStateEnter(State state) OVERRIDE {
-      wrapper_->HandleUserStateEnter(static_cast<StateEnum>(state));
-    }
-
-    void HandleUserStateExit(State state) OVERRIDE {
-      wrapper_->HandleUserStateExit(static_cast<StateEnum>(state));
-    }
-
-    // A public exposure of EnableLogging so the wrapper can access it. Since
-    // this class is private to the wrapper, it is the only one who can see it.
-    void EnableLoggingPublic() {
-      EnableLogging();
-    }
-
-   private:
-    StateMachineShell<StateEnum, EventEnum> *wrapper_;
-  };
-
-  WrappedMachine machine_;
-};
+using StateMachineShell = StateMachine<StateEnum, EventEnum>;
 
 }  // namespace base
 
diff --git a/src/build/common.gypi b/src/build/common.gypi
index 901b1eb..7078e81 100644
--- a/src/build/common.gypi
+++ b/src/build/common.gypi
@@ -2,6 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+#####################################################################
+# If you modify this file, PLEASE REMEMBER TO UPDATE
+# //cobalt/build/config/base.gni or //starboard/build/config/base.gni
+# AS WELL
+#####################################################################
+
 # IMPORTANT:
 # Please don't directly include this file if you are building via gyp_cobalt,
 # since gyp_cobalt is automatically forcing its inclusion.
@@ -38,12 +44,6 @@
       'target_arch%': '<(target_arch)',
       'android_build_type%': '<(android_build_type)',
 
-      # Set to 1 to enable dcheck in release without having to use the flag.
-      'dcheck_always_on%': 0,
-
-      # Python version.
-      'python_ver%': '2.6',
-
       # Set ARM-v7 compilation flags
       'armv7%': 0,
 
@@ -74,24 +74,10 @@
       # See http://clang.llvm.org/docs/ThreadSanitizer.html
       'tsan%': 0,
 
-      # Use a modified version of Clang to intercept allocated types and sizes
-      # for allocated objects. clang_type_profiler=1 implies clang=1.
-      # See http://dev.chromium.org/developers/deep-memory-profiler/cpp-object-type-identifier
-      # TODO(dmikurube): Support mac.  See http://crbug.com/123758#c11
-      'clang_type_profiler%': 0,
-
-      # Set to true to instrument the code with function call logger.
-      # See src/third_party/cygprofile/cyg-profile.cc for details.
-      'order_profiling%': 0,
-
       # Set this to true when building with Clang.
       # See http://code.google.com/p/chromium/wiki/Clang for details.
       'clang%': 0,
 
-      # Set to "tsan", "memcheck", or "drmemory" to configure the build to work
-      # with one of those tools.
-      'build_for_tool%': '',
-
       'os_posix%': 0,
       'os_bsd%': 0,
     },
@@ -107,16 +93,12 @@
     'library%': 'static_library',
     'os_bsd%': '<(os_bsd)',
     'os_posix%': '<(os_posix)',
-    'dcheck_always_on%': '<(dcheck_always_on)',
-    'python_ver%': '<(python_ver)',
     'armv7%': '<(armv7)',
     'arm_neon%': '<(arm_neon)',
     'sysroot%': '<(sysroot)',
     'component%': '<(component)',
     'asan%': '<(asan)',
     'tsan%': '<(tsan)',
-    'clang_type_profiler%': '<(clang_type_profiler)',
-    'order_profiling%': '<(order_profiling)',
     'use_system_libjpeg%': '<(use_system_libjpeg)',
     'android_build_type%': '<(android_build_type)',
 
@@ -143,10 +125,6 @@
     'clang_load%': '',
     'clang_add_plugin%': '',
 
-    # Enable sampling based profiler.
-    # See http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html
-    'profiling%': '0',
-
     # Set Thumb compilation flags.
     'arm_thumb%': 0,
 
@@ -163,8 +141,6 @@
     # whether warnings are treated as errors.
     'chromium_code%': 0,
 
-    'release_valgrind_build%': 0,
-
     # TODO(thakis): Make this a blacklist instead, http://crbug.com/101600
     'enable_wexit_time_destructors%': 0,
 
@@ -241,59 +217,6 @@
       ['tsan==1', {
         'clang%': 1,
       }],
-
-      # On valgrind bots, override the optimizer settings so we don't inline too
-      # much and make the stacks harder to figure out.
-      #
-      # TODO(rnk): Kill off variables that no one else uses and just implement
-      # them under a build_for_tool== condition.
-      ['build_for_tool=="memcheck" or build_for_tool=="tsan"', {
-        # gcc flags
-        'mac_debug_optimization': '1',
-        'mac_release_optimization': '1',
-        'release_optimize': '1',
-        'no_gc_sections': 1,
-        'debug_extra_cflags': '-g -fno-inline -fno-omit-frame-pointer '
-                              '-fno-builtin -fno-optimize-sibling-calls',
-        'release_extra_cflags': '-g -fno-inline -fno-omit-frame-pointer '
-                                '-fno-builtin -fno-optimize-sibling-calls',
-
-        # MSVS flags for TSan on Pin and Windows.
-        'win_debug_RuntimeChecks': '0',
-        'win_debug_disable_iterator_debugging': '1',
-        'win_debug_Optimization': '1',
-        'win_debug_InlineFunctionExpansion': '0',
-        'win_release_InlineFunctionExpansion': '0',
-        'win_release_OmitFramePointers': '0',
-
-        'linux_use_tcmalloc': 1,
-        'release_valgrind_build': 1,
-        'werror': '',
-        'component': 'static_library',
-        'use_system_zlib': 0,
-      }],
-
-      # Build tweaks for DrMemory.
-      # TODO(rnk): Combine with tsan config to share the builder.
-      # http://crbug.com/108155
-      ['build_for_tool=="drmemory"', {
-        # These runtime checks force initialization of stack vars which blocks
-        # DrMemory's uninit detection.
-        'win_debug_RuntimeChecks': '0',
-        # Iterator debugging is slow.
-        'win_debug_disable_iterator_debugging': '1',
-        # Try to disable optimizations that mess up stacks in a release build.
-        # DrM-i#1054 (http://code.google.com/p/drmemory/issues/detail?id=1054)
-        # /O2 and /Ob0 (disable inline) cannot be used together because of a
-        # compiler bug, so we use /Ob1 instead.
-        'win_release_InlineFunctionExpansion': '1',
-        'win_release_OmitFramePointers': '0',
-        # Ditto for debug, to support bumping win_debug_Optimization.
-        'win_debug_InlineFunctionExpansion': 0,
-        'win_debug_OmitFramePointers': 0,
-        # Keep the code under #ifndef NVALGRIND.
-        'release_valgrind_build': 1,
-      }],
     ],
   },
   'target_defaults': {
@@ -336,8 +259,6 @@
       'release_extra_cflags%': '',
       'debug_extra_cflags%': '',
 
-      'release_valgrind_build%': '<(release_valgrind_build)',
-
       # the non-qualified versions are widely assumed to be *nix-only
       'win_release_extra_cflags%': '',
       'win_debug_extra_cflags%': '',
@@ -379,12 +300,6 @@
       ['component=="shared_library"', {
         'defines': ['COMPONENT_BUILD'],
       }],
-      ['profiling==1', {
-        'defines': ['ENABLE_PROFILING=1'],
-      }],
-      ['dcheck_always_on!=0', {
-        'defines': ['DCHECK_ALWAYS_ON=1'],
-      }],  # dcheck_always_on!=0
     ],  # conditions for 'target_defaults'
     'target_conditions': [
       ['enable_wexit_time_destructors==1', {
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index 2abcfc8..9d943ae 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -40,6 +40,14 @@
    `kSbInputEventTypeMove` events.  These will be passed into the WebModule to
    be processed by JavaScript.
 
+ - **Custom Web Extension Support**
+
+   Cobalt now allows platforms to inject a custom namespace property into
+   the JavaScript global `window` object visible to the web apps.  This allows
+   for custom web apps to interface with custom C++ code written outside of
+   Cobalt common code.  See
+   [doc/webapi_extension.md](doc/webapi_extension.md) for more details.
+
  - **Playback Rate**
 
    Cobalt now supports adjusting the video playback rate.
diff --git a/src/cobalt/accessibility/screen_reader_tests.cc b/src/cobalt/accessibility/screen_reader_tests.cc
index 2814b21..9adc74d 100644
--- a/src/cobalt/accessibility/screen_reader_tests.cc
+++ b/src/cobalt/accessibility/screen_reader_tests.cc
@@ -104,7 +104,9 @@
 
   scoped_refptr<script::Wrappable> CreateWindowAttribute(
       const scoped_refptr<dom::Window>& window,
-      dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+      dom::MutationObserverTaskManager* mutation_observer_task_manager,
+      script::GlobalEnvironment* global_environment) {
+    UNREFERENCED_PARAMETER(global_environment);
     screen_reader_.reset(new accessibility::ScreenReader(
         window->document(), &tts_engine_, mutation_observer_task_manager));
     screen_reader_->set_enabled(GetParam().screen_reader_enabled);
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index f4f9f34..ccbcc85 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -41,6 +41,8 @@
         'event.h',
         'event_dispatcher.cc',
         'event_dispatcher.h',
+        'get_application_key.cc',
+        'get_application_key.h',
         'init_cobalt.cc',
         'init_cobalt.h',
         'language.cc',
diff --git a/src/cobalt/base/get_application_key.cc b/src/cobalt/base/get_application_key.cc
new file mode 100644
index 0000000..15e9175
--- /dev/null
+++ b/src/cobalt/base/get_application_key.cc
@@ -0,0 +1,60 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/base/get_application_key.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+
+namespace base {
+
+namespace {
+// The Application Origin is like the origin, but it supports nonstandard
+// schemes, and preserves the path.
+GURL GetApplicationOrigin(const GURL& url) {
+  if (!url.is_valid()) {
+    return GURL();
+  }
+
+  url_canon::Replacements<char> replacements;
+  replacements.ClearUsername();
+  replacements.ClearPassword();
+  replacements.ClearQuery();
+  replacements.ClearRef();
+
+  return url.ReplaceComponents(replacements);
+}
+}  // namespace
+
+optional<std::string> GetApplicationKey(const GURL& url) {
+  if (!url.is_valid()) {
+    return base::nullopt;
+  }
+
+  std::string raw_url = GetApplicationOrigin(url).spec();
+  std::string encoded_url;
+  base::Base64Encode(raw_url, &encoded_url);
+
+  // Make web-safe.
+  ReplaceChars(encoded_url, "/", "_", &encoded_url);
+  ReplaceChars(encoded_url, "+", "-", &encoded_url);
+
+  return encoded_url;
+}
+
+}  // namespace base
diff --git a/src/cobalt/base/get_application_key.h b/src/cobalt/base/get_application_key.h
new file mode 100644
index 0000000..ab9a995
--- /dev/null
+++ b/src/cobalt/base/get_application_key.h
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_GET_APPLICATION_KEY_H_
+#define COBALT_BASE_GET_APPLICATION_KEY_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "googleurl/src/gurl.h"
+
+namespace base {
+
+// Returns a filesystem-safe key that corresponds to the application whose start
+// URL is |url|. Returns nullopt if |url| is not a valid URL.
+optional<std::string> GetApplicationKey(const GURL& url);
+
+}  // namespace base
+
+#endif  // COBALT_BASE_GET_APPLICATION_KEY_H_
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 35d8e57..5c826de 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -64,6 +64,7 @@
     MacroOpWithNameOnly(hashchange)                                  \
     MacroOpWithNameOnly(keydown)                                     \
     MacroOpWithNameOnly(keypress)                                    \
+    MacroOpWithNameOnly(keystatuseschange)                           \
     MacroOpWithNameOnly(keyup)                                       \
     MacroOpWithNameOnly(load)                                        \
     MacroOpWithNameOnly(loadeddata)                                  \
diff --git a/src/cobalt/bindings/bindings.gypi b/src/cobalt/bindings/bindings.gypi
index 29c4d39..9260d08 100644
--- a/src/cobalt/bindings/bindings.gypi
+++ b/src/cobalt/bindings/bindings.gypi
@@ -553,6 +553,7 @@
         'inputs': [
           '<@(jinja_module_files)',
           '<(DEPTH)/cobalt/bindings/code_generator_cobalt.py',
+          '<(DEPTH)/cobalt/bindings/path_generator.py',
           '<@(code_generator_template_files)',
         ],
         'outputs': [
diff --git a/src/cobalt/bindings/code_generator_cobalt.py b/src/cobalt/bindings/code_generator_cobalt.py
index 7227af8..c9dd3ac 100644
--- a/src/cobalt/bindings/code_generator_cobalt.py
+++ b/src/cobalt/bindings/code_generator_cobalt.py
@@ -483,8 +483,7 @@
       referenced_interface_names.update(IdlType.callback_interfaces)
     else:
       # Build the set of referenced interfaces from this interface's members.
-      referenced_interface_names = set()
-      referenced_interface_names.update(
+      referenced_interface_names = set(
           get_interface_type_names_from_typed_objects(self.info_provider,
                                                       interface.attributes))
       referenced_interface_names.update(
diff --git a/src/cobalt/bindings/contexts.py b/src/cobalt/bindings/contexts.py
index c67a214..702090e 100644
--- a/src/cobalt/bindings/contexts.py
+++ b/src/cobalt/bindings/contexts.py
@@ -56,6 +56,15 @@
   return str(idl_literal)
 
 
+def get_dictionary_default_value(idl_type, idl_literal, name):
+  """Mapping to cobalt value filtering for dictionary acceptable values."""
+  if is_any_type(idl_type) and not idl_literal.is_null:
+    raise ValueError('Unsupported default value in dictionary: '
+                     '\'%s %s = %s\'. Only null default is supported.' %
+                     (idl_type, name, idl_literal))
+  return idl_literal_to_cobalt_literal(idl_type, idl_literal)
+
+
 def idl_primitive_type_to_cobalt(idl_type):
   """Map IDL primitive type to C++ type."""
   type_map = {
@@ -198,7 +207,7 @@
             not idl_type.is_callback_interface), 'Callback types not supported.'
     element_cobalt_type = self.idl_type_to_cobalt_type(
         self.resolve_typedef(element_idl_type))
-    return 'script::Sequence< %s >' % element_cobalt_type
+    return '::cobalt::script::Sequence< %s >' % element_cobalt_type
 
   def idl_union_type_to_cobalt(self, idl_type):
     """Map IDL union type to C++ union type implementation."""
@@ -216,8 +225,8 @@
         flattened_types.append(member)
 
     cobalt_types = [self.idl_type_to_cobalt_type(t) for t in flattened_types]
-    return 'script::UnionType%d<%s >' % (len(cobalt_types),
-                                         ', '.join(cobalt_types))
+    return '::cobalt::script::UnionType%d<%s >' % (len(cobalt_types),
+                                                   ', '.join(cobalt_types))
 
   def idl_type_to_cobalt_type(self, idl_type):
     """Map IDL type to C++ type."""
@@ -228,8 +237,8 @@
     elif idl_type.is_string_type:
       cobalt_type = 'std::string'
     elif idl_type.is_callback_interface:
-      cobalt_type = 'CallbackInterfaceTraits<%s >' % get_interface_name(
-          idl_type)
+      cobalt_type = '::cobalt::script::CallbackInterfaceTraits<%s >' % (
+          get_interface_name(idl_type))
     elif idl_type.is_interface_type:
       cobalt_type = 'scoped_refptr<%s>' % get_interface_name(idl_type)
     elif idl_type.is_union_type:
@@ -241,13 +250,13 @@
     elif idl_type.name == 'void':
       cobalt_type = 'void'
     elif is_object_type(idl_type):
-      cobalt_type = 'OpaqueHandle'
+      cobalt_type = '::cobalt::script::OpaqueHandle'
     elif is_any_type(idl_type):
-      cobalt_type = 'ValueHandle'
+      cobalt_type = '::cobalt::script::ValueHandle'
     elif idl_type.is_dictionary:
       cobalt_type = get_interface_name(idl_type)
     elif is_promise_type(idl_type):
-      cobalt_type = 'NativePromise'
+      cobalt_type = '::cobalt::script::NativePromise'
 
     assert cobalt_type, 'Unsupported idl_type %s' % idl_type
 
@@ -275,6 +284,8 @@
     if (idl_type.is_callback_function or idl_type.is_object_type or
         idl_type.is_callback_interface):
       return base_type + '*'
+    if is_any_type(idl_type):
+      return 'const ::cobalt::script::ScriptValue<%s>*' % base_type
     if idl_type.is_string_type or idl_type.is_interface_type:
       return 'const %s&' % base_type
     return base_type
@@ -600,6 +611,8 @@
             dictionary_member.idl_type,
         'name':
             convert_to_cobalt_name(dictionary_member.name),
+        'is_script_value':
+            is_any_type(dictionary_member.idl_type),
         'idl_name':
             dictionary_member.name,
         'type':
@@ -611,7 +624,8 @@
                 self.resolve_typedef(dictionary_member.idl_type),
                 dictionary_member.extended_attributes),
         'default_value':
-            idl_literal_to_cobalt_literal(dictionary_member.idl_type,
-                                          dictionary_member.default_value)
+            get_dictionary_default_value(dictionary_member.idl_type,
+                                         dictionary_member.default_value,
+                                         dictionary_member.name)
             if dictionary_member.default_value else None,
     }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
index eee757a..02dbce6 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -25,7 +25,9 @@
 #include <string>
 
 #include "base/optional.h"
+#include "cobalt/script/script_value.h"
 #include "cobalt/script/sequence.h"
+#include "cobalt/script/value_handle.h"
 #include "cobalt/bindings/testing/test_dictionary.h"
 
 using cobalt::bindings::testing::TestDictionary;
@@ -41,6 +43,17 @@
     nested_dictionary_ = TestDictionary();
   }
 
+  DictionaryWithDictionaryMember(const DictionaryWithDictionaryMember& other) {
+    has_nested_dictionary_ = other.has_nested_dictionary_;
+    nested_dictionary_ = other.nested_dictionary_;
+  }
+
+  DictionaryWithDictionaryMember& operator=(const DictionaryWithDictionaryMember& other) {
+    has_nested_dictionary_ = other.has_nested_dictionary_;
+    nested_dictionary_ = other.nested_dictionary_;
+    return *this;
+  }
+
   bool has_nested_dictionary() const {
     return has_nested_dictionary_;
   }
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
index 09b1d08..045f8e2 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -265,7 +265,7 @@
       WrapperPrivate::GetFromObject(context, object);
   CallbackInterfaceInterface* impl =
       wrapper_private->wrappable<CallbackInterfaceInterface>().get();
-  TypeTraits<CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
+  TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
   FromJSValue(context, vp, (kConversionFlagNullable), &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
@@ -323,7 +323,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callback_interface;
+  TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callback_interface;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
index 1b341be..e40be08 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -265,7 +265,7 @@
       WrapperPrivate::GetFromObject(context, object);
   DictionaryInterface* impl =
       wrapper_private->wrappable<DictionaryInterface>().get();
-  TypeTraits<script::Sequence< TestDictionary > >::ConversionType value;
+  TypeTraits<::cobalt::script::Sequence< TestDictionary > >::ConversionType value;
   FromJSValue(context, vp, kNoConversionFlags, &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
index 07aeddb..e5b69e4 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -291,7 +291,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<ValueHandle >::ConversionType value;
+  TypeTraits<::cobalt::script::ValueHandle >::ConversionType value;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc
new file mode 100644
index 0000000..3fce4bc
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc
@@ -0,0 +1,642 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/interface.cc.template
+
+#include "cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h"
+
+#include "base/debug/trace_event.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/opaque_handle.h"
+#include "cobalt/script/script_value.h"
+
+#include "mozjs_gen_type_conversion.h"
+
+#include "base/lazy_instance.h"
+#include "cobalt/script/exception_state.h"
+#include "cobalt/script/mozjs/callback_function_conversion.h"
+#include "cobalt/script/mozjs/conversion_helpers.h"
+#include "cobalt/script/mozjs/mozjs_callback_function.h"
+#include "cobalt/script/mozjs/mozjs_exception_state.h"
+#include "cobalt/script/mozjs/mozjs_global_environment.h"
+#include "cobalt/script/mozjs/mozjs_object_handle.h"
+#include "cobalt/script/mozjs/mozjs_property_enumerator.h"
+#include "cobalt/script/mozjs/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs/mozjs_value_handle.h"
+#include "cobalt/script/mozjs/native_promise.h"
+#include "cobalt/script/mozjs/proxy_handler.h"
+#include "cobalt/script/mozjs/type_traits.h"
+#include "cobalt/script/mozjs/wrapper_factory.h"
+#include "cobalt/script/mozjs/wrapper_private.h"
+#include "cobalt/script/property_enumerator.h"
+#include "cobalt/script/sequence.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+#include "third_party/mozjs/js/src/jsfriendapi.h"
+
+namespace {
+using cobalt::bindings::testing::InterfaceWithAnyDictionary;
+using cobalt::bindings::testing::MozjsInterfaceWithAnyDictionary;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::GlobalEnvironment;
+using cobalt::script::OpaqueHandle;
+using cobalt::script::OpaqueHandleHolder;
+using cobalt::script::ScriptValue;
+using cobalt::script::ValueHandle;
+using cobalt::script::Wrappable;
+
+using cobalt::script::CallbackFunction;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::ExceptionState;
+using cobalt::script::Wrappable;
+using cobalt::script::mozjs::FromJSValue;
+using cobalt::script::mozjs::InterfaceData;
+using cobalt::script::mozjs::MozjsCallbackFunction;
+using cobalt::script::mozjs::MozjsExceptionState;
+using cobalt::script::mozjs::MozjsGlobalEnvironment;
+using cobalt::script::mozjs::MozjsPropertyEnumerator;
+using cobalt::script::mozjs::MozjsUserObjectHolder;
+using cobalt::script::mozjs::ProxyHandler;
+using cobalt::script::mozjs::ToJSValue;
+using cobalt::script::mozjs::TypeTraits;
+using cobalt::script::mozjs::WrapperFactory;
+using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
+using cobalt::script::mozjs::kConversionFlagNullable;
+using cobalt::script::mozjs::kConversionFlagRestricted;
+using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
+using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
+using cobalt::script::mozjs::kNoConversionFlags;
+}  // namespace
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+namespace {
+
+class MozjsInterfaceWithAnyDictionaryHandler : public ProxyHandler {
+ public:
+  MozjsInterfaceWithAnyDictionaryHandler()
+      : ProxyHandler(indexed_property_hooks, named_property_hooks) {}
+
+ private:
+  static NamedPropertyHooks named_property_hooks;
+  static IndexedPropertyHooks indexed_property_hooks;
+};
+
+ProxyHandler::NamedPropertyHooks
+MozjsInterfaceWithAnyDictionaryHandler::named_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+ProxyHandler::IndexedPropertyHooks
+MozjsInterfaceWithAnyDictionaryHandler::indexed_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+
+static base::LazyInstance<MozjsInterfaceWithAnyDictionaryHandler>
+    proxy_handler;
+
+JSBool Constructor(JSContext* context, unsigned int argc, JS::Value* vp);
+JSBool HasInstance(JSContext *context, JS::HandleObject type,
+                   JS::MutableHandleValue vp, JSBool *success) {
+  JS::RootedObject global_object(
+      context, JS_GetGlobalForObject(context, type));
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(
+      context, MozjsInterfaceWithAnyDictionary::GetPrototype(context, global_object));
+
+  // |IsDelegate| walks the prototype chain of an object returning true if
+  // .prototype is found.
+  bool is_delegate;
+  if (!IsDelegate(context, prototype, vp, &is_delegate)) {
+    *success = false;
+    return false;
+  }
+
+  *success = is_delegate;
+  return true;
+}
+
+InterfaceData* CreateCachedInterfaceData() {
+  InterfaceData* interface_data = new InterfaceData();
+  memset(&interface_data->instance_class_definition, 0,
+         sizeof(interface_data->instance_class_definition));
+  memset(&interface_data->prototype_class_definition, 0,
+         sizeof(interface_data->prototype_class_definition));
+  memset(&interface_data->interface_object_class_definition, 0,
+         sizeof(interface_data->interface_object_class_definition));
+
+  JSClass* instance_class = &interface_data->instance_class_definition;
+  const int kGlobalFlags = 0;
+  instance_class->name = "InterfaceWithAnyDictionary";
+  instance_class->flags = kGlobalFlags | JSCLASS_HAS_PRIVATE;
+  instance_class->addProperty = JS_PropertyStub;
+  instance_class->delProperty = JS_DeletePropertyStub;
+  instance_class->getProperty = JS_PropertyStub;
+  instance_class->setProperty = JS_StrictPropertyStub;
+  instance_class->enumerate = JS_EnumerateStub;
+  instance_class->resolve = JS_ResolveStub;
+  instance_class->convert = JS_ConvertStub;
+  // Function to be called before on object of this class is garbage collected.
+  instance_class->finalize = &WrapperPrivate::Finalizer;
+  // Called to trace objects that can be referenced from this object.
+  instance_class->trace = &WrapperPrivate::Trace;
+
+  JSClass* prototype_class = &interface_data->prototype_class_definition;
+  prototype_class->name = "InterfaceWithAnyDictionaryPrototype";
+  prototype_class->flags = 0;
+  prototype_class->addProperty = JS_PropertyStub;
+  prototype_class->delProperty = JS_DeletePropertyStub;
+  prototype_class->getProperty = JS_PropertyStub;
+  prototype_class->setProperty = JS_StrictPropertyStub;
+  prototype_class->enumerate = JS_EnumerateStub;
+  prototype_class->resolve = JS_ResolveStub;
+  prototype_class->convert = JS_ConvertStub;
+
+  JSClass* interface_object_class =
+      &interface_data->interface_object_class_definition;
+  interface_object_class->name = "InterfaceWithAnyDictionaryConstructor";
+  interface_object_class->flags = 0;
+  interface_object_class->addProperty = JS_PropertyStub;
+  interface_object_class->delProperty = JS_DeletePropertyStub;
+  interface_object_class->getProperty = JS_PropertyStub;
+  interface_object_class->setProperty = JS_StrictPropertyStub;
+  interface_object_class->enumerate = JS_EnumerateStub;
+  interface_object_class->resolve = JS_ResolveStub;
+  interface_object_class->convert = JS_ConvertStub;
+  interface_object_class->hasInstance = &HasInstance;
+  interface_object_class->construct = Constructor;
+  return interface_data;
+}
+
+JSBool fcn_getAny(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->GetAny(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+JSBool fcn_hasAny(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->HasAny(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+JSBool fcn_hasAnyDefault(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->HasAnyDefault(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+JSBool fcn_setAny(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+  const size_t kMinArguments = 1;
+  if (args.length() < kMinArguments) {
+    exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+    return false;
+  }
+  // Non-optional arguments
+  TypeTraits<::cobalt::script::ValueHandle >::ConversionType value;
+
+  DCHECK_LT(0, args.length());
+  JS::RootedValue non_optional_value0(
+      context, args[0]);
+  FromJSValue(context,
+              non_optional_value0,
+              kNoConversionFlags,
+              &exception_state, &value);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+
+  impl->SetAny(value);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
+
+const JSPropertySpec prototype_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec prototype_functions[] = {
+  {
+      "getAny",
+      JSOP_WRAPPER(&fcn_getAny),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
+      "hasAny",
+      JSOP_WRAPPER(&fcn_hasAny),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
+      "hasAnyDefault",
+      JSOP_WRAPPER(&fcn_hasAnyDefault),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  {
+      "setAny",
+      JSOP_WRAPPER(&fcn_setAny),
+      1,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
+  JS_FS_END
+};
+
+const JSPropertySpec interface_object_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec interface_object_functions[] = {
+  JS_FS_END
+};
+
+const JSPropertySpec own_properties[] = {
+  JS_PS_END
+};
+
+void InitializePrototypeAndInterfaceObject(
+    InterfaceData* interface_data, JSContext* context,
+    JS::HandleObject global_object) {
+  DCHECK(!interface_data->prototype);
+  DCHECK(!interface_data->interface_object);
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  JS::RootedObject parent_prototype(
+      context, JS_GetObjectPrototype(context, global_object));
+  DCHECK(parent_prototype);
+
+  // Create the Prototype object.
+  interface_data->prototype = JS_NewObjectWithGivenProto(
+      context, &interface_data->prototype_class_definition, parent_prototype,
+      NULL);
+  bool success = JS_DefineProperties(
+      context, interface_data->prototype, prototype_properties);
+  DCHECK(success);
+  success = JS_DefineFunctions(
+      context, interface_data->prototype, prototype_functions);
+  DCHECK(success);
+
+  JS::RootedObject function_prototype(
+      context, JS_GetFunctionPrototype(context, global_object));
+  DCHECK(function_prototype);
+  // Create the Interface object.
+  interface_data->interface_object = JS_NewObjectWithGivenProto(
+      context, &interface_data->interface_object_class_definition,
+      function_prototype, NULL);
+
+  // Add the InterfaceObject.name property.
+  JS::RootedObject rooted_interface_object(
+      context, interface_data->interface_object);
+  JS::RootedValue name_value(context);
+  const char name[] =
+      "InterfaceWithAnyDictionary";
+  name_value.setString(JS_NewStringCopyZ(context, name));
+  success =
+      JS_DefineProperty(context, rooted_interface_object, "name", name_value,
+                        JS_PropertyStub, JS_StrictPropertyStub,
+                        JSPROP_READONLY);
+  DCHECK(success);
+
+  // Add the InterfaceObject.length property. It is set to the length of the
+  // shortest argument list of all overload constructors.
+  JS::RootedValue length_value(context);
+  length_value.setInt32(0);
+  success =
+      JS_DefineProperty(context, rooted_interface_object, "length",
+                        length_value, JS_PropertyStub, JS_StrictPropertyStub,
+                        JSPROP_READONLY);
+  DCHECK(success);
+
+  // Define interface object properties (including constants).
+  success = JS_DefineProperties(context, rooted_interface_object,
+                                interface_object_properties);
+  DCHECK(success);
+  // Define interface object functions (static).
+  success = JS_DefineFunctions(context, rooted_interface_object,
+                               interface_object_functions);
+  DCHECK(success);
+
+
+  // Set the Prototype.constructor and Constructor.prototype properties.
+  DCHECK(interface_data->interface_object);
+  DCHECK(interface_data->prototype);
+  JS::RootedObject rooted_prototype(context, interface_data->prototype);
+  success = JS_LinkConstructorAndPrototype(
+      context,
+      rooted_interface_object,
+      rooted_prototype);
+  DCHECK(success);
+}
+
+InterfaceData* GetInterfaceData(JSContext* context) {
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  // Use the address of the properties definition for this interface as a
+  // unique key for looking up the InterfaceData for this interface.
+  intptr_t key = reinterpret_cast<intptr_t>(&own_properties);
+  InterfaceData* interface_data = global_environment->GetInterfaceData(key);
+  if (!interface_data) {
+    interface_data = CreateCachedInterfaceData();
+    DCHECK(interface_data);
+    global_environment->CacheInterfaceData(key, interface_data);
+    DCHECK_EQ(interface_data, global_environment->GetInterfaceData(key));
+  }
+  return interface_data;
+}
+
+}  // namespace
+
+// static
+JSObject* MozjsInterfaceWithAnyDictionary::CreateProxy(
+    JSContext* context, const scoped_refptr<Wrappable>& wrappable) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  DCHECK(prototype);
+  JS::RootedObject new_object(context, JS_NewObjectWithGivenProto(
+      context, &interface_data->instance_class_definition, prototype, NULL));
+  DCHECK(new_object);
+  JS::RootedObject proxy(context,
+      ProxyHandler::NewProxy(context, new_object, prototype, NULL,
+                             proxy_handler.Pointer()));
+  WrapperPrivate::AddPrivateData(context, proxy, wrappable);
+  return proxy;
+}
+
+//static
+const JSClass* MozjsInterfaceWithAnyDictionary::PrototypeClass(
+      JSContext* context) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  JSClass* proto_class = JS_GetClass(*prototype.address());
+  return proto_class;
+}
+
+// static
+JSObject* MozjsInterfaceWithAnyDictionary::GetPrototype(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->prototype) {
+    // Create new prototype that has all the props and methods
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->prototype);
+  return interface_data->prototype;
+}
+
+// static
+JSObject* MozjsInterfaceWithAnyDictionary::GetInterfaceObject(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->interface_object) {
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->interface_object);
+  return interface_data->interface_object;
+}
+
+
+namespace {
+JSBool Constructor(JSContext* context, unsigned int argc, JS::Value* vp) {
+  MozjsExceptionState exception_state(context);
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+  scoped_refptr<InterfaceWithAnyDictionary> new_object =
+      new InterfaceWithAnyDictionary();
+  JS::RootedValue result_value(context);
+  ToJSValue(context, new_object, &result_value);
+  DCHECK(result_value.isObject());
+  JS::RootedObject result_object(context, JSVAL_TO_OBJECT(result_value));
+  args.rval().setObject(*result_object);
+  return true;
+}
+}  // namespace
+
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h
new file mode 100644
index 0000000..185dcbc
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/interface.h.template
+
+#ifndef MozjsInterfaceWithAnyDictionary_h
+#define MozjsInterfaceWithAnyDictionary_h
+
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/bindings/testing/interface_with_any_dictionary.h"
+
+#include "third_party/mozjs/js/src/jsapi.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class MozjsInterfaceWithAnyDictionary {
+ public:
+  static JSObject* CreateProxy(JSContext* context,
+      const scoped_refptr<script::Wrappable>& wrappable);
+  static const JSClass* PrototypeClass(JSContext* context);
+  static JSObject* GetPrototype(JSContext* context,
+                                JS::HandleObject global_object);
+  static JSObject* GetInterfaceObject(JSContext* context,
+                                      JS::HandleObject global_object);
+};
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
+
+#endif  // MozjsInterfaceWithAnyDictionary_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
index 8e2d83d..7541b5b 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -478,7 +478,7 @@
       WrapperPrivate::GetFromObject(context, object);
   ObjectTypeBindingsInterface* impl =
       wrapper_private->wrappable<ObjectTypeBindingsInterface>().get();
-  TypeTraits<OpaqueHandle >::ConversionType value;
+  TypeTraits<::cobalt::script::OpaqueHandle >::ConversionType value;
   FromJSValue(context, vp, (kConversionFlagNullable), &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
index e05d81c..0ddaaeb 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -607,7 +607,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< scoped_refptr<ArbitraryInterface> > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< scoped_refptr<ArbitraryInterface> > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -671,7 +671,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< script::Sequence< script::Sequence< scoped_refptr<ArbitraryInterface> > > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< ::cobalt::script::Sequence< ::cobalt::script::Sequence< scoped_refptr<ArbitraryInterface> > > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -735,7 +735,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< int32_t > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< int32_t > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -799,7 +799,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< std::string > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< std::string > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -863,7 +863,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< script::Sequence< std::string > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< ::cobalt::script::Sequence< std::string > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -927,7 +927,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::UnionType2<std::string, script::Sequence< std::string > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::UnionType2<std::string, ::cobalt::script::Sequence< std::string > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -991,7 +991,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< script::UnionType2<std::string, scoped_refptr<ArbitraryInterface> > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< ::cobalt::script::UnionType2<std::string, scoped_refptr<ArbitraryInterface> > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
index 50bd3c2..8c8f3c4 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -132,6 +132,28 @@
       return;
     }
   }
+  if (in_dictionary.has_any_member_with_default()) {
+    JS::RootedValue member_value(context);
+    ToJSValue(context, in_dictionary.any_member_with_default(), &member_value);
+    if (!JS_DefineProperty(context, dictionary_object,
+                           "anyMemberWithDefault",
+                           member_value, NULL, NULL, kPropertyAttributes)) {
+      // Some internal error occurred.
+      NOTREACHED();
+      return;
+    }
+  }
+  if (in_dictionary.has_any_member()) {
+    JS::RootedValue member_value(context);
+    ToJSValue(context, in_dictionary.any_member(), &member_value);
+    if (!JS_DefineProperty(context, dictionary_object,
+                           "anyMember",
+                           member_value, NULL, NULL, kPropertyAttributes)) {
+      // Some internal error occurred.
+      NOTREACHED();
+      return;
+    }
+  }
   out_value.set(OBJECT_TO_JSVAL(dictionary_object));
 }
 
@@ -305,6 +327,20 @@
     }
     out_dictionary->set_non_default_member(converted_value);
   }
+  JS::RootedValue any_member_with_default(context);
+  if (!JS_GetProperty(context, dictionary_object,
+                      "anyMemberWithDefault",
+                      any_member_with_default.address())) {
+    exception_state->SetSimpleException(kSimpleError);
+    return;
+  }
+  JS::RootedValue any_member(context);
+  if (!JS_GetProperty(context, dictionary_object,
+                      "anyMember",
+                      any_member.address())) {
+    exception_state->SetSimpleException(kSimpleError);
+    return;
+  }
 }
 
 }  // namespace mozjs
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
index 2eb8e32..4529e86 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -269,7 +269,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<script::UnionType4<std::string, bool, scoped_refptr<ArbitraryInterface>, int32_t > >::ConversionType value;
+  TypeTraits<::cobalt::script::UnionType4<std::string, bool, scoped_refptr<ArbitraryInterface>, int32_t > >::ConversionType value;
   FromJSValue(context, vp, kNoConversionFlags, &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
@@ -351,7 +351,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<base::optional<script::UnionType2<double, std::string > > >::ConversionType value;
+  TypeTraits<base::optional<::cobalt::script::UnionType2<double, std::string > > >::ConversionType value;
   FromJSValue(context, vp, kNoConversionFlags, &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
@@ -433,7 +433,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<base::optional<script::UnionType2<double, std::string > > >::ConversionType value;
+  TypeTraits<base::optional<::cobalt::script::UnionType2<double, std::string > > >::ConversionType value;
   FromJSValue(context, vp, (kConversionFlagNullable), &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
@@ -515,7 +515,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<script::UnionType2<scoped_refptr<BaseInterface>, std::string > >::ConversionType value;
+  TypeTraits<::cobalt::script::UnionType2<scoped_refptr<BaseInterface>, std::string > >::ConversionType value;
   FromJSValue(context, vp, kNoConversionFlags, &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
index ea34cc4..b28b493 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -50,6 +50,7 @@
 #include "cobalt/bindings/testing/implemented_interface.h"
 #include "cobalt/bindings/testing/indexed_getter_interface.h"
 #include "cobalt/bindings/testing/interface_with_any.h"
+#include "cobalt/bindings/testing/interface_with_any_dictionary.h"
 #include "cobalt/bindings/testing/interface_with_unsupported_properties.h"
 #include "cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.h"
 #include "cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.h"
@@ -77,6 +78,7 @@
 #include "cobalt/bindings/testing/mozjs_implemented_interface.h"
 #include "cobalt/bindings/testing/mozjs_indexed_getter_interface.h"
 #include "cobalt/bindings/testing/mozjs_interface_with_any.h"
+#include "cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h"
 #include "cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.h"
 #include "cobalt/bindings/testing/mozjs_named_constructor_interface.h"
 #include "cobalt/bindings/testing/mozjs_named_getter_interface.h"
@@ -177,6 +179,7 @@
 using cobalt::bindings::testing::ImplementedInterface;
 using cobalt::bindings::testing::IndexedGetterInterface;
 using cobalt::bindings::testing::InterfaceWithAny;
+using cobalt::bindings::testing::InterfaceWithAnyDictionary;
 using cobalt::bindings::testing::InterfaceWithUnsupportedProperties;
 using cobalt::bindings::testing::MozjsAnonymousIndexedGetterInterface;
 using cobalt::bindings::testing::MozjsAnonymousNamedGetterInterface;
@@ -208,6 +211,7 @@
 using cobalt::bindings::testing::MozjsImplementedInterface;
 using cobalt::bindings::testing::MozjsIndexedGetterInterface;
 using cobalt::bindings::testing::MozjsInterfaceWithAny;
+using cobalt::bindings::testing::MozjsInterfaceWithAnyDictionary;
 using cobalt::bindings::testing::MozjsInterfaceWithUnsupportedProperties;
 using cobalt::bindings::testing::MozjsNamedConstructorInterface;
 using cobalt::bindings::testing::MozjsNamedGetterInterface;
@@ -589,7 +593,7 @@
       WrapperPrivate::GetFromObject(context, object);
   Window* impl =
       wrapper_private->wrappable<Window>().get();
-  TypeTraits<CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
+  TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
   FromJSValue(context, vp, (kConversionFlagNullable), &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
@@ -1165,6 +1169,10 @@
       base::Bind(MozjsInterfaceWithAny::CreateProxy),
       base::Bind(MozjsInterfaceWithAny::PrototypeClass));
   wrapper_factory->RegisterWrappableType(
+      InterfaceWithAnyDictionary::InterfaceWithAnyDictionaryWrappableType(),
+      base::Bind(MozjsInterfaceWithAnyDictionary::CreateProxy),
+      base::Bind(MozjsInterfaceWithAnyDictionary::PrototypeClass));
+  wrapper_factory->RegisterWrappableType(
       InterfaceWithUnsupportedProperties::InterfaceWithUnsupportedPropertiesWrappableType(),
       base::Bind(MozjsInterfaceWithUnsupportedProperties::CreateProxy),
       base::Bind(MozjsInterfaceWithUnsupportedProperties::PrototypeClass));
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
index 97ffad7..4ec2228 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
@@ -25,7 +25,9 @@
 #include <string>
 
 #include "base/optional.h"
+#include "cobalt/script/script_value.h"
 #include "cobalt/script/sequence.h"
+#include "cobalt/script/value_handle.h"
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 
 using cobalt::bindings::testing::ArbitraryInterface;
@@ -49,10 +51,73 @@
     string_member_ = std::string();
     has_interface_member_ = false;
     interface_member_ = scoped_refptr<ArbitraryInterface>();
-    has_member_with_default_ = true;
     member_with_default_ = 5;
     has_non_default_member_ = false;
     non_default_member_ = int32_t();
+    has_any_member_ = false;
+  }
+
+  TestDictionary(const TestDictionary& other) {
+    has_boolean_member_ = other.has_boolean_member_;
+    boolean_member_ = other.boolean_member_;
+    has_short_clamp_member_ = other.has_short_clamp_member_;
+    short_clamp_member_ = other.short_clamp_member_;
+    has_long_member_ = other.has_long_member_;
+    long_member_ = other.long_member_;
+    has_double_member_ = other.has_double_member_;
+    double_member_ = other.double_member_;
+    has_string_member_ = other.has_string_member_;
+    string_member_ = other.string_member_;
+    has_interface_member_ = other.has_interface_member_;
+    interface_member_ = other.interface_member_;
+    member_with_default_ = other.member_with_default_;
+    has_non_default_member_ = other.has_non_default_member_;
+    non_default_member_ = other.non_default_member_;
+    if (other.any_member_with_default_) {
+      any_member_with_default_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+              other.any_member_with_default_->referenced_value()));
+    }
+    has_any_member_ = other.has_any_member_;
+    if (other.any_member_) {
+      any_member_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+              other.any_member_->referenced_value()));
+    }
+  }
+
+  TestDictionary& operator=(const TestDictionary& other) {
+    has_boolean_member_ = other.has_boolean_member_;
+    boolean_member_ = other.boolean_member_;
+    has_short_clamp_member_ = other.has_short_clamp_member_;
+    short_clamp_member_ = other.short_clamp_member_;
+    has_long_member_ = other.has_long_member_;
+    long_member_ = other.long_member_;
+    has_double_member_ = other.has_double_member_;
+    double_member_ = other.double_member_;
+    has_string_member_ = other.has_string_member_;
+    string_member_ = other.string_member_;
+    has_interface_member_ = other.has_interface_member_;
+    interface_member_ = other.interface_member_;
+    member_with_default_ = other.member_with_default_;
+    has_non_default_member_ = other.has_non_default_member_;
+    non_default_member_ = other.non_default_member_;
+    if (other.any_member_with_default_) {
+      any_member_with_default_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+                other.any_member_with_default_->referenced_value()));
+    } else {
+      any_member_with_default_.reset();
+    }
+    has_any_member_ = other.has_any_member_;
+    if (other.any_member_) {
+      any_member_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+                other.any_member_->referenced_value()));
+    } else {
+      any_member_.reset();
+    }
+    return *this;
   }
 
   bool has_boolean_member() const {
@@ -106,7 +171,7 @@
   bool has_string_member() const {
     return has_string_member_;
   }
-  std::string string_member() const {
+  const std::string& string_member() const {
     DCHECK(has_string_member_);
     return string_member_;
   }
@@ -118,7 +183,7 @@
   bool has_interface_member() const {
     return has_interface_member_;
   }
-  scoped_refptr<ArbitraryInterface> interface_member() const {
+  const scoped_refptr<ArbitraryInterface>& interface_member() const {
     DCHECK(has_interface_member_);
     return interface_member_;
   }
@@ -128,14 +193,12 @@
   }
 
   bool has_member_with_default() const {
-    return has_member_with_default_;
+    return true;
   }
   int32_t member_with_default() const {
-    DCHECK(has_member_with_default_);
     return member_with_default_;
   }
   void set_member_with_default(int32_t value) {
-    has_member_with_default_ = true;
     member_with_default_ = value;
   }
 
@@ -151,6 +214,44 @@
     non_default_member_ = value;
   }
 
+  bool has_any_member_with_default() const {
+    return true;
+  }
+  const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* any_member_with_default() const {
+    if (!any_member_with_default_) {
+      return NULL;
+    }
+    return &(any_member_with_default_->referenced_value());
+  }
+  void set_any_member_with_default(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) {
+    if (value) {
+      any_member_with_default_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value));
+    } else {
+      any_member_with_default_.reset();
+    }
+  }
+
+  bool has_any_member() const {
+    return has_any_member_;
+  }
+  const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* any_member() const {
+    DCHECK(has_any_member_);
+    if (!any_member_) {
+      return NULL;
+    }
+    return &(any_member_->referenced_value());
+  }
+  void set_any_member(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) {
+    has_any_member_ = true;
+    if (value) {
+      any_member_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value));
+    } else {
+      any_member_.reset();
+    }
+  }
+
  private:
   bool has_boolean_member_;
   bool boolean_member_;
@@ -164,10 +265,12 @@
   std::string string_member_;
   bool has_interface_member_;
   scoped_refptr<ArbitraryInterface> interface_member_;
-  bool has_member_with_default_;
   int32_t member_with_default_;
   bool has_non_default_member_;
   int32_t non_default_member_;
+  scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_with_default_;
+  bool has_any_member_;
+  scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_;
 };
 
 // This ostream override is necessary for MOCK_METHODs commonly used
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
index eee757a..02dbce6 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -25,7 +25,9 @@
 #include <string>
 
 #include "base/optional.h"
+#include "cobalt/script/script_value.h"
 #include "cobalt/script/sequence.h"
+#include "cobalt/script/value_handle.h"
 #include "cobalt/bindings/testing/test_dictionary.h"
 
 using cobalt::bindings::testing::TestDictionary;
@@ -41,6 +43,17 @@
     nested_dictionary_ = TestDictionary();
   }
 
+  DictionaryWithDictionaryMember(const DictionaryWithDictionaryMember& other) {
+    has_nested_dictionary_ = other.has_nested_dictionary_;
+    nested_dictionary_ = other.nested_dictionary_;
+  }
+
+  DictionaryWithDictionaryMember& operator=(const DictionaryWithDictionaryMember& other) {
+    has_nested_dictionary_ = other.has_nested_dictionary_;
+    nested_dictionary_ = other.nested_dictionary_;
+    return *this;
+  }
+
   bool has_nested_dictionary() const {
     return has_nested_dictionary_;
   }
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
index c5622f7..657efc9 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -265,7 +265,7 @@
       WrapperPrivate::GetFromObject(context, object);
   CallbackInterfaceInterface* impl =
       wrapper_private->wrappable<CallbackInterfaceInterface>().get();
-  TypeTraits<CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
+  TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -327,7 +327,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callback_interface;
+  TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callback_interface;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
index 1b865a6..15d0eed 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -265,7 +265,7 @@
       WrapperPrivate::GetFromObject(context, object);
   DictionaryInterface* impl =
       wrapper_private->wrappable<DictionaryInterface>().get();
-  TypeTraits<script::Sequence< TestDictionary > >::ConversionType value;
+  TypeTraits<::cobalt::script::Sequence< TestDictionary > >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
index 5943475..f59f815 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -276,7 +276,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<ValueHandle >::ConversionType value;
+  TypeTraits<::cobalt::script::ValueHandle >::ConversionType value;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc
new file mode 100644
index 0000000..34cf85c
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc
@@ -0,0 +1,608 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/interface.cc.template
+
+#include "cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h"
+
+#include "base/debug/trace_event.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/opaque_handle.h"
+#include "cobalt/script/script_value.h"
+
+#include "mozjs_gen_type_conversion.h"
+
+#include "base/lazy_instance.h"
+#include "cobalt/script/exception_state.h"
+#include "cobalt/script/mozjs-45/callback_function_conversion.h"
+#include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "cobalt/script/mozjs-45/mozjs_callback_function.h"
+#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_global_environment.h"
+#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
+#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
+#include "cobalt/script/mozjs-45/proxy_handler.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/wrapper_factory.h"
+#include "cobalt/script/mozjs-45/wrapper_private.h"
+#include "cobalt/script/property_enumerator.h"
+#include "cobalt/script/sequence.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "third_party/mozjs-45/js/src/jsfriendapi.h"
+
+namespace {
+using cobalt::bindings::testing::InterfaceWithAnyDictionary;
+using cobalt::bindings::testing::MozjsInterfaceWithAnyDictionary;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::GlobalEnvironment;
+using cobalt::script::OpaqueHandle;
+using cobalt::script::OpaqueHandleHolder;
+using cobalt::script::ScriptValue;
+using cobalt::script::ValueHandle;
+using cobalt::script::Wrappable;
+
+using cobalt::script::CallbackFunction;
+using cobalt::script::CallbackInterfaceTraits;
+using cobalt::script::ExceptionState;
+using cobalt::script::Wrappable;
+using cobalt::script::mozjs::FromJSValue;
+using cobalt::script::mozjs::InterfaceData;
+using cobalt::script::mozjs::MozjsCallbackFunction;
+using cobalt::script::mozjs::MozjsExceptionState;
+using cobalt::script::mozjs::MozjsGlobalEnvironment;
+using cobalt::script::mozjs::MozjsPropertyEnumerator;
+using cobalt::script::mozjs::MozjsUserObjectHolder;
+using cobalt::script::mozjs::ProxyHandler;
+using cobalt::script::mozjs::ToJSValue;
+using cobalt::script::mozjs::TypeTraits;
+using cobalt::script::mozjs::WrapperFactory;
+using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
+using cobalt::script::mozjs::kConversionFlagNullable;
+using cobalt::script::mozjs::kConversionFlagRestricted;
+using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
+using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
+using cobalt::script::mozjs::kNoConversionFlags;
+}  // namespace
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+namespace {
+
+class MozjsInterfaceWithAnyDictionaryHandler : public ProxyHandler {
+ public:
+  MozjsInterfaceWithAnyDictionaryHandler()
+      : ProxyHandler(indexed_property_hooks, named_property_hooks) {}
+
+ private:
+  static NamedPropertyHooks named_property_hooks;
+  static IndexedPropertyHooks indexed_property_hooks;
+};
+
+ProxyHandler::NamedPropertyHooks
+MozjsInterfaceWithAnyDictionaryHandler::named_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+ProxyHandler::IndexedPropertyHooks
+MozjsInterfaceWithAnyDictionaryHandler::indexed_property_hooks = {
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+
+static base::LazyInstance<MozjsInterfaceWithAnyDictionaryHandler>
+    proxy_handler;
+
+bool Constructor(JSContext* context, unsigned int argc, JS::Value* vp);
+bool HasInstance(JSContext *context, JS::HandleObject type,
+                   JS::MutableHandleValue vp, bool *success) {
+  JS::RootedObject global_object(
+      context, JS_GetGlobalForObject(context, type));
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(
+      context, MozjsInterfaceWithAnyDictionary::GetPrototype(context, global_object));
+
+  // |IsDelegate| walks the prototype chain of an object returning true if
+  // .prototype is found.
+  bool is_delegate;
+  if (!IsDelegate(context, prototype, vp, &is_delegate)) {
+    *success = false;
+    return false;
+  }
+
+  *success = is_delegate;
+  return true;
+}
+
+const JSClass instance_class_definition = {
+    "InterfaceWithAnyDictionary",
+    0 | JSCLASS_HAS_PRIVATE,
+    NULL,  // addProperty
+    NULL,  // delProperty
+    NULL,  // getProperty
+    NULL,  // setProperty
+    NULL,  // enumerate
+    NULL,  // resolve
+    NULL,  // mayResolve
+    &WrapperPrivate::Finalizer,  // finalize
+    NULL,  // call
+    NULL,  // hasInstance
+    NULL,  // construct
+    &WrapperPrivate::Trace,  // trace
+};
+
+const JSClass prototype_class_definition = {
+    "InterfaceWithAnyDictionaryPrototype",
+};
+
+const JSClass interface_object_class_definition = {
+    "InterfaceWithAnyDictionaryConstructor",
+    0,
+    NULL,  // addProperty
+    NULL,  // delProperty
+    NULL,  // getProperty
+    NULL,  // setProperty
+    NULL,  // enumerate
+    NULL,  // resolve
+    NULL,  // mayResolve
+    NULL,  // finalize
+    NULL,  // call
+    &HasInstance,
+    Constructor,
+};
+
+bool fcn_getAny(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->GetAny(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+bool fcn_hasAny(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->HasAny(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+bool fcn_hasAnyDefault(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->HasAnyDefault(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+bool fcn_setAny(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsInterfaceWithAnyDictionary::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<InterfaceWithAnyDictionary>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  InterfaceWithAnyDictionary* impl =
+      wrapper_private->wrappable<InterfaceWithAnyDictionary>().get();
+  const size_t kMinArguments = 1;
+  if (args.length() < kMinArguments) {
+    exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+    return false;
+  }
+  // Non-optional arguments
+  TypeTraits<::cobalt::script::ValueHandle >::ConversionType value;
+
+  DCHECK_LT(0, args.length());
+  JS::RootedValue non_optional_value0(
+      context, args[0]);
+  FromJSValue(context,
+              non_optional_value0,
+              kNoConversionFlags,
+              &exception_state, &value);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+
+  impl->SetAny(value);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
+
+
+const JSPropertySpec prototype_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec prototype_functions[] = {
+  JS_FNSPEC(
+      "getAny", fcn_getAny, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "hasAny", fcn_hasAny, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "hasAnyDefault", fcn_hasAnyDefault, NULL,
+      0, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "setAny", fcn_setAny, NULL,
+      1, JSPROP_ENUMERATE, NULL),
+  JS_FS_END
+};
+
+const JSPropertySpec interface_object_properties[] = {
+  JS_PS_END
+};
+
+const JSFunctionSpec interface_object_functions[] = {
+  JS_FS_END
+};
+
+const JSPropertySpec own_properties[] = {
+  JS_PS_END
+};
+
+void InitializePrototypeAndInterfaceObject(
+    InterfaceData* interface_data, JSContext* context,
+    JS::HandleObject global_object) {
+  DCHECK(!interface_data->prototype);
+  DCHECK(!interface_data->interface_object);
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  JS::RootedObject parent_prototype(
+      context, JS_GetObjectPrototype(context, global_object));
+  DCHECK(parent_prototype);
+
+  interface_data->prototype = JS_NewObjectWithGivenProto(
+    context, &prototype_class_definition, parent_prototype
+  );
+
+  JS::RootedObject rooted_prototype(context, interface_data->prototype);
+  bool success = JS_DefineProperties(
+      context,
+      rooted_prototype,
+      prototype_properties);
+
+  DCHECK(success);
+  success = JS_DefineFunctions(
+      context, rooted_prototype, prototype_functions);
+  DCHECK(success);
+
+  JS::RootedObject function_prototype(
+      context, JS_GetFunctionPrototype(context, global_object));
+  DCHECK(function_prototype);
+  // Create the Interface object.
+  interface_data->interface_object = JS_NewObjectWithGivenProto(
+      context, &interface_object_class_definition,
+      function_prototype);
+
+  // Add the InterfaceObject.name property.
+  JS::RootedObject rooted_interface_object(
+      context, interface_data->interface_object);
+  JS::RootedValue name_value(context);
+  const char name[] =
+      "InterfaceWithAnyDictionary";
+  name_value.setString(JS_NewStringCopyZ(context, name));
+  success = JS_DefineProperty(
+      context, rooted_interface_object, "name", name_value, JSPROP_READONLY,
+      NULL, NULL);
+  DCHECK(success);
+
+  // Add the InterfaceObject.length property. It is set to the length of the
+  // shortest argument list of all overload constructors.
+  JS::RootedValue length_value(context);
+  length_value.setInt32(0);
+  success = JS_DefineProperty(
+      context, rooted_interface_object, "length", length_value,
+      JSPROP_READONLY, NULL, NULL);
+  DCHECK(success);
+
+  // Define interface object properties (including constants).
+  success = JS_DefineProperties(context, rooted_interface_object,
+                                interface_object_properties);
+  DCHECK(success);
+  // Define interface object functions (static).
+  success = JS_DefineFunctions(context, rooted_interface_object,
+                               interface_object_functions);
+  DCHECK(success);
+
+  // Set the Prototype.constructor and Constructor.prototype properties.
+  DCHECK(interface_data->interface_object);
+  DCHECK(interface_data->prototype);
+  success = JS_LinkConstructorAndPrototype(
+      context,
+      rooted_interface_object,
+      rooted_prototype);
+  DCHECK(success);
+}
+
+inline InterfaceData* GetInterfaceData(JSContext* context) {
+  const int kInterfaceUniqueId = 27;
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  // By convention, the |MozjsGlobalEnvironment| that we are associated with
+  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
+  // for it to be there in the first place, and could not have conflicted with
+  // any other interface.
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+}
+
+}  // namespace
+
+// static
+JSObject* MozjsInterfaceWithAnyDictionary::CreateProxy(
+    JSContext* context, const scoped_refptr<Wrappable>& wrappable) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  DCHECK(prototype);
+  JS::RootedObject new_object(
+      context,
+      JS_NewObjectWithGivenProto(
+          context, &instance_class_definition, prototype));
+  DCHECK(new_object);
+  JS::RootedObject proxy(context,
+      ProxyHandler::NewProxy(
+          context, proxy_handler.Pointer(), new_object, prototype));
+  WrapperPrivate::AddPrivateData(context, proxy, wrappable);
+  return proxy;
+}
+
+// static
+const JSClass* MozjsInterfaceWithAnyDictionary::PrototypeClass(
+      JSContext* context) {
+  DCHECK(MozjsGlobalEnvironment::GetFromContext(context));
+  JS::RootedObject global_object(
+      context,
+      MozjsGlobalEnvironment::GetFromContext(context)->global_object());
+  DCHECK(global_object);
+
+  JS::RootedObject prototype(context, GetPrototype(context, global_object));
+  const JSClass* proto_class = JS_GetClass(prototype);
+  return proto_class;
+}
+
+// static
+JSObject* MozjsInterfaceWithAnyDictionary::GetPrototype(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->prototype) {
+    // Create new prototype that has all the props and methods
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->prototype);
+  return interface_data->prototype;
+}
+
+// static
+JSObject* MozjsInterfaceWithAnyDictionary::GetInterfaceObject(
+    JSContext* context, JS::HandleObject global_object) {
+  DCHECK(JS_IsGlobalObject(global_object));
+
+  InterfaceData* interface_data = GetInterfaceData(context);
+  if (!interface_data->interface_object) {
+    InitializePrototypeAndInterfaceObject(
+        interface_data, context, global_object);
+  }
+  DCHECK(interface_data->interface_object);
+  return interface_data->interface_object;
+}
+
+
+namespace {
+bool Constructor(JSContext* context, unsigned int argc, JS::Value* vp) {
+  MozjsExceptionState exception_state(context);
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+  scoped_refptr<InterfaceWithAnyDictionary> new_object =
+      new InterfaceWithAnyDictionary();
+  JS::RootedValue result_value(context);
+  ToJSValue(context, new_object, &result_value);
+  DCHECK(result_value.isObject());
+  args.rval().setObject(result_value.toObject());
+  return true;
+}
+}  // namespace
+
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h
new file mode 100644
index 0000000..8efc06e
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/interface.h.template
+
+#ifndef MozjsInterfaceWithAnyDictionary_h
+#define MozjsInterfaceWithAnyDictionary_h
+
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/bindings/testing/interface_with_any_dictionary.h"
+
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class MozjsInterfaceWithAnyDictionary {
+ public:
+  static JSObject* CreateProxy(JSContext* context,
+      const scoped_refptr<script::Wrappable>& wrappable);
+  static const JSClass* PrototypeClass(JSContext* context);
+  static JSObject* GetPrototype(JSContext* context,
+                                JS::HandleObject global_object);
+  static JSObject* GetInterfaceObject(JSContext* context,
+                                      JS::HandleObject global_object);
+};
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
+
+#endif  // MozjsInterfaceWithAnyDictionary_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
index c9d71d7..fd76bd8 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
@@ -319,7 +319,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 27;
+  const int kInterfaceUniqueId = 28;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
index a013300..b9f0a33 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
@@ -275,7 +275,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 28;
+  const int kInterfaceUniqueId = 29;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
index e183554..a5fe033 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
@@ -596,7 +596,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 29;
+  const int kInterfaceUniqueId = 30;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
index 3874d45..3abd0a1 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
@@ -970,7 +970,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 30;
+  const int kInterfaceUniqueId = 31;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
index f73f3c5..148e851 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
@@ -397,7 +397,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 31;
+  const int kInterfaceUniqueId = 32;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
index 5940907..4990467 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
@@ -265,7 +265,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 32;
+  const int kInterfaceUniqueId = 33;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
index bafa068..d9cf90e 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
@@ -228,7 +228,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 33;
+  const int kInterfaceUniqueId = 34;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
index 9fc4e54..3ad3e5d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
@@ -1181,7 +1181,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 34;
+  const int kInterfaceUniqueId = 35;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
index c335147..9dfd580 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
@@ -3393,7 +3393,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 35;
+  const int kInterfaceUniqueId = 36;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
index af0e29b..a04bd09 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -521,7 +521,7 @@
       WrapperPrivate::GetFromObject(context, object);
   ObjectTypeBindingsInterface* impl =
       wrapper_private->wrappable<ObjectTypeBindingsInterface>().get();
-  TypeTraits<OpaqueHandle >::ConversionType value;
+  TypeTraits<::cobalt::script::OpaqueHandle >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -649,7 +649,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 36;
+  const int kInterfaceUniqueId = 37;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
index 213d6bf..5232239 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
@@ -1817,7 +1817,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 37;
+  const int kInterfaceUniqueId = 38;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
index 62a9828..e4ef6d9 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -534,7 +534,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 38;
+  const int kInterfaceUniqueId = 39;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
index f25d3fb..a537335 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
@@ -456,7 +456,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 39;
+  const int kInterfaceUniqueId = 40;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
index fa79d52..c499278 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -592,7 +592,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< scoped_refptr<ArbitraryInterface> > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< scoped_refptr<ArbitraryInterface> > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -656,7 +656,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< script::Sequence< script::Sequence< scoped_refptr<ArbitraryInterface> > > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< ::cobalt::script::Sequence< ::cobalt::script::Sequence< scoped_refptr<ArbitraryInterface> > > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -720,7 +720,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< int32_t > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< int32_t > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -784,7 +784,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< std::string > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< std::string > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -848,7 +848,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< script::Sequence< std::string > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< ::cobalt::script::Sequence< std::string > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -912,7 +912,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::UnionType2<std::string, script::Sequence< std::string > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::UnionType2<std::string, ::cobalt::script::Sequence< std::string > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -976,7 +976,7 @@
     return false;
   }
   // Non-optional arguments
-  TypeTraits<script::Sequence< script::UnionType2<std::string, scoped_refptr<ArbitraryInterface> > > >::ConversionType elements;
+  TypeTraits<::cobalt::script::Sequence< ::cobalt::script::UnionType2<std::string, scoped_refptr<ArbitraryInterface> > > >::ConversionType elements;
 
   DCHECK_LT(0, args.length());
   JS::RootedValue non_optional_value0(
@@ -1133,7 +1133,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 40;
+  const int kInterfaceUniqueId = 41;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
index 8526725..dc0f2a4 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
@@ -581,7 +581,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 42;
+  const int kInterfaceUniqueId = 43;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
index dfb6793..e1171ba 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
@@ -326,7 +326,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 43;
+  const int kInterfaceUniqueId = 44;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
index 480fab8..a12de2b 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
@@ -432,7 +432,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 44;
+  const int kInterfaceUniqueId = 45;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
index d0f28bd..0a1bc87 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
@@ -381,7 +381,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 45;
+  const int kInterfaceUniqueId = 46;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
index 92e30b5..8ac1392 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
@@ -363,7 +363,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 46;
+  const int kInterfaceUniqueId = 47;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
index 98fc778..78401b1 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -132,6 +132,28 @@
       return;
     }
   }
+  if (in_dictionary.has_any_member_with_default()) {
+    JS::RootedValue member_value(context);
+    ToJSValue(context, in_dictionary.any_member_with_default(), &member_value);
+    if (!JS_DefineProperty(context, dictionary_object,
+                           "anyMemberWithDefault",
+                           member_value, kPropertyAttributes, nullptr, nullptr)) {
+      // Some internal error occurred.
+      NOTREACHED();
+      return;
+    }
+  }
+  if (in_dictionary.has_any_member()) {
+    JS::RootedValue member_value(context);
+    ToJSValue(context, in_dictionary.any_member(), &member_value);
+    if (!JS_DefineProperty(context, dictionary_object,
+                           "anyMember",
+                           member_value, kPropertyAttributes, nullptr, nullptr)) {
+      // Some internal error occurred.
+      NOTREACHED();
+      return;
+    }
+  }
   out_value.setObject(*dictionary_object);
 }
 
@@ -305,6 +327,20 @@
     }
     out_dictionary->set_non_default_member(converted_value);
   }
+  JS::RootedValue any_member_with_default(context);
+  if (!JS_GetProperty(context, dictionary_object,
+                      "anyMemberWithDefault",
+                      &any_member_with_default)) {
+    exception_state->SetSimpleException(kSimpleError);
+    return;
+  }
+  JS::RootedValue any_member(context);
+  if (!JS_GetProperty(context, dictionary_object,
+                      "anyMember",
+                      &any_member)) {
+    exception_state->SetSimpleException(kSimpleError);
+    return;
+  }
 }
 
 }  // namespace mozjs
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
index 7908828..5269ebc 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -269,7 +269,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<script::UnionType4<std::string, bool, scoped_refptr<ArbitraryInterface>, int32_t > >::ConversionType value;
+  TypeTraits<::cobalt::script::UnionType4<std::string, bool, scoped_refptr<ArbitraryInterface>, int32_t > >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -369,7 +369,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<base::optional<script::UnionType2<double, std::string > > >::ConversionType value;
+  TypeTraits<base::optional<::cobalt::script::UnionType2<double, std::string > > >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -469,7 +469,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<base::optional<script::UnionType2<double, std::string > > >::ConversionType value;
+  TypeTraits<base::optional<::cobalt::script::UnionType2<double, std::string > > >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -569,7 +569,7 @@
       WrapperPrivate::GetFromObject(context, object);
   UnionTypesInterface* impl =
       wrapper_private->wrappable<UnionTypesInterface>().get();
-  TypeTraits<script::UnionType2<scoped_refptr<BaseInterface>, std::string > >::ConversionType value;
+  TypeTraits<::cobalt::script::UnionType2<scoped_refptr<BaseInterface>, std::string > >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -697,7 +697,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 48;
+  const int kInterfaceUniqueId = 49;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
index 949d54e..b9cb145 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -50,6 +50,7 @@
 #include "cobalt/bindings/testing/implemented_interface.h"
 #include "cobalt/bindings/testing/indexed_getter_interface.h"
 #include "cobalt/bindings/testing/interface_with_any.h"
+#include "cobalt/bindings/testing/interface_with_any_dictionary.h"
 #include "cobalt/bindings/testing/interface_with_unsupported_properties.h"
 #include "cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.h"
 #include "cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.h"
@@ -77,6 +78,7 @@
 #include "cobalt/bindings/testing/mozjs_implemented_interface.h"
 #include "cobalt/bindings/testing/mozjs_indexed_getter_interface.h"
 #include "cobalt/bindings/testing/mozjs_interface_with_any.h"
+#include "cobalt/bindings/testing/mozjs_interface_with_any_dictionary.h"
 #include "cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.h"
 #include "cobalt/bindings/testing/mozjs_named_constructor_interface.h"
 #include "cobalt/bindings/testing/mozjs_named_getter_interface.h"
@@ -177,6 +179,7 @@
 using cobalt::bindings::testing::ImplementedInterface;
 using cobalt::bindings::testing::IndexedGetterInterface;
 using cobalt::bindings::testing::InterfaceWithAny;
+using cobalt::bindings::testing::InterfaceWithAnyDictionary;
 using cobalt::bindings::testing::InterfaceWithUnsupportedProperties;
 using cobalt::bindings::testing::MozjsAnonymousIndexedGetterInterface;
 using cobalt::bindings::testing::MozjsAnonymousNamedGetterInterface;
@@ -208,6 +211,7 @@
 using cobalt::bindings::testing::MozjsImplementedInterface;
 using cobalt::bindings::testing::MozjsIndexedGetterInterface;
 using cobalt::bindings::testing::MozjsInterfaceWithAny;
+using cobalt::bindings::testing::MozjsInterfaceWithAnyDictionary;
 using cobalt::bindings::testing::MozjsInterfaceWithUnsupportedProperties;
 using cobalt::bindings::testing::MozjsNamedConstructorInterface;
 using cobalt::bindings::testing::MozjsNamedGetterInterface;
@@ -614,7 +618,7 @@
       WrapperPrivate::GetFromObject(context, object);
   Window* impl =
       wrapper_private->wrappable<Window>().get();
-  TypeTraits<CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
+  TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -952,7 +956,7 @@
 }
 
 inline InterfaceData* GetInterfaceData(JSContext* context) {
-  const int kInterfaceUniqueId = 50;
+  const int kInterfaceUniqueId = 51;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
   // By convention, the |MozjsGlobalEnvironment| that we are associated with
@@ -1186,6 +1190,10 @@
       base::Bind(MozjsInterfaceWithAny::CreateProxy),
       base::Bind(MozjsInterfaceWithAny::PrototypeClass));
   wrapper_factory->RegisterWrappableType(
+      InterfaceWithAnyDictionary::InterfaceWithAnyDictionaryWrappableType(),
+      base::Bind(MozjsInterfaceWithAnyDictionary::CreateProxy),
+      base::Bind(MozjsInterfaceWithAnyDictionary::PrototypeClass));
+  wrapper_factory->RegisterWrappableType(
       InterfaceWithUnsupportedProperties::InterfaceWithUnsupportedPropertiesWrappableType(),
       base::Bind(MozjsInterfaceWithUnsupportedProperties::CreateProxy),
       base::Bind(MozjsInterfaceWithUnsupportedProperties::PrototypeClass));
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
index 97ffad7..4ec2228 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
@@ -25,7 +25,9 @@
 #include <string>
 
 #include "base/optional.h"
+#include "cobalt/script/script_value.h"
 #include "cobalt/script/sequence.h"
+#include "cobalt/script/value_handle.h"
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 
 using cobalt::bindings::testing::ArbitraryInterface;
@@ -49,10 +51,73 @@
     string_member_ = std::string();
     has_interface_member_ = false;
     interface_member_ = scoped_refptr<ArbitraryInterface>();
-    has_member_with_default_ = true;
     member_with_default_ = 5;
     has_non_default_member_ = false;
     non_default_member_ = int32_t();
+    has_any_member_ = false;
+  }
+
+  TestDictionary(const TestDictionary& other) {
+    has_boolean_member_ = other.has_boolean_member_;
+    boolean_member_ = other.boolean_member_;
+    has_short_clamp_member_ = other.has_short_clamp_member_;
+    short_clamp_member_ = other.short_clamp_member_;
+    has_long_member_ = other.has_long_member_;
+    long_member_ = other.long_member_;
+    has_double_member_ = other.has_double_member_;
+    double_member_ = other.double_member_;
+    has_string_member_ = other.has_string_member_;
+    string_member_ = other.string_member_;
+    has_interface_member_ = other.has_interface_member_;
+    interface_member_ = other.interface_member_;
+    member_with_default_ = other.member_with_default_;
+    has_non_default_member_ = other.has_non_default_member_;
+    non_default_member_ = other.non_default_member_;
+    if (other.any_member_with_default_) {
+      any_member_with_default_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+              other.any_member_with_default_->referenced_value()));
+    }
+    has_any_member_ = other.has_any_member_;
+    if (other.any_member_) {
+      any_member_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+              other.any_member_->referenced_value()));
+    }
+  }
+
+  TestDictionary& operator=(const TestDictionary& other) {
+    has_boolean_member_ = other.has_boolean_member_;
+    boolean_member_ = other.boolean_member_;
+    has_short_clamp_member_ = other.has_short_clamp_member_;
+    short_clamp_member_ = other.short_clamp_member_;
+    has_long_member_ = other.has_long_member_;
+    long_member_ = other.long_member_;
+    has_double_member_ = other.has_double_member_;
+    double_member_ = other.double_member_;
+    has_string_member_ = other.has_string_member_;
+    string_member_ = other.string_member_;
+    has_interface_member_ = other.has_interface_member_;
+    interface_member_ = other.interface_member_;
+    member_with_default_ = other.member_with_default_;
+    has_non_default_member_ = other.has_non_default_member_;
+    non_default_member_ = other.non_default_member_;
+    if (other.any_member_with_default_) {
+      any_member_with_default_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+                other.any_member_with_default_->referenced_value()));
+    } else {
+      any_member_with_default_.reset();
+    }
+    has_any_member_ = other.has_any_member_;
+    if (other.any_member_) {
+      any_member_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(
+                other.any_member_->referenced_value()));
+    } else {
+      any_member_.reset();
+    }
+    return *this;
   }
 
   bool has_boolean_member() const {
@@ -106,7 +171,7 @@
   bool has_string_member() const {
     return has_string_member_;
   }
-  std::string string_member() const {
+  const std::string& string_member() const {
     DCHECK(has_string_member_);
     return string_member_;
   }
@@ -118,7 +183,7 @@
   bool has_interface_member() const {
     return has_interface_member_;
   }
-  scoped_refptr<ArbitraryInterface> interface_member() const {
+  const scoped_refptr<ArbitraryInterface>& interface_member() const {
     DCHECK(has_interface_member_);
     return interface_member_;
   }
@@ -128,14 +193,12 @@
   }
 
   bool has_member_with_default() const {
-    return has_member_with_default_;
+    return true;
   }
   int32_t member_with_default() const {
-    DCHECK(has_member_with_default_);
     return member_with_default_;
   }
   void set_member_with_default(int32_t value) {
-    has_member_with_default_ = true;
     member_with_default_ = value;
   }
 
@@ -151,6 +214,44 @@
     non_default_member_ = value;
   }
 
+  bool has_any_member_with_default() const {
+    return true;
+  }
+  const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* any_member_with_default() const {
+    if (!any_member_with_default_) {
+      return NULL;
+    }
+    return &(any_member_with_default_->referenced_value());
+  }
+  void set_any_member_with_default(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) {
+    if (value) {
+      any_member_with_default_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value));
+    } else {
+      any_member_with_default_.reset();
+    }
+  }
+
+  bool has_any_member() const {
+    return has_any_member_;
+  }
+  const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* any_member() const {
+    DCHECK(has_any_member_);
+    if (!any_member_) {
+      return NULL;
+    }
+    return &(any_member_->referenced_value());
+  }
+  void set_any_member(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) {
+    has_any_member_ = true;
+    if (value) {
+      any_member_.reset(
+          new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value));
+    } else {
+      any_member_.reset();
+    }
+  }
+
  private:
   bool has_boolean_member_;
   bool boolean_member_;
@@ -164,10 +265,12 @@
   std::string string_member_;
   bool has_interface_member_;
   scoped_refptr<ArbitraryInterface> interface_member_;
-  bool has_member_with_default_;
   int32_t member_with_default_;
   bool has_non_default_member_;
   int32_t non_default_member_;
+  scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_with_default_;
+  bool has_any_member_;
+  scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_;
 };
 
 // This ostream override is necessary for MOCK_METHODs commonly used
diff --git a/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template b/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
index cc878eb..3fb2623 100644
--- a/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
@@ -115,6 +115,7 @@
     exception_state->SetSimpleException(kSimpleError);
     return;
   }
+{% if not member.is_script_value %}
   if (!{{member.name}}.isUndefined()) {
     {{member.type}} converted_value;
     FromJSValue(context,
@@ -127,6 +128,7 @@
     }
     out_dictionary->set_{{member.name}}(converted_value);
   }
+{% endif %}
 {% endfor %}
 }
 
diff --git a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
index f276cbc..eefd158 100644
--- a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
@@ -115,6 +115,7 @@
     exception_state->SetSimpleException(kSimpleError);
     return;
   }
+{% if not member.is_script_value %}
   if (!{{member.name}}.isUndefined()) {
     {{member.type}} converted_value;
     FromJSValue(context,
@@ -127,6 +128,7 @@
     }
     out_dictionary->set_{{member.name}}(converted_value);
   }
+{% endif %}
 {% endfor %}
 }
 
diff --git a/src/cobalt/bindings/path_generator.py b/src/cobalt/bindings/path_generator.py
index 46206cb..362f4c1 100644
--- a/src/cobalt/bindings/path_generator.py
+++ b/src/cobalt/bindings/path_generator.py
@@ -48,6 +48,7 @@
 
   def NamespaceComponents(self, interface_name):
     """Get the interface's namespace as a list of namespace components."""
+
     # Get the IDL filename relative to the cobalt directory, and split the
     # directory to get the list of namespace components.
     if interface_name in self.interfaces_info:
@@ -59,9 +60,23 @@
     else:
       raise KeyError('Unknown interface name %s', interface_name)
 
-    idl_path = os.path.relpath(idl_path, self.interfaces_root)
-    components = os.path.dirname(idl_path).split(os.sep)
-    return [os.path.basename(self.interfaces_root)] + components
+    rel_idl_path = os.path.relpath(idl_path, self.interfaces_root)
+    components = os.path.dirname(rel_idl_path).split(os.sep)
+
+    # Check if this IDL's path lies in our interfaces root.  If it does not,
+    # we treat it as an extension IDL.
+    real_interfaces_root = os.path.realpath(self.interfaces_root)
+    real_idl_path = os.path.realpath(os.path.dirname(idl_path))
+    interfaces_root_is_in_components_path = (os.path.commonprefix(
+        [real_interfaces_root, real_idl_path]) == real_interfaces_root)
+
+    if interfaces_root_is_in_components_path:
+      return [os.path.basename(self.interfaces_root)] + components
+    else:
+      # If our IDL path lies outside of the cobalt/ directory, assume it is
+      # an externally defined web extension and assign it the 'webapi_extension'
+      # namespace.
+      return [os.path.basename(self.interfaces_root), 'webapi_extension']
 
   def Namespace(self, interface_name):
     """Get the interface's namespace."""
diff --git a/src/cobalt/bindings/templates/dictionary.h.template b/src/cobalt/bindings/templates/dictionary.h.template
index 20c58ff..3a8bc12 100644
--- a/src/cobalt/bindings/templates/dictionary.h.template
+++ b/src/cobalt/bindings/templates/dictionary.h.template
@@ -44,7 +44,9 @@
 #include <string>
 
 #include "base/optional.h"
+#include "cobalt/script/script_value.h"
 #include "cobalt/script/sequence.h"
+#include "cobalt/script/value_handle.h"
 {% for include in includes %}
 #include "{{include}}"
 {% endfor %}
@@ -71,34 +73,112 @@
  public:
   {{class_name}}() {
 {% for member in members %}
+{% if not member.default_value %}
+    has_{{member.name}}_ = false;
+{% endif %}
+{% if not member.is_script_value %}
 {% if member.default_value %}
-    has_{{member.name}}_ = true;
     {{member.name}}_ = {{member.default_value}};
 {% else %}
-    has_{{member.name}}_ = false;
     {{member.name}}_ = {{member.type}}();
 {% endif %}
+{% endif %}
 {% endfor %}
   }
 
+{% if parent %}
+  {{class_name}}(const {{class_name}}& other)
+    : {{parent}}(other) {
+{% else %}
+  {{class_name}}(const {{class_name}}& other) {
+{% endif %}
+{% for member in members %}
+{% if not member.default_value %}
+    has_{{member.name}}_ = other.has_{{member.name}}_;
+{% endif %}
+{% if not member.is_script_value %}
+    {{member.name}}_ = other.{{member.name}}_;
+{% else %}
+    if (other.{{member.name}}_) {
+      {{member.name}}_.reset(
+          new script::ScriptValue<{{member.type}}>::StrongReference(
+              other.{{member.name}}_->referenced_value()));
+    }
+{% endif %}
+{% endfor %}
+  }
+
+  {{class_name}}& operator=(const {{class_name}}& other) {
+{% if parent %}
+    {{parent}}::operator=(other);
+{% endif %}
+{% for member in members %}
+{% if not member.default_value %}
+    has_{{member.name}}_ = other.has_{{member.name}}_;
+{% endif %}
+{% if not member.is_script_value %}
+    {{member.name}}_ = other.{{member.name}}_;
+{% else %}
+    if (other.{{member.name}}_) {
+      {{member.name}}_.reset(
+          new script::ScriptValue<{{member.type}}>::StrongReference(
+                other.{{member.name}}_->referenced_value()));
+    } else {
+      {{member.name}}_.reset();
+    }
+{% endif %}
+{% endfor %}
+    return *this;
+  }
+
 {% for member in members %}
   bool has_{{member.name}}() const {
+{% if member.default_value %}
+    return true;
+{% else %}
     return has_{{member.name}}_;
+{% endif %}
   }
-  {{member.type}} {{member.name}}() const {
+  {{member.arg_type}} {{member.name}}() const {
+{% if not member.default_value %}
     DCHECK(has_{{member.name}}_);
+{% endif %}
+{% if member.is_script_value %}
+    if (!{{member.name}}_) {
+      return NULL;
+    }
+    return &({{member.name}}_->referenced_value());
+{% else %}
     return {{member.name}}_;
+{% endif %}
   }
   void set_{{member.name}}({{member.arg_type}} value) {
+{% if not member.default_value %}
     has_{{member.name}}_ = true;
+{% endif %}
+{% if member.is_script_value %}
+    if (value) {
+      {{member.name}}_.reset(
+          new script::ScriptValue<{{member.type}}>::StrongReference(*value));
+    } else {
+      {{member.name}}_.reset();
+    }
+{% else %}
     {{member.name}}_ = value;
+{% endif %}
   }
 
 {% endfor %}
  private:
 {% for member in members %}
+{% if not member.default_value %}
   bool has_{{member.name}}_;
+{% endif %}
+{% if member.is_script_value %}
+  scoped_ptr<script::ScriptValue<{{member.type}}>::StrongReference> {{member.name}}_;
+{% else %}
   {{member.type}} {{member.name}}_;
+{% endif %}
 {% endfor %}
 };
 
diff --git a/src/cobalt/bindings/testing/any_dictionary_bindings_test.cc b/src/cobalt/bindings/testing/any_dictionary_bindings_test.cc
new file mode 100644
index 0000000..1892843
--- /dev/null
+++ b/src/cobalt/bindings/testing/any_dictionary_bindings_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/logging.h"
+
+#include "cobalt/bindings/testing/bindings_test_base.h"
+#include "cobalt/bindings/testing/interface_with_any_dictionary.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ContainsRegex;
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+namespace {
+
+class AnyDictionaryBindingsTest
+    : public InterfaceBindingsTest<InterfaceWithAnyDictionary> {};
+
+TEST_F(AnyDictionaryBindingsTest, All) {
+  std::string result;
+
+  EXPECT_TRUE(EvaluateScript("test.hasAnyDefault()", &result)) << result;
+  EXPECT_STREQ("true", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("test.hasAny()", &result)) << result;
+  EXPECT_STREQ("false", result.c_str());
+
+  // We should be able to clear it out this way.
+  EXPECT_TRUE(EvaluateScript("test.setAny(undefined)", &result)) << result;
+  EXPECT_TRUE(EvaluateScript("test.getAny()", &result)) << result;
+  EXPECT_STREQ("undefined", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("test.setAny(2001)", &result)) << result;
+  EXPECT_TRUE(EvaluateScript("test.getAny()", &result)) << result;
+  EXPECT_STREQ("2001", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("test.setAny(1.21)", &result)) << result;
+  EXPECT_TRUE(EvaluateScript("test.getAny()", &result)) << result;
+  EXPECT_STREQ("1.21", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("test.setAny('test')", &result)) << result;
+  EXPECT_TRUE(EvaluateScript("test.getAny()", &result)) << result;
+  EXPECT_STREQ("test", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("test.setAny(new Object())", &result)) << result;
+  EXPECT_TRUE(EvaluateScript("test.getAny()", &result)) << result;
+  EXPECT_STREQ("[object Object]", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("test.setAny(new ArbitraryInterface())", &result))
+      << result;
+  EXPECT_TRUE(EvaluateScript("test.getAny()", &result)) << result;
+  EXPECT_STREQ("[object ArbitraryInterface]", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("test.setAny(null)", &result)) << result;
+  EXPECT_TRUE(EvaluateScript("test.getAny()", &result)) << result;
+  EXPECT_STREQ("null", result.c_str());
+}
+
+}  // namespace
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/testing/interface_with_any_dictionary.h b/src/cobalt/bindings/testing/interface_with_any_dictionary.h
new file mode 100644
index 0000000..9b6608d
--- /dev/null
+++ b/src/cobalt/bindings/testing/interface_with_any_dictionary.h
@@ -0,0 +1,57 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BINDINGS_TESTING_INTERFACE_WITH_ANY_DICTIONARY_H_
+#define COBALT_BINDINGS_TESTING_INTERFACE_WITH_ANY_DICTIONARY_H_
+
+#include "base/compiler_specific.h"
+
+#include "cobalt/bindings/testing/test_dictionary.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/value_handle.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class InterfaceWithAnyDictionary : public script::Wrappable {
+ public:
+  InterfaceWithAnyDictionary() {}
+
+  bool HasAnyDefault() const {
+    return dictionary_.has_any_member_with_default();
+  }
+
+  bool HasAny() const { return dictionary_.has_any_member(); }
+
+  const script::ValueHandleHolder* GetAny() const {
+    return dictionary_.any_member();
+  }
+
+  void SetAny(const script::ValueHandleHolder& value) {
+    dictionary_.set_any_member(&value);
+  }
+
+  DEFINE_WRAPPABLE_TYPE(InterfaceWithAnyDictionary);
+
+ private:
+  TestDictionary dictionary_;
+};
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
+
+#endif  // COBALT_BINDINGS_TESTING_INTERFACE_WITH_ANY_DICTIONARY_H_
diff --git a/src/starboard/win/lib/atomic_public.h b/src/cobalt/bindings/testing/interface_with_any_dictionary.idl
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/cobalt/bindings/testing/interface_with_any_dictionary.idl
index be4e805..4c909d5 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/cobalt/bindings/testing/interface_with_any_dictionary.idl
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+[ Constructor ]
+interface InterfaceWithAnyDictionary {
+  boolean hasAnyDefault();
+  boolean hasAny();
+  any getAny();
+  void setAny(any value);
+};
diff --git a/src/cobalt/bindings/testing/test_dictionary.idl b/src/cobalt/bindings/testing/test_dictionary.idl
index c05dc58..5a0078c 100644
--- a/src/cobalt/bindings/testing/test_dictionary.idl
+++ b/src/cobalt/bindings/testing/test_dictionary.idl
@@ -24,4 +24,7 @@
 
   long memberWithDefault = 5;
   long nonDefaultMember;
+
+  any anyMemberWithDefault = null;
+  any anyMember;
 };
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp
index bc92e7c..2ef0d01 100644
--- a/src/cobalt/bindings/testing/testing.gyp
+++ b/src/cobalt/bindings/testing/testing.gyp
@@ -50,6 +50,7 @@
         'global_interface_parent.idl',
         'indexed_getter_interface.idl',
         'interface_with_any.idl',
+        'interface_with_any_dictionary.idl',
         'interface_with_unsupported_properties.idl',
         'named_constructor_interface.idl',
         'named_getter_interface.idl',
@@ -142,6 +143,7 @@
       'type': '<(gtest_target_type)',
       'sources': [
         'any_bindings_test.cc',
+        'any_dictionary_bindings_test.cc',
         'boolean_type_bindings_test.cc',
         'callback_function_test.cc',
         'callback_interface_test.cc',
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index d97bbbb..9f855ca 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -33,6 +33,7 @@
 #include "cobalt/base/application_event.h"
 #include "cobalt/base/cobalt_paths.h"
 #include "cobalt/base/deep_link_event.h"
+#include "cobalt/base/get_application_key.h"
 #include "cobalt/base/init_cobalt.h"
 #include "cobalt/base/language.h"
 #include "cobalt/base/localized_strings.h"
@@ -365,6 +366,8 @@
     "h5vcc-location-src "
     "https://www.youtube.com/tv "
     "https://www.youtube.com/tv/ "
+    "https://web-green-qa.youtube.com/tv "
+    "https://web-green-qa.youtube.com/tv/ "
     "https://web-release-qa.youtube.com/tv "
     "https://web-release-qa.youtube.com/tv/ "
 #if defined(ENABLE_ABOUT_SCHEME)
@@ -497,6 +500,17 @@
   }
 #endif
 
+  base::optional<std::string> initial_key =
+      base::GetApplicationKey(initial_url);
+  options.storage_manager_options.savegame_options.id = initial_key;
+
+  base::optional<std::string> default_key =
+      base::GetApplicationKey(GURL(kDefaultURL));
+  if (initial_key == default_key) {
+    options.storage_manager_options.savegame_options.fallback_to_default_id =
+        true;
+  }
+
   // User can specify an extra search path entry for files loaded via file://.
   options.web_module_options.extra_web_file_dir = GetExtraWebFileDir();
   options.web_module_options.location_policy = kYouTubeTvLocationPolicy;
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 0ea1fe2..97fff2f 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -66,6 +66,8 @@
         'memory_tracker/tool/log_writer_tool.h',
         'memory_tracker/tool/malloc_stats_tool.cc',
         'memory_tracker/tool/malloc_stats_tool.h',
+        'memory_tracker/tool/malloc_logger_tool.cc',
+        'memory_tracker/tool/malloc_logger_tool.h',
         'memory_tracker/tool/memory_size_binner_tool.cc',
         'memory_tracker/tool/memory_size_binner_tool.h',
         'memory_tracker/tool/params.cc',
@@ -150,6 +152,7 @@
         '<(DEPTH)/nb/nb.gyp:nb',
         'browser_bindings.gyp:bindings',
         'screen_shot_writer',
+        '<(cobalt_webapi_extension_gyp_target)',
       ],
       # This target doesn't generate any headers, but it exposes generated
       # header files (for idl dictionaries) through this module's public header
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index 392a6f5..92f4157 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -209,6 +209,8 @@
         '../xhr/xml_http_request.idl',
         '../xhr/xml_http_request_event_target.idl',
         '../xhr/xml_http_request_upload.idl',
+
+        '<@(cobalt_webapi_extension_source_idl_files)',
     ],
 
     # IDL files that will end up generating a .h that will be #included in
@@ -243,6 +245,7 @@
         '../web_animations/animation_fill_mode.idl',
         '../web_animations/animation_playback_direction.idl',
         '../websocket/close_event_init.idl',
+        '<@(cobalt_webapi_extension_generated_header_idl_files)',
     ],
 
     # Partial interfaces and the right-side of "implements". Also includes
@@ -291,6 +294,7 @@
             '../dom/eme/media_encrypted_event.idl',
             '../dom/eme/media_key_message_event.idl',
             '../dom/eme/media_key_session.idl',
+            '../dom/eme/media_key_status_map.idl',
             '../dom/eme/media_key_system_access.idl',
             '../dom/eme/media_keys.idl',
         ],
@@ -299,6 +303,7 @@
             '../dom/eme/media_key_message_event_init.idl',
             '../dom/eme/media_key_message_type.idl',
             '../dom/eme/media_key_session_type.idl',
+            '../dom/eme/media_key_status.idl',
             '../dom/eme/media_key_system_configuration.idl',
             '../dom/eme/media_key_system_media_capability.idl',
             '../dom/eme/media_keys_requirement.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 905991b..a9eb520 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -35,6 +35,7 @@
 #include "cobalt/browser/screen_shot_writer.h"
 #include "cobalt/browser/storage_upgrade_handler.h"
 #include "cobalt/browser/switches.h"
+#include "cobalt/browser/webapi_extension.h"
 #include "cobalt/dom/csp_delegate_factory.h"
 #include "cobalt/dom/keycode.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
@@ -171,11 +172,21 @@
 scoped_refptr<script::Wrappable> CreateH5VCC(
     const h5vcc::H5vcc::Settings& settings,
     const scoped_refptr<dom::Window>& window,
-    dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+    dom::MutationObserverTaskManager* mutation_observer_task_manager,
+    script::GlobalEnvironment* global_environment) {
+  UNREFERENCED_PARAMETER(global_environment);
   return scoped_refptr<script::Wrappable>(
       new h5vcc::H5vcc(settings, window, mutation_observer_task_manager));
 }
 
+scoped_refptr<script::Wrappable> CreateExtensionInterface(
+    const scoped_refptr<dom::Window>& window,
+    dom::MutationObserverTaskManager* mutation_observer_task_manager,
+    script::GlobalEnvironment* global_environment) {
+  UNREFERENCED_PARAMETER(mutation_observer_task_manager);
+  return CreateWebAPIExtensionObject(window, global_environment);
+}
+
 renderer::RendererModule::Options RendererModuleWithCameraOptions(
     renderer::RendererModule::Options options,
     scoped_refptr<input::Camera3D> camera_3d) {
@@ -197,10 +208,9 @@
       options_(options),
       self_message_loop_(MessageLoop::current()),
       event_dispatcher_(event_dispatcher),
-      storage_manager_(
-          scoped_ptr<StorageUpgradeHandler>(new StorageUpgradeHandler(url))
-              .PassAs<storage::StorageManager::UpgradeHandler>(),
-          options_.storage_manager_options),
+      storage_manager_(make_scoped_ptr(new StorageUpgradeHandler(url))
+                           .PassAs<storage::StorageManager::UpgradeHandler>(),
+                       options_.storage_manager_options),
 #if defined(OS_STARBOARD)
       is_rendered_(false),
 #endif  // OS_STARBOARD
@@ -272,6 +282,14 @@
   options_.web_module_options.injected_window_attributes["h5vcc"] =
       base::Bind(&CreateH5VCC, h5vcc_settings);
 
+  base::optional<std::string> extension_object_name =
+      GetWebAPIExtensionObjectPropertyName();
+  if (extension_object_name) {
+    options_.web_module_options
+        .injected_window_attributes[*extension_object_name] =
+        base::Bind(&CreateExtensionInterface);
+  }
+
 #if defined(ENABLE_DEBUG_CONSOLE) && defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kInputFuzzer)) {
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp
index 473f353..9c1f00b 100644
--- a/src/cobalt/browser/cobalt.gyp
+++ b/src/cobalt/browser/cobalt.gyp
@@ -25,18 +25,14 @@
       ],
       'conditions': [
         ['cobalt_enable_lib == 1', {
-          'sources': ['lib/main.cc',],
-          'all_dependent_settings': {
-            'target_conditions': [
-              ['_type=="executable" and _toolset=="target"', {
-                'sources': [
-                  'lib/imported/main_stub.cc',
-                ],
-              }],
-            ],
-          },
+          'sources': [
+            'lib/cobalt.def',
+            'lib/main.cc',
+          ],
         }, {
-          'sources': ['main.cc',],
+          'sources': [
+            'main.cc',
+          ],
         }],
         ['cobalt_copy_test_data == 1', {
           'dependencies': [
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc
index 0813095..79a6cd5 100644
--- a/src/cobalt/browser/debug_console.cc
+++ b/src/cobalt/browser/debug_console.cc
@@ -150,9 +150,11 @@
     const debug::DebugHub::GetHudModeCallback& get_hud_mode_function,
     const debug::Debugger::GetDebugServerCallback& get_debug_server_callback,
     const scoped_refptr<dom::Window>& window,
-    dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+    dom::MutationObserverTaskManager* mutation_observer_task_manager,
+    script::GlobalEnvironment* global_environment) {
   UNREFERENCED_PARAMETER(window);
   UNREFERENCED_PARAMETER(mutation_observer_task_manager);
+  UNREFERENCED_PARAMETER(global_environment);
   return new debug::DebugHub(get_hud_mode_function, get_debug_server_callback);
 }
 
diff --git a/src/cobalt/browser/lib/cobalt.def b/src/cobalt/browser/lib/cobalt.def
new file mode 100644
index 0000000..966eca6
--- /dev/null
+++ b/src/cobalt/browser/lib/cobalt.def
@@ -0,0 +1,652 @@
+; This file defines functions which are explicitly exported by cobalt.dll.
+; More info can be found here:
+;   https://msdn.microsoft.com/en-us/library/d91k01sh.aspx
+; And broader context is explained here:
+;   https://blogs.msdn.microsoft.com/oldnewthing/20140321-00/?p=1433/
+
+EXPORTS
+    ; From starboard/shared/lib/exported/starboard_main.h:
+    StarboardMain
+
+    ; From cobalt/browser/lib/exported/main.h:
+    CbLibMainSetCallbackRegistrationReadyCallback
+    CbLibMainSetOnCobaltInitializedCallback
+    CbLibMainSetHandleEventCallback
+
+    ; From cobalt/render/rasterizer/lib/exported/graphics.h:
+    CbLibGraphicsSetContextCreatedCallback
+    CbLibGraphicsSetBeginRenderFrameCallback
+    CbLibGraphicsSetEndRenderFrameCallback
+
+    ; From cobalt/render/rasterizer/lib/exported/video.h:
+    CbLibVideoSetOnUpdateProjectionType
+    CbLibVideoSetOnUpdateMeshes
+    CbLibVideoSetOnUpdateStereoMode
+    CbLibVideoSetOnUpdateRgbTextureId
+
+    ; Following GL functions are copied from libGLESv2.def and EGL from
+    ; libEGL.def. We export these so that host-applications may use the same
+    ; GLES & EGL implementation that Cobalt is using for rendering.
+    glActiveTexture
+    glAttachShader
+    glBindAttribLocation
+    glBindBuffer
+    glBindFramebuffer
+    glBindRenderbuffer
+    glBindTexture
+    glBlendColor
+    glBlendEquation
+    glBlendEquationSeparate
+    glBlendFunc
+    glBlendFuncSeparate
+    glBufferData
+    glBufferSubData
+    glCheckFramebufferStatus
+    glClear
+    glClearColor
+    glClearDepthf
+    glClearStencil
+    glColorMask
+    glCompileShader
+    glCompressedTexImage2D
+    glCompressedTexSubImage2D
+    glCopyTexImage2D
+    glCopyTexSubImage2D
+    glCreateProgram
+    glCreateShader
+    glCullFace
+    glDeleteBuffers
+    glDeleteFramebuffers
+    glDeleteProgram
+    glDeleteRenderbuffers
+    glDeleteShader
+    glDeleteTextures
+    glDepthFunc
+    glDepthMask
+    glDepthRangef
+    glDetachShader
+    glDisable
+    glDisableVertexAttribArray
+    glDrawArrays
+    glDrawElements
+    glEnable
+    glEnableVertexAttribArray
+    glFinish
+    glFlush
+    glFramebufferRenderbuffer
+    glFramebufferTexture2D
+    glFrontFace
+    glGenBuffers
+    glGenFramebuffers
+    glGenRenderbuffers
+    glGenTextures
+    glGenerateMipmap
+    glGetActiveAttrib
+    glGetActiveUniform
+    glGetAttachedShaders
+    glGetAttribLocation
+    glGetBooleanv
+    glGetBufferParameteriv
+    glGetError
+    glGetFloatv
+    glGetFramebufferAttachmentParameteriv
+    glGetIntegerv
+    glGetProgramInfoLog
+    glGetProgramiv
+    glGetRenderbufferParameteriv
+    glGetShaderInfoLog
+    glGetShaderPrecisionFormat
+    glGetShaderSource
+    glGetShaderiv
+    glGetString
+    glGetTexParameterfv
+    glGetTexParameteriv
+    glGetUniformLocation
+    glGetUniformfv
+    glGetUniformiv
+    glGetVertexAttribPointerv
+    glGetVertexAttribfv
+    glGetVertexAttribiv
+    glHint
+    glIsBuffer
+    glIsEnabled
+    glIsFramebuffer
+    glIsProgram
+    glIsRenderbuffer
+    glIsShader
+    glIsTexture
+    glLineWidth
+    glLinkProgram
+    glPixelStorei
+    glPolygonOffset
+    glReadPixels
+    glReleaseShaderCompiler
+    glRenderbufferStorage
+    glSampleCoverage
+    glScissor
+    glShaderBinary
+    glShaderSource
+    glStencilFunc
+    glStencilFuncSeparate
+    glStencilMask
+    glStencilMaskSeparate
+    glStencilOp
+    glStencilOpSeparate
+    glTexImage2D
+    glTexParameterf
+    glTexParameterfv
+    glTexParameteri
+    glTexParameteriv
+    glTexSubImage2D
+    glUniform1f
+    glUniform1fv
+    glUniform1i
+    glUniform1iv
+    glUniform2f
+    glUniform2fv
+    glUniform2i
+    glUniform2iv
+    glUniform3f
+    glUniform3fv
+    glUniform3i
+    glUniform3iv
+    glUniform4f
+    glUniform4fv
+    glUniform4i
+    glUniform4iv
+    glUniformMatrix2fv
+    glUniformMatrix3fv
+    glUniformMatrix4fv
+    glUseProgram
+    glValidateProgram
+    glVertexAttrib1f
+    glVertexAttrib1fv
+    glVertexAttrib2f
+    glVertexAttrib2fv
+    glVertexAttrib3f
+    glVertexAttrib3fv
+    glVertexAttrib4f
+    glVertexAttrib4fv
+    glVertexAttribPointer
+    glViewport
+
+    ; Extensions
+    glBlitFramebufferANGLE
+    glRenderbufferStorageMultisampleANGLE
+    glDeleteFencesNV
+    glFinishFenceNV
+    glGenFencesNV
+    glGetFenceivNV
+    glIsFenceNV
+    glSetFenceNV
+    glTestFenceNV
+    glGetTranslatedShaderSourceANGLE
+    glTexStorage2DEXT
+    glGetGraphicsResetStatusEXT
+    glReadnPixelsEXT
+    glGetnUniformfvEXT
+    glGetnUniformivEXT
+    glGenQueriesEXT
+    glDeleteQueriesEXT
+    glIsQueryEXT
+    glBeginQueryEXT
+    glEndQueryEXT
+    glGetQueryivEXT
+    glGetQueryObjectuivEXT
+    glVertexAttribDivisorANGLE
+    glDrawArraysInstancedANGLE
+    glDrawElementsInstancedANGLE
+    glProgramBinaryOES
+    glGetProgramBinaryOES
+    glDrawBuffersEXT
+    glMapBufferOES
+    glUnmapBufferOES
+    glGetBufferPointervOES
+    glMapBufferRangeEXT
+    glFlushMappedBufferRangeEXT
+    glDiscardFramebufferEXT
+    glInsertEventMarkerEXT
+    glPushGroupMarkerEXT
+    glPopGroupMarkerEXT
+    glEGLImageTargetTexture2DOES
+    glEGLImageTargetRenderbufferStorageOES
+    glBindVertexArrayOES
+    glDeleteVertexArraysOES
+    glGenVertexArraysOES
+    glIsVertexArrayOES
+    glDebugMessageControlKHR
+    glDebugMessageInsertKHR
+    glDebugMessageCallbackKHR
+    glGetDebugMessageLogKHR
+    glPushDebugGroupKHR
+    glPopDebugGroupKHR
+    glObjectLabelKHR
+    glGetObjectLabelKHR
+    glObjectPtrLabelKHR
+    glGetObjectPtrLabelKHR
+    glGetPointervKHR
+    glQueryCounterEXT
+    glGetQueryObjectivEXT
+    glGetQueryObjecti64vEXT
+    glGetQueryObjectui64vEXT
+    glBindUniformLocationCHROMIUM
+    glCoverageModulationCHROMIUM
+
+    glMatrixLoadfCHROMIUM
+    glMatrixLoadIdentityCHROMIUM
+    glGenPathsCHROMIUM
+    glDeletePathsCHROMIUM
+    glIsPathCHROMIUM
+    glPathCommandsCHROMIUM
+    glPathParameterfCHROMIUM
+    glPathParameteriCHROMIUM
+    glGetPathParameterfvCHROMIUM
+    glGetPathParameterivCHROMIUM
+    glPathStencilFuncCHROMIUM
+    glStencilFillPathCHROMIUM
+    glStencilStrokePathCHROMIUM
+    glCoverFillPathCHROMIUM
+    glCoverStrokePathCHROMIUM
+    glStencilThenCoverFillPathCHROMIUM
+    glStencilThenCoverStrokePathCHROMIUM
+    glCoverFillPathInstancedCHROMIUM
+    glCoverStrokePathInstancedCHROMIUM
+    glStencilStrokePathInstancedCHROMIUM
+    glStencilFillPathInstancedCHROMIUM
+    glStencilThenCoverFillPathInstancedCHROMIUM
+    glStencilThenCoverStrokePathInstancedCHROMIUM
+    glBindFragmentInputLocationCHROMIUM
+    glProgramPathFragmentInputGenCHROMIUM
+
+    ; GLES 3.0 Functions
+    glReadBuffer
+    glDrawRangeElements
+    glTexImage3D
+    glTexSubImage3D
+    glCopyTexSubImage3D
+    glCompressedTexImage3D
+    glCompressedTexSubImage3D
+    glGenQueries
+    glDeleteQueries
+    glIsQuery
+    glBeginQuery
+    glEndQuery
+    glGetQueryiv
+    glGetQueryObjectuiv
+    glUnmapBuffer
+    glGetBufferPointerv
+    glDrawBuffers
+    glUniformMatrix2x3fv
+    glUniformMatrix3x2fv
+    glUniformMatrix2x4fv
+    glUniformMatrix4x2fv
+    glUniformMatrix3x4fv
+    glUniformMatrix4x3fv
+    glBlitFramebuffer
+    glRenderbufferStorageMultisample
+    glFramebufferTextureLayer
+    glMapBufferRange
+    glFlushMappedBufferRange
+    glBindVertexArray
+    glDeleteVertexArrays
+    glGenVertexArrays
+    glIsVertexArray
+    glGetIntegeri_v
+    glBeginTransformFeedback
+    glEndTransformFeedback
+    glBindBufferRange
+    glBindBufferBase
+    glTransformFeedbackVaryings
+    glGetTransformFeedbackVarying
+    glVertexAttribIPointer
+    glGetVertexAttribIiv
+    glGetVertexAttribIuiv
+    glVertexAttribI4i
+    glVertexAttribI4ui
+    glVertexAttribI4iv
+    glVertexAttribI4uiv
+    glGetUniformuiv
+    glGetFragDataLocation
+    glUniform1ui
+    glUniform2ui
+    glUniform3ui
+    glUniform4ui
+    glUniform1uiv
+    glUniform2uiv
+    glUniform3uiv
+    glUniform4uiv
+    glClearBufferiv
+    glClearBufferuiv
+    glClearBufferfv
+    glClearBufferfi
+    glGetStringi
+    glCopyBufferSubData
+    glGetUniformIndices
+    glGetActiveUniformsiv
+    glGetUniformBlockIndex
+    glGetActiveUniformBlockiv
+    glGetActiveUniformBlockName
+    glUniformBlockBinding
+    glDrawArraysInstanced
+    glDrawElementsInstanced
+    glFenceSync
+    glIsSync
+    glDeleteSync
+    glClientWaitSync
+    glWaitSync
+    glGetInteger64v
+    glGetSynciv
+    glGetInteger64i_v
+    glGetBufferParameteri64v
+    glGenSamplers
+    glDeleteSamplers
+    glIsSampler
+    glBindSampler
+    glSamplerParameteri
+    glSamplerParameteriv
+    glSamplerParameterf
+    glSamplerParameterfv
+    glGetSamplerParameteriv
+    glGetSamplerParameterfv
+    glVertexAttribDivisor
+    glBindTransformFeedback
+    glDeleteTransformFeedbacks
+    glGenTransformFeedbacks
+    glIsTransformFeedback
+    glPauseTransformFeedback
+    glResumeTransformFeedback
+    glGetProgramBinary
+    glProgramBinary
+    glProgramParameteri
+    glInvalidateFramebuffer
+    glInvalidateSubFramebuffer
+    glTexStorage2D
+    glTexStorage3D
+    glGetInternalformativ
+
+    ; GLES 3.1 Functions
+    glDispatchCompute
+    glDispatchComputeIndirect
+    glDrawArraysIndirect
+    glDrawElementsIndirect
+    glFramebufferParameteri
+    glGetFramebufferParameteriv
+    glGetProgramInterfaceiv
+    glGetProgramResourceIndex
+    glGetProgramResourceName
+    glGetProgramResourceiv
+    glGetProgramResourceLocation
+    glUseProgramStages
+    glActiveShaderProgram
+    glCreateShaderProgramv
+    glBindProgramPipeline
+    glDeleteProgramPipelines
+    glGenProgramPipelines
+    glIsProgramPipeline
+    glGetProgramPipelineiv
+    glProgramUniform1i
+    glProgramUniform2i
+    glProgramUniform3i
+    glProgramUniform4i
+    glProgramUniform1ui
+    glProgramUniform2ui
+    glProgramUniform3ui
+    glProgramUniform4ui
+    glProgramUniform1f
+    glProgramUniform2f
+    glProgramUniform3f
+    glProgramUniform4f
+    glProgramUniform1iv
+    glProgramUniform2iv
+    glProgramUniform3iv
+    glProgramUniform4iv
+    glProgramUniform1uiv
+    glProgramUniform2uiv
+    glProgramUniform3uiv
+    glProgramUniform4uiv
+    glProgramUniform1fv
+    glProgramUniform2fv
+    glProgramUniform3fv
+    glProgramUniform4fv
+    glProgramUniformMatrix2fv
+    glProgramUniformMatrix3fv
+    glProgramUniformMatrix4fv
+    glProgramUniformMatrix2x3fv
+    glProgramUniformMatrix3x2fv
+    glProgramUniformMatrix2x4fv
+    glProgramUniformMatrix4x2fv
+    glProgramUniformMatrix3x4fv
+    glProgramUniformMatrix4x3fv
+    glValidateProgramPipeline
+    glGetProgramPipelineInfoLog
+    glBindImageTexture
+    glGetBooleani_v
+    glMemoryBarrier
+    glMemoryBarrierByRegion
+    glTexStorage2DMultisample
+    glGetMultisamplefv
+    glSampleMaski
+    glGetTexLevelParameteriv
+    glGetTexLevelParameterfv
+    glBindVertexBuffer
+    glVertexAttribFormat
+    glVertexAttribIFormat
+    glVertexAttribBinding
+    glVertexBindingDivisor
+
+    ; ANGLE Platform Implementation
+    ANGLEGetDisplayPlatform
+    ANGLEResetDisplayPlatform
+    eglBindAPI
+    eglBindTexImage
+    eglChooseConfig
+    eglCopyBuffers
+    eglCreateContext
+    eglCreatePbufferFromClientBuffer
+    eglCreatePbufferSurface
+    eglCreatePixmapSurface
+    eglCreateWindowSurface
+    eglDestroyContext
+    eglDestroySurface
+    eglGetConfigAttrib
+    eglGetConfigs
+    eglGetCurrentContext
+    eglGetCurrentDisplay
+    eglGetCurrentSurface
+    eglGetDisplay
+    eglGetError
+    eglGetProcAddress
+    eglInitialize
+    eglMakeCurrent
+    eglQueryAPI
+    eglQueryContext
+    eglQueryString
+    eglQuerySurface
+    eglReleaseTexImage
+    eglReleaseThread
+    eglSurfaceAttrib
+    eglSwapBuffers
+    eglSwapInterval
+    eglTerminate
+    eglWaitClient
+    eglWaitGL
+    eglWaitNative
+
+    ; Extensions
+    eglGetPlatformDisplayEXT
+    eglQuerySurfacePointerANGLE
+    eglPostSubBufferNV
+    eglQueryDisplayAttribEXT
+    eglQueryDeviceAttribEXT
+    eglQueryDeviceStringEXT
+    eglCreateImageKHR
+    eglDestroyImageKHR
+    eglCreateDeviceANGLE
+    eglReleaseDeviceANGLE
+    eglCreateStreamKHR
+    eglDestroyStreamKHR
+    eglStreamAttribKHR
+    eglQueryStreamKHR
+    eglQueryStreamu64KHR
+    eglStreamConsumerGLTextureExternalKHR
+    eglStreamConsumerAcquireKHR
+    eglStreamConsumerReleaseKHR
+    eglStreamConsumerGLTextureExternalAttribsNV
+    eglCreateStreamProducerD3DTextureNV12ANGLE
+    eglStreamPostD3DTextureNV12ANGLE
+    eglGetSyncValuesCHROMIUM
+    eglSwapBuffersWithDamageEXT
+
+    ; 1.5 entry points
+    eglCreateSync
+    eglDestroySync
+    eglClientWaitSync
+    eglGetSyncAttrib
+    eglCreateImage
+    eglDestroyImage
+    eglGetPlatformDisplay
+    eglCreatePlatformWindowSurface
+    eglCreatePlatformPixmapSurface
+    eglWaitSync
+    eglBindAPI
+    eglBindTexImage
+    eglChooseConfig
+    eglCopyBuffers
+    eglCreateContext
+    eglCreatePbufferFromClientBuffer
+    eglCreatePbufferSurface
+    eglCreatePixmapSurface
+    eglCreateWindowSurface
+    eglDestroyContext
+    eglDestroySurface
+    eglGetConfigAttrib
+    eglGetConfigs
+    eglGetCurrentContext
+    eglGetCurrentDisplay
+    eglGetCurrentSurface
+    eglGetDisplay
+    eglGetError
+    eglGetProcAddress
+    eglInitialize
+    eglMakeCurrent
+    eglQueryAPI
+    eglQueryContext
+    eglQueryString
+    eglQuerySurface
+    eglReleaseTexImage
+    eglReleaseThread
+    eglSurfaceAttrib
+    eglSwapBuffers
+    eglSwapInterval
+    eglTerminate
+    eglWaitClient
+    eglWaitGL
+    eglWaitNative
+
+    ; Extensions
+    eglGetPlatformDisplayEXT
+    eglQuerySurfacePointerANGLE
+    eglPostSubBufferNV
+    eglQueryDisplayAttribEXT
+    eglQueryDeviceAttribEXT
+    eglQueryDeviceStringEXT
+    eglCreateImageKHR
+    eglDestroyImageKHR
+    eglCreateDeviceANGLE
+    eglReleaseDeviceANGLE
+    eglCreateStreamKHR
+    eglDestroyStreamKHR
+    eglStreamAttribKHR
+    eglQueryStreamKHR
+    eglQueryStreamu64KHR
+    eglStreamConsumerGLTextureExternalKHR
+    eglStreamConsumerAcquireKHR
+    eglStreamConsumerReleaseKHR
+    eglStreamConsumerGLTextureExternalAttribsNV
+    eglCreateStreamProducerD3DTextureNV12ANGLE
+    eglStreamPostD3DTextureNV12ANGLE
+    eglGetSyncValuesCHROMIUM
+    eglSwapBuffersWithDamageEXT
+
+    ; 1.5 entry points
+    eglCreateSync
+    eglDestroySync
+    eglClientWaitSync
+    eglGetSyncAttrib
+    eglCreateImage
+    eglDestroyImage
+    eglGetPlatformDisplay
+    eglCreatePlatformWindowSurface
+    eglCreatePlatformPixmapSurface
+    eglWaitSync
+    eglBindAPI
+    eglBindTexImage
+    eglChooseConfig
+    eglCopyBuffers
+    eglCreateContext
+    eglCreatePbufferFromClientBuffer
+    eglCreatePbufferSurface
+    eglCreatePixmapSurface
+    eglCreateWindowSurface
+    eglDestroyContext
+    eglDestroySurface
+    eglGetConfigAttrib
+    eglGetConfigs
+    eglGetCurrentContext
+    eglGetCurrentDisplay
+    eglGetCurrentSurface
+    eglGetDisplay
+    eglGetError
+    eglGetProcAddress
+    eglInitialize
+    eglMakeCurrent
+    eglQueryAPI
+    eglQueryContext
+    eglQueryString
+    eglQuerySurface
+    eglReleaseTexImage
+    eglReleaseThread
+    eglSurfaceAttrib
+    eglSwapBuffers
+    eglSwapInterval
+    eglTerminate
+    eglWaitClient
+    eglWaitGL
+    eglWaitNative
+
+    ; Extensions
+    eglGetPlatformDisplayEXT
+    eglQuerySurfacePointerANGLE
+    eglPostSubBufferNV
+    eglQueryDisplayAttribEXT
+    eglQueryDeviceAttribEXT
+    eglQueryDeviceStringEXT
+    eglCreateImageKHR
+    eglDestroyImageKHR
+    eglCreateDeviceANGLE
+    eglReleaseDeviceANGLE
+    eglCreateStreamKHR
+    eglDestroyStreamKHR
+    eglStreamAttribKHR
+    eglQueryStreamKHR
+    eglQueryStreamu64KHR
+    eglStreamConsumerGLTextureExternalKHR
+    eglStreamConsumerAcquireKHR
+    eglStreamConsumerReleaseKHR
+    eglStreamConsumerGLTextureExternalAttribsNV
+    eglCreateStreamProducerD3DTextureNV12ANGLE
+    eglStreamPostD3DTextureNV12ANGLE
+    eglGetSyncValuesCHROMIUM
+    eglSwapBuffersWithDamageEXT
+
+    ; 1.5 entry points
+    eglCreateSync
+    eglDestroySync
+    eglClientWaitSync
+    eglGetSyncAttrib
+    eglCreateImage
+    eglDestroyImage
+    eglGetPlatformDisplay
+    eglCreatePlatformWindowSurface
+    eglCreatePlatformPixmapSurface
+    eglWaitSync
diff --git a/src/cobalt/browser/lib/exported/main.h b/src/cobalt/browser/lib/exported/main.h
new file mode 100644
index 0000000..087cb52
--- /dev/null
+++ b/src/cobalt/browser/lib/exported/main.h
@@ -0,0 +1,53 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// All imported functions defined below MUST be implemented by client
+// applications.
+
+#ifndef COBALT_BROWSER_LIB_EXPORTED_MAIN_H_
+#define COBALT_BROWSER_LIB_EXPORTED_MAIN_H_
+
+#include "starboard/event.h"
+#include "starboard/export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*CbLibMainCallbackRegistrationReadyCallback)(void* context);
+typedef void (*CbLibMainOnCobaltInitializedCallback)(void* context);
+typedef bool (*CbLibMainHandleEventCallback)(void* context,
+                                             const SbEvent* event);
+
+// Sets a callback which will be called once Cobalt is ready to accept
+// further callbacks. This is the only callback that may be set before
+// intializing Cobalt; as soon as this callback is called, all remaining
+// callbacks may be set.
+SB_EXPORT_PLATFORM void CbLibMainSetCallbackRegistrationReadyCallback(
+    void* context, CbLibMainCallbackRegistrationReadyCallback callback);
+
+// Sets a callback which will be called after Cobalt has been initialized.
+SB_EXPORT_PLATFORM void CbLibMainSetOnCobaltInitializedCallback(
+    void* context, CbLibMainOnCobaltInitializedCallback callback);
+
+// Sets a callback which will be called when Cobalt is receiving an event from
+// Starboard. Returns true if the client consumed |event|; false otherwise.
+SB_EXPORT_PLATFORM void CbLibMainSetHandleEventCallback(
+    void* context, CbLibMainHandleEventCallback callback);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_BROWSER_LIB_EXPORTED_MAIN_H_
diff --git a/src/cobalt/browser/lib/imported/main.h b/src/cobalt/browser/lib/imported/main.h
deleted file mode 100644
index 1cbfd3a..0000000
--- a/src/cobalt/browser/lib/imported/main.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// All imported functions defined below MUST be implemented by client
-// applications.
-
-#ifndef COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
-#define COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
-
-#include "starboard/event.h"
-#include "starboard/export.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Invoked after Cobalt has been initialized.
-SB_IMPORT_PLATFORM void CbLibOnCobaltInitialized();
-
-// Invoked when Cobalt is receiving an event from Starboard.
-// Returns true if the client consumed |event|; false otherwise.
-SB_IMPORT_PLATFORM bool CbLibHandleEvent(const SbEvent* event);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
diff --git a/src/cobalt/browser/lib/imported/main_stub.cc b/src/cobalt/browser/lib/imported/main_stub.cc
deleted file mode 100644
index ad5c642..0000000
--- a/src/cobalt/browser/lib/imported/main_stub.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "cobalt/browser/lib/imported/main.h"
-
-// Empty implementations so that 'lib' targets can be compiled into executables
-// by the builder without missing symbol definitions.
-void CbLibOnCobaltInitialized() {}
-bool CbLibHandleEvent(const SbEvent* event) {
-  (void) event;
-  return false;
-}
diff --git a/src/cobalt/browser/lib/main.cc b/src/cobalt/browser/lib/main.cc
index 13657ee..fea5e48 100644
--- a/src/cobalt/browser/lib/main.cc
+++ b/src/cobalt/browser/lib/main.cc
@@ -12,21 +12,41 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/browser/lib/exported/main.h"
+
+#include "base/bind.h"
 #include "base/callback.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "cobalt/base/wrap_main.h"
 #include "cobalt/browser/application.h"
-#include "cobalt/browser/lib/imported/main.h"
 #include "starboard/event.h"
 #include "starboard/input.h"
 
 namespace {
 
 cobalt::browser::Application* g_application = NULL;
+typedef base::Callback<void(void)> InitializedCallback;
+typedef base::Callback<bool(const SbEvent*)> HandleEventCallback;
+static base::LazyInstance<InitializedCallback>
+    g_on_cobalt_initialized_callback = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<HandleEventCallback> g_handle_event_callback =
+    LAZY_INSTANCE_INITIALIZER;
+
+// We cannot use LazyInstance here as this can be set before Cobalt has been
+// initialized at all - thus there will not yet exist an AtExitManager which
+// means the app would crash if we tried to replace the lazy instance too
+// early. Instead, for this one callback, we simply do things manually.
+CbLibMainCallbackRegistrationReadyCallback g_callback_registration_ready =
+    nullptr;
+void* g_registration_ready_context = nullptr;
+
 
 void PreloadApplication(int /*argc*/, char** /*argv*/, const char* /*link*/,
                         const base::Closure& quit_closure) {
   DCHECK(!g_application);
+  CHECK(g_callback_registration_ready);
+  g_callback_registration_ready(g_registration_ready_context);
   g_application =
       new cobalt::browser::Application(quit_closure, true /*should_preload*/);
   DCHECK(g_application);
@@ -36,6 +56,8 @@
                       const base::Closure& quit_closure) {
   LOG(INFO) << "Starting application!";
   if (!g_application) {
+    CHECK(g_callback_registration_ready);
+    g_callback_registration_ready(g_registration_ready_context);
     g_application = new cobalt::browser::Application(quit_closure,
                                                      false /*should_preload*/);
     DCHECK(g_application);
@@ -43,7 +65,8 @@
     g_application->Start();
   }
   DCHECK(g_application);
-  CbLibOnCobaltInitialized();
+  if (!g_on_cobalt_initialized_callback.Get().is_null())
+    g_on_cobalt_initialized_callback.Get().Run();
 }
 
 void StopApplication() {
@@ -54,9 +77,8 @@
 }
 
 void HandleStarboardEvent(const SbEvent* starboard_event) {
-  DCHECK(starboard_event);
-  if (!CbLibHandleEvent(starboard_event)) {
-    DCHECK(g_application);
+  if (g_application && (g_handle_event_callback.Get().is_null() ||
+                        !g_handle_event_callback.Get().Run(starboard_event))) {
     g_application->HandleStarboardEvent(starboard_event);
   }
 }
@@ -65,3 +87,21 @@
 
 COBALT_WRAP_MAIN(PreloadApplication, StartApplication, HandleStarboardEvent,
                  StopApplication);
+
+void CbLibMainSetCallbackRegistrationReadyCallback(
+    void* context, CbLibMainOnCobaltInitializedCallback callback) {
+  g_registration_ready_context = context;
+  g_callback_registration_ready = callback;
+}
+
+void CbLibMainSetOnCobaltInitializedCallback(
+    void* context, CbLibMainOnCobaltInitializedCallback callback) {
+  g_on_cobalt_initialized_callback.Get() =
+      callback ? base::Bind(callback, context) : InitializedCallback();
+}
+
+void CbLibMainSetHandleEventCallback(void* context,
+                                     CbLibMainHandleEventCallback callback) {
+  g_handle_event_callback.Get() =
+      callback ? base::Bind(callback, context) : HandleEventCallback();
+}
diff --git a/src/cobalt/browser/memory_tracker/tool.cc b/src/cobalt/browser/memory_tracker/tool.cc
index 9a95e1b..95b9290 100644
--- a/src/cobalt/browser/memory_tracker/tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool.cc
@@ -23,6 +23,7 @@
 #include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h"
 #include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h"
 #include "cobalt/browser/memory_tracker/tool/log_writer_tool.h"
+#include "cobalt/browser/memory_tracker/tool/malloc_logger_tool.h"
 #include "cobalt/browser/memory_tracker/tool/malloc_stats_tool.h"
 #include "cobalt/browser/memory_tracker/tool/memory_size_binner_tool.h"
 #include "cobalt/browser/memory_tracker/tool/print_csv_tool.h"
@@ -65,6 +66,7 @@
   kLeakTracer,
   kJavascriptLeakTracer,
   kMallocStats,
+  kMallocLogger,
 };
 
 struct SwitchVal {
@@ -160,58 +162,67 @@
 
   SwitchVal startup_tool(
       "startup(num_mins=1)",  // Name of tool.
-      "Records high-frequency memory metrics for the first 60 "
-      "seconds of program launch and then dumps it out in CSV format "
-      "to stdout.",
+      "  Records high-frequency memory metrics for the first 60\n"
+      "  seconds of program launch and then dumps it out in CSV format\n"
+      "  to stdout.\n",
       kStartup);
 
   SwitchVal continuous_printer_tool(
       "continuous_printer",  // Name of tool.
-      "Once every second the memory state is dumped to stdout.",
+      "  Once every second the memory state is dumped to stdout.\n",
       kContinuousPrinter);
 
   SwitchVal compressed_timeseries_tool(
       "compressed_timeseries",  // Name of tool.
-      "Use this tool to see the growth in memory usage as the app runs. The "
-      "memory growth is segmented into memory scopes and outputted as CSV. "
-      "The compressed time-series will depict the full history of the memory "
-      "using a fixed number of rows. Older history has degraded resolution and "
-      "while new entries are captured in full detail. This achieved by "
-      "evicting old entries by an exponential decay scheme.",
+      "  Use this tool to see the growth in memory usage as the app runs.\n"
+      "  The memory growth is segmented into memory scopes and outputted as\n"
+      "  CSV. The compressed time-series will depict the full history of\n"
+      "  the memory using a fixed number of rows. Older history has degraded\n"
+      "  resolution and while new entries are captured in full detail. This\n"
+      "  achieved by evicting old entries by an exponential decay scheme.\n",
       kCompressedTimeseries);
 
   SwitchVal binner_tool(
       "binner(region=NULL)",
-      "Dumps memory statistics once a second in CSV format to stdout. "
-      "The default memory region is all memory regions. Pass the "
-      "name of the memory region to specify that only that memory region "
-      "should be tracked. For example: binner(Javascript).",
+      "  Dumps memory statistics once a second in CSV format to stdout.\n"
+      "  The default memory region is all memory regions. Pass the\n"
+      "  name of the memory region to specify that only that memory region\n"
+      "  should be tracked. For example: binner(Javascript).\n",
       kBinnerAnalytics);
 
   SwitchVal allocation_logger_tool(
       "allocation_logger",
-      "Continuously writes allocations and deallocations to memory_log.txt. "
-      "This is a legacy format used by lbshell. The location of this "
-      "memory_log.txt file is in the platform dependent directory specified "
-      "by kSbSystemPathDebugOutputDirectory.",
+      "  Continuously writes allocations and deallocations to\n"
+      "  memory_log.txt. This is a legacy format used by lbshell. The\n"
+      "  location of this memory_log.txt file is in the platform dependent\n"
+      "  directory specified by kSbSystemPathDebugOutputDirectory.\n",
       kAllocationLogger);
 
   SwitchVal leak_tracing_tool(
       "leak_tracer",
-      "Automatically detects leaks and reports them in CSV format.",
+      "  Automatically detects leaks and reports them in CSV format.\n",
       kLeakTracer);
 
   SwitchVal js_leak_tracing_tool(
       "js_leak_tracer",
-      "Automatically detects Javascript leaks and reports them in CSV format.",
+      "  Automatically detects Javascript leaks and reports them in CSV\n"
+      "  format.\n",
       kJavascriptLeakTracer);
 
   SwitchVal malloc_stats_tool(
       "malloc_stats",
-      "Queries the allocation system for memory usage. This is the most "
-      "lightweight tool. Output is CSV format.",
+      "  Queries the allocation system for memory usage. This is the most\n"
+      "  lightweight tool. Output is CSV format.\n",
       kMallocStats);
 
+  SwitchVal malloc_logger_tool(
+      "malloc_logger",
+      "  Continuously writes allocations, deallocations, allocation location\n"
+      "  and malloc stats to memory_log_<timestamp>.csv, without headers.\n"
+      "  The location of this log file is in the platform dependent\n"
+      "  directory specified by kSbSystemPathDebugOutputDirectory.\n",
+      kMallocLogger);
+
   SwitchMap switch_map;
   switch_map[ParseToolName(startup_tool.tool_name)] = startup_tool;
   switch_map[ParseToolName(continuous_printer_tool.tool_name)] =
@@ -224,6 +235,8 @@
   switch_map[ParseToolName(leak_tracing_tool.tool_name)] = leak_tracing_tool;
   switch_map[ParseToolName(js_leak_tracing_tool.tool_name)] =
       js_leak_tracing_tool;
+  switch_map[ParseToolName(malloc_logger_tool.tool_name)] =
+      malloc_logger_tool;
 
   switch_map[ParseToolName(malloc_stats_tool.tool_name)] = malloc_stats_tool;
 
@@ -383,6 +396,16 @@
       tool_ptr.reset(new MallocStatsTool);
       break;
     }
+    case kMallocLogger: {
+      scoped_ptr<MallocLoggerTool> malloc_logger(
+          new MallocLoggerTool());
+
+      memory_tracker = MemoryTracker::Get();
+      memory_tracker->InstallGlobalTrackingHooks();
+      memory_tracker->SetMemoryTrackerDebugCallback(malloc_logger.get());
+      tool_ptr.reset(malloc_logger.release());
+      break;
+    }
     default: {
       SB_NOTREACHED() << "Unhandled case.";
       break;
diff --git a/src/cobalt/browser/memory_tracker/tool/malloc_logger_tool.cc b/src/cobalt/browser/memory_tracker/tool/malloc_logger_tool.cc
new file mode 100644
index 0000000..9243bd6
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/malloc_logger_tool.cc
@@ -0,0 +1,204 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/malloc_logger_tool.h"
+
+#include <algorithm>
+
+#include "base/time.h"
+#include "cobalt/base/c_val.h"
+#include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "nb/memory_scope.h"
+#include "starboard/atomic.h"
+#include "starboard/string.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+namespace {
+const int kAllocationRecord = 1;
+const int kDeallocationRecord = 0;
+const size_t kStartIndex = 5;
+const size_t kNumAddressPrints = 2;
+const size_t kMaxStackSize = 10;
+const size_t kRecordLimit = 1024;
+const NbMemoryScopeInfo kEmptyCallstackMemoryScopeInfo = {nullptr,
+  "-", "-", 0, "-", true};
+}  // namespace
+
+MallocLoggerTool::MallocLoggerTool() : start_time_(NowTime()),
+  atomic_counter_(0),
+  atomic_used_memory_(SbSystemGetUsedCPUMemory()) {
+    buffered_file_writer_.reset(new BufferedFileWriter(MemoryLogPath()));
+}
+
+MallocLoggerTool::~MallocLoggerTool() {
+  // No locks are used for the thread reporter, so when it's set to null
+  // we allow one second for any suspended threads to run through and finish
+  // their reporting.
+  SbMemorySetReporter(NULL);
+  SbThreadSleep(kSbTimeSecond);
+  buffered_file_writer_.reset(NULL);
+}
+
+std::string MallocLoggerTool::tool_name() const {
+  return "MemoryTrackerMallocLogger";
+}
+
+void MallocLoggerTool::Run(Params* params) {
+  // Update malloc stats every second
+  params->logger()->Output("MemoryTrackerMallocLogger running...");
+
+  // There are some memory allocations which do not get tracked.
+  // Those allocations show up as fragmentation.
+  // It has been empirically observed that a majority (98%+) of these
+  // untracked allocations happen in the first 20 seconds of Cobalt runtime.
+  //
+  // Also, there is minimal external fragmentation (< 1 MB) during this initial
+  // period.
+  //
+  // The following piece of code resets atomic_used_memory_ at the 20 second
+  // mark, to compensate for the deviation due to untracked memory.
+  base::TimeDelta current_sample_interval =
+      base::TimeDelta::FromSeconds(20);
+  if (!params->wait_for_finish_signal(current_sample_interval.ToSbTime())) {
+    atomic_used_memory_.store(SbSystemGetUsedCPUMemory());
+  }
+
+  // Export fragmentation as a CVal on HUD.
+  base::CVal<base::cval::SizeInBytes> memory_fragmentation(
+      "Memory.CPU.Fragmentation", base::cval::SizeInBytes(0),
+      "Memory Fragmentation");
+
+  // Update CVal every 5 seconds
+  current_sample_interval = base::TimeDelta::FromSeconds(5);
+  int64_t allocated_memory = 0;
+  int64_t used_memory = 0;
+  while (!params->wait_for_finish_signal(current_sample_interval.ToSbTime())) {
+    allocated_memory = SbSystemGetUsedCPUMemory();
+    used_memory = atomic_used_memory_.load();
+    memory_fragmentation = static_cast<uint64>(
+        std::max(allocated_memory - used_memory, static_cast<int64_t>(0)));
+  }
+}
+
+void MallocLoggerTool::LogRecord(const void* memory_block,
+    const nb::analytics::AllocationRecord& record,
+    const nb::analytics::CallStack& callstack, int type) {
+  const int log_counter = atomic_counter_.increment();
+  const int64_t used_memory = atomic_used_memory_.load();
+  const int64_t allocated_memory = SbSystemGetUsedCPUMemory();
+  const int time_since_start_ms = GetTimeSinceStartMs();
+  char buff[kRecordLimit] = {0};
+  size_t buff_pos = 0;
+  void* addresses[kMaxStackSize];
+
+  const NbMemoryScopeInfo* memory_scope;
+  if (callstack.empty()) {
+    memory_scope = &kEmptyCallstackMemoryScopeInfo;
+  } else {
+    memory_scope = callstack.back();
+  }
+
+  int bytes_written = SbStringFormatF(buff, sizeof(buff),
+    "%u,%d,%zd,\"%s\",%d,%s,%d,%" PRId64 ",%" PRId64 ",%" PRIXPTR ",\"",
+    log_counter, type, record.size, memory_scope->file_name_,
+    memory_scope->line_number_, memory_scope->function_name_,
+    time_since_start_ms, allocated_memory, used_memory,
+    reinterpret_cast<uintptr_t>(memory_block));
+
+  buff_pos += static_cast<size_t>(bytes_written);
+  const size_t count = std::max(SbSystemGetStack(addresses, kMaxStackSize), 0);
+  const size_t end_index = std::min(count, kStartIndex + kNumAddressPrints);
+  // For each of the stack addresses that we care about, concat them to the
+  // buffer. This was originally written to do multiple stack addresses but
+  // this tends to overflow on some lower platforms so it's possible that
+  // this loop only iterates once.
+  for (size_t i = kStartIndex; i < end_index; ++i) {
+    void* p = addresses[i];
+    bytes_written =
+        SbStringFormatF(buff + buff_pos, kRecordLimit - buff_pos,
+                        ",%" PRIXPTR "", reinterpret_cast<uintptr_t>(p));
+    DCHECK_GE(bytes_written, 0);
+    buff_pos += static_cast<size_t>(bytes_written);
+  }
+
+  // Adds a "\n" at the end.
+  bytes_written = SbStringConcat(buff + buff_pos, "\"\n",
+                                 static_cast<int>(kRecordLimit - buff_pos));
+  buff_pos += bytes_written;
+  buffered_file_writer_->Append(buff, buff_pos);
+}
+
+void MallocLoggerTool::OnMemoryAllocation(
+    const void* memory_block, const nb::analytics::AllocationRecord& record,
+    const nb::analytics::CallStack& callstack) {
+  atomic_used_memory_.fetch_add(record.size);
+  LogRecord(memory_block, record, callstack, kAllocationRecord);
+}
+
+void MallocLoggerTool::OnMemoryDeallocation(
+    const void* memory_block, const nb::analytics::AllocationRecord& record,
+    const nb::analytics::CallStack& callstack) {
+  atomic_used_memory_.fetch_sub(record.size);
+  LogRecord(memory_block, record, callstack, kDeallocationRecord);
+}
+
+std::string MallocLoggerTool::MemoryLogPath() {
+  char file_name_buff[2048] = {};
+  SbSystemGetPath(kSbSystemPathDebugOutputDirectory, file_name_buff,
+                  arraysize(file_name_buff));
+  std::string path(file_name_buff);
+  if (!path.empty()) {  // Protect against a dangling "/" at end.
+    const int back_idx_signed = static_cast<int>(path.length()) - 1;
+    if (back_idx_signed >= 0) {
+      const size_t idx = back_idx_signed;
+      if (path[idx] == '/') {
+        path.erase(idx);
+      }
+    }
+  }
+
+  base::Time time = base::Time::Now();
+  base::Time::Exploded exploded;
+  time.LocalExplode(&exploded);
+
+  std::stringstream ss;
+  ss << "/memory_log_" << exploded.year << "-"
+     << exploded.month << "-" << exploded.day_of_month << ":"
+     << exploded.hour << "-" << exploded.minute << "-" << exploded.second
+     << ".csv";
+  path.append(ss.str());
+  return path;
+}
+
+base::TimeTicks MallocLoggerTool::NowTime() {
+  // NowFromSystemTime() is slower but more accurate. However it might
+  // be useful to use the faster but less accurate version if there is
+  // a speedup.
+  return base::TimeTicks::Now();
+}
+
+int MallocLoggerTool::GetTimeSinceStartMs() const {
+  base::TimeDelta dt = NowTime() - start_time_;
+  return static_cast<int>(dt.InMilliseconds());
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/malloc_logger_tool.h b/src/cobalt/browser/memory_tracker/tool/malloc_logger_tool.h
new file mode 100644
index 0000000..80313f9
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/malloc_logger_tool.h
@@ -0,0 +1,79 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_MALLOC_LOGGER_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_MALLOC_LOGGER_TOOL_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/memory_scope.h"
+#include "starboard/atomic.h"
+#include "starboard/memory_reporter.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class BufferedFileWriter;
+
+// Outputs memory_log_<time_stamp>.csv to the output log location. This log
+// contains allocations and deallocations with a non-symbolized stack trace.
+class MallocLoggerTool: public AbstractTool,
+  public nb::analytics::MemoryTrackerDebugCallback {
+ public:
+  MallocLoggerTool();
+  virtual ~MallocLoggerTool();
+
+  // Interface AbstractMemoryTrackerTool
+  virtual std::string tool_name() const OVERRIDE;
+  virtual void Run(Params* params) OVERRIDE;
+
+  // OnMemoryAllocation() and OnMemoryDeallocation() are part of
+  // class MemoryTrackerDebugCallback.
+  void OnMemoryAllocation(const void* memory_block,
+                          const nb::analytics::AllocationRecord& record,
+                          const nb::analytics::CallStack& callstack) OVERRIDE;
+
+  void OnMemoryDeallocation(const void* memory_block,
+                            const nb::analytics::AllocationRecord& record,
+                            const nb::analytics::CallStack& callstack) OVERRIDE;
+
+  // Method to obtain allocation, stack information and generate records
+  void LogRecord(const void* memory_block,
+    const nb::analytics::AllocationRecord& record,
+    const nb::analytics::CallStack& callstack,
+    const int type);
+
+ private:
+  static std::string MemoryLogPath();
+  static base::TimeTicks NowTime();
+
+  int GetTimeSinceStartMs() const;
+
+  base::TimeTicks start_time_;
+  scoped_ptr<SbMemoryReporter> memory_reporter_;
+  scoped_ptr<BufferedFileWriter> buffered_file_writer_;
+  starboard::atomic_int32_t atomic_counter_;
+  starboard::atomic_int64_t atomic_used_memory_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_MALLOC_LOGGER_TOOL_H_
diff --git a/src/cobalt/browser/null_webapi_extension.cc b/src/cobalt/browser/null_webapi_extension.cc
new file mode 100644
index 0000000..6d7e13b
--- /dev/null
+++ b/src/cobalt/browser/null_webapi_extension.cc
@@ -0,0 +1,41 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/webapi_extension.h"
+
+#include "base/compiler_specific.h"
+#include "cobalt/script/global_environment.h"
+
+namespace cobalt {
+namespace browser {
+
+base::optional<std::string> GetWebAPIExtensionObjectPropertyName() {
+  return base::nullopt;
+}
+
+scoped_refptr<script::Wrappable> CreateWebAPIExtensionObject(
+    const scoped_refptr<dom::Window>& window,
+    script::GlobalEnvironment* global_environment) {
+  UNREFERENCED_PARAMETER(window);
+  UNREFERENCED_PARAMETER(global_environment);
+
+  // We should never get called if GetWindowExtensionObjectName() above returns
+  // base::nullopt.
+  NOTREACHED();
+
+  return scoped_refptr<script::Wrappable>();
+}
+
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/null_webapi_extension.gyp b/src/cobalt/browser/null_webapi_extension.gyp
new file mode 100644
index 0000000..ad078eb
--- /dev/null
+++ b/src/cobalt/browser/null_webapi_extension.gyp
@@ -0,0 +1,29 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'targets': [
+    {
+      'target_name': 'null_webapi_extension',
+      'type': 'static_library',
+      'sources': [
+        'null_webapi_extension.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/script/script.gyp:script',
+        '<(DEPTH)/base/base.gyp:base',
+      ],
+    },
+  ],
+}
diff --git a/src/cobalt/browser/splash_screen_cache.cc b/src/cobalt/browser/splash_screen_cache.cc
index ab674df..29de881 100644
--- a/src/cobalt/browser/splash_screen_cache.cc
+++ b/src/cobalt/browser/splash_screen_cache.cc
@@ -16,11 +16,11 @@
 
 #include <string>
 
-#include "base/base64.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/optional.h"
 #include "base/string_util.h"
 #include "base/synchronization/lock.h"
+#include "cobalt/base/get_application_key.h"
 #include "starboard/directory.h"
 #include "starboard/file.h"
 #include "starboard/string.h"
@@ -113,15 +113,10 @@
 // static
 base::optional<std::string> SplashScreenCache::GetKeyForStartUrl(
     const GURL& url) {
-  std::string encoded_url = "";
-  if (!url.is_valid()) {
+  base::optional<std::string> encoded_url = base::GetApplicationKey(url);
+  if (!encoded_url) {
     return base::nullopt;
   }
-  base::Base64Encode(base::StringPiece(url.host() + url.path()), &encoded_url);
-
-  // Make web-safe.
-  ReplaceChars(encoded_url, "/", "_", &encoded_url);
-  ReplaceChars(encoded_url, "+", "-", &encoded_url);
 
   char path[SB_FILE_MAX_PATH] = {0};
   bool has_cache_dir =
@@ -137,7 +132,7 @@
     return base::nullopt;
   }
   subpath += "splash_screen";
-  subcomponent = SB_FILE_SEP_STRING + encoded_url;
+  subcomponent = SB_FILE_SEP_STRING + *encoded_url;
   if (SbStringConcat(path, subcomponent.c_str(), SB_FILE_MAX_PATH) >=
       SB_FILE_MAX_PATH) {
     return base::nullopt;
diff --git a/src/cobalt/browser/stack_size_constants.h b/src/cobalt/browser/stack_size_constants.h
index d6b33fc..3d7d7d2 100644
--- a/src/cobalt/browser/stack_size_constants.h
+++ b/src/cobalt/browser/stack_size_constants.h
@@ -21,12 +21,12 @@
 namespace browser {
 #if defined(COBALT_BUILD_TYPE_DEBUG)
   // Non-optimized builds require a bigger stack size.
-  const size_t kBaseStackSize = 2 * 1024 * 1024;
+  const size_t kBaseStackSize = 3 * 1024 * 1024;
 #elif defined(COBALT_BUILD_TYPE_DEVEL)
   // Devel builds require a slightly bigger stack size.
-  const size_t kBaseStackSize = 448 * 1024;
+  const size_t kBaseStackSize = 832 * 1024;
 #else
-  const size_t kBaseStackSize = 384 * 1024;
+  const size_t kBaseStackSize = 768 * 1024;
 #endif
   const size_t kWebModuleStackSize =
       kBaseStackSize + base::kAsanAdditionalStackSize;
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index efd1b83..63abd63 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -799,8 +799,8 @@
            attributes.begin();
        iter != attributes.end(); ++iter) {
     global_environment_->Bind(
-        iter->first,
-        iter->second.Run(window_, &mutation_observer_task_manager_));
+        iter->first, iter->second.Run(window_, &mutation_observer_task_manager_,
+                                      global_environment_.get()));
   }
 }
 
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 07d8975..b296d6f 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -82,7 +82,8 @@
   struct Options {
     typedef base::Callback<scoped_refptr<script::Wrappable>(
         const scoped_refptr<dom::Window>& window,
-        dom::MutationObserverTaskManager* mutation_observer_task_manager)>
+        dom::MutationObserverTaskManager* mutation_observer_task_manager,
+        script::GlobalEnvironment* global_environment)>
         CreateObjectFunction;
     typedef base::hash_map<std::string, CreateObjectFunction>
         InjectedWindowAttributes;
diff --git a/src/cobalt/browser/webapi_extension.h b/src/cobalt/browser/webapi_extension.h
new file mode 100644
index 0000000..30a977e
--- /dev/null
+++ b/src/cobalt/browser/webapi_extension.h
@@ -0,0 +1,53 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_WEBAPI_EXTENSION_H_
+#define COBALT_BROWSER_WEBAPI_EXTENSION_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace browser {
+
+// This file declares the interface that Cobalt calls in order to possibly
+// inject external functionality into the web app's JavaScript environment.
+// See cobalt/doc/webapi_extension.md for more information.
+
+// The implementation of this function should return the name of the property
+// to be injected into to the JavaScript window object.  If base::nullopt is
+// returned, then CreateWebExtensionObject() will not be subsequently called
+// and nothing will be injected.
+base::optional<std::string> GetWebAPIExtensionObjectPropertyName();
+
+// The actual object that will be assigned to the window property with name
+// given by *GetWebExtensionObjectPropertyName().  The returned object should
+// be specified by an IDL interface.  It is passed a reference to |window|
+// so that it can access and introspect any properties of the window object.
+// It is passed |global_environment| so that it can access functions of the
+// GlobalEnvironment interface, such as using it to execute arbitrary
+// JavaScript.
+scoped_refptr<script::Wrappable> CreateWebAPIExtensionObject(
+    const scoped_refptr<dom::Window>& window,
+    script::GlobalEnvironment* global_environment);
+
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_WEBAPI_EXTENSION_H_
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index cf4e0e6..e6c9c94 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-84472
\ No newline at end of file
+88774
\ No newline at end of file
diff --git a/src/cobalt/build/build_config.h b/src/cobalt/build/build_config.h
index bfe3df5..e743e5f 100644
--- a/src/cobalt/build/build_config.h
+++ b/src/cobalt/build/build_config.h
@@ -50,4 +50,12 @@
 #endif  // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K <
         //     COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
 
+#if COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE
+#if SB_API_VERSION < SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+#error COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE requires \
+           that SB_API_VERSION is greater than or equal to \
+           SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+#endif  // SB_API_VERSION < SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+#endif  // COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE
+
 #endif  // COBALT_BUILD_BUILD_CONFIG_H_
diff --git a/src/cobalt/build/cobalt_build_id.gni b/src/cobalt/build/cobalt_build_id.gni
new file mode 100644
index 0000000..f6d248b
--- /dev/null
+++ b/src/cobalt/build/cobalt_build_id.gni
@@ -0,0 +1,17 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Cobalt build id.
+# TODO: ensure that this is updated after a new source revision is pulled down
+cobalt_build_id = exec_script("get_build_id.py", "value")
diff --git a/src/cobalt/build/config/BUILD.gn b/src/cobalt/build/config/BUILD.gn
new file mode 100644
index 0000000..f0af403
--- /dev/null
+++ b/src/cobalt/build/config/BUILD.gn
@@ -0,0 +1,181 @@
+# Copyright 2014 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//cobalt/build/config/base.gni")
+
+# TODO: split out into relevant subdirectories
+config("compiler_defaults") {
+  if (cobalt_media_buffer_pool_allocate_on_demand) {
+    allocate_on_demand = 1
+  } else {
+    allocate_on_demand = 0
+  }
+
+  if (cobalt_encrypted_media_extension_enable_key_statuses_update) {
+    enable_key_statuses_update = 1
+  } else {
+    enable_key_statuses_update = 0
+  }
+
+  defines = [
+    "COBALT",
+    "COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND=$allocate_on_demand",
+    "COBALT_MEDIA_BUFFER_INITIAL_CAPACITY=$cobalt_media_buffer_initial_capacity",
+    "COBALT_MEDIA_BUFFER_ALLOCATION_UNIT=$cobalt_media_buffer_allocation_unit",
+    "COBALT_MEDIA_BUFFER_ALIGNMENT=$cobalt_media_buffer_alignment",
+    "COBALT_MEDIA_BUFFER_PADDING=$cobalt_media_buffer_padding",
+    "COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET=$cobalt_media_buffer_progressive_budget",
+    "COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=$cobalt_media_buffer_non_video_budget",
+    "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=$cobalt_media_buffer_video_budget_1080p",
+    "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=$cobalt_media_buffer_video_budget_4k",
+    "COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE=$enable_key_statuses_update",
+
+    # From common.gypi
+    "USE_OPENSSL=1",
+  ]
+
+  include_dirs = [ "//" ]
+
+  if (cobalt_use_media_source_2016) {
+    defines += [ "COBALT_MEDIA_SOURCE_2016=1" ]
+  } else {
+    defines += [ "COBALT_MEDIA_SOURCE_2012=1" ]
+  }
+
+  if (cobalt_media_buffer_storage_type == "memory") {
+    defines += [ "COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY=1" ]
+  } else {
+    defines += [ "COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE=1" ]
+  }
+
+  if (enable_in_app_dial) {
+    defines += [ "DIAL_SERVER" ]
+  }
+
+  if (enable_file_scheme) {
+    defines += [ "COBALT_ENABLE_FILE_SCHEME" ]
+  }
+
+  if (!enable_spdy) {
+    defines += [ "COBALT_DISABLE_SPDY" ]
+  }
+}
+
+config("final_executable_target_config") {
+  # XXX: ideally this config would only apply when we are compiling targets
+  # for the target platform. We try to check for that here, however, this check
+  # is ineffective when default_toolchain happens to be the same as
+  # host_toolchain.
+  # Therefore, **targets which should be compiled for the host must remove this
+  # config from their list of configs.**
+  if (current_toolchain == default_toolchain &&
+      final_executable_type == "shared_library") {
+    defines = [
+      # Rewrite main() functions into StarboardMain. TODO: This is a
+      # hack, it would be better to be more surgical, here.
+      "main=StarboardMain",
+    ]
+
+    cflags = [
+      # To link into a shared library on Linux and similar platforms,
+      # the compiler must be told to generate Position Independent Code.
+      # This appears to cause errors when linking the code statically,
+      # however.
+      "-fPIC",
+    ]
+  }
+}
+
+config("compiler_defaults_debug") {
+  defines = [
+    "ALLOCATOR_STATS_TRACKING",
+    "COBALT_BOX_DUMP_ENABLED",
+    "COBALT_BUILD_TYPE_DEBUG",
+    "COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING",
+    "COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR",
+    "_DEBUG",
+    "ENABLE_DEBUG_COMMAND_LINE_SWITCHES",
+    "ENABLE_DEBUG_C_VAL",
+    "ENABLE_DEBUG_CONSOLE",
+    "ENABLE_DIR_SOURCE_ROOT_ACCESS",
+    "ENABLE_IGNORE_CERTIFICATE_ERRORS",
+    "ENABLE_PARTIAL_LAYOUT_CONTROL",
+    "ENABLE_TEST_RUNNER",
+    "__LB_SHELL__ENABLE_SCREENSHOT__",
+    "__LB_SHELL__FORCE_LOGGING__",  # TODO: Rename to COBALT_LOGGING_ENABLED.
+    "SK_DEVELOPER",
+  ]
+}
+
+config("compiler_defaults_devel") {
+  defines = [
+    "_DEBUG",
+    "ALLOCATOR_STATS_TRACKING",
+    "COBALT_BUILD_TYPE_DEVEL",
+    "COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING",
+    "COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR",
+    "ENABLE_DEBUG_COMMAND_LINE_SWITCHES",
+    "ENABLE_DEBUG_C_VAL",
+    "ENABLE_DEBUG_CONSOLE",
+    "ENABLE_DIR_SOURCE_ROOT_ACCESS",
+    "ENABLE_IGNORE_CERTIFICATE_ERRORS",
+    "ENABLE_PARTIAL_LAYOUT_CONTROL",
+    "ENABLE_TEST_RUNNER",
+    "__LB_SHELL__ENABLE_SCREENSHOT__",
+    "__LB_SHELL__FORCE_LOGGING__",
+    "SK_DEVELOPER",
+  ]
+}
+
+config("compiler_defaults_qa") {
+  defines = [
+    "ALLOCATOR_STATS_TRACKING",
+    "COBALT_BUILD_TYPE_QA",
+    "COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING",
+    "COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR",
+    "ENABLE_DEBUG_COMMAND_LINE_SWITCHES",
+    "ENABLE_DEBUG_C_VAL",
+    "ENABLE_DEBUG_CONSOLE",
+    "ENABLE_DIR_SOURCE_ROOT_ACCESS",
+    "ENABLE_IGNORE_CERTIFICATE_ERRORS",
+    "ENABLE_PARTIAL_LAYOUT_CONTROL",
+    "ENABLE_TEST_RUNNER",
+    "__LB_SHELL__ENABLE_SCREENSHOT__",
+    "__LB_SHELL__FOR_QA__",
+    "NDEBUG",
+  ]
+}
+
+config("compiler_defaults_gold") {
+  defines = [
+    "ALLOCATOR_STATS_TRACKING",
+    "COBALT_BUILD_TYPE_GOLD",
+    "COBALT_FORCE_CSP",
+    "COBALT_FORCE_HTTPS",
+    "__LB_SHELL__FOR_RELEASE__",
+    "NDEBUG",
+    "TRACING_DISABLED",
+  ]
+}
+
+config("chromium_code") {
+  defines = [
+    "__STDC_CONSTANT_MACROS",
+    "__STDC_FORMAT_MACROS",
+  ]
+}
diff --git a/src/cobalt/build/config/BUILDCONFIG.gn b/src/cobalt/build/config/BUILDCONFIG.gn
new file mode 100644
index 0000000..9b8796e
--- /dev/null
+++ b/src/cobalt/build/config/BUILDCONFIG.gn
@@ -0,0 +1,203 @@
+# Copyright 2014 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# =============================================================================
+# WHAT IS THIS FILE?
+# =============================================================================
+#
+# This is the master GN build configuration. This file is loaded after the
+# build args (args.gn) for the build directory and after the toplevel ".gn"
+# file (which points to this file as the build configuration).
+#
+# This file will be executed and the resulting context will be used to execute
+# every other file in the build. So variables declared here (that don't start
+# with an underscore) will be implicitly global. Note that unlike in GYP, in
+# GN the practice is to limit the number of global variables and define
+# variables in .gni files instead.
+#
+# YOU SHOULD ALMOST NEVER NEED TO ADD FLAGS TO THIS FILE. GN allows any file in
+# the build to declare build flags.
+#
+# - If you want to add a new per-platform variable (e.g. javascript_engine,
+#   enable_map_to_mesh, etc.), add that to //cobalt/build/config/base.gni or
+#   //starboard/build/config/base.gni.
+#
+# - If you want to add an actual build arg (i.e. something a developer would
+#   specify at compile time, such as cobalt_use_fastbuild or use_asan):
+#
+#    - If your build arg will only be used in a single target, say //cobalt/foo,
+#      you can put a declare_args() block in //cobalt/foo/BUILD.gn and use it
+#      there.  Nobody else in the build needs to see the flag.
+#
+#    - Otherwise, you can put the argument in a .gni file. This should be put in
+#      the lowest level of the build that knows about this feature (which should
+#      almost always be outside of "build" directories!).
+#
+# - If your flag toggles a target on and off or toggles between different
+#   versions of similar things, write a "group" target that forwards to the
+#   right target (or no target) depending on the value of the build flag. This
+#   group can be in the same BUILD.gn file as the build flag, and targets can
+#   depend unconditionally on the group rather than duplicating flag checks
+#   across many targets.
+
+# =============================================================================
+# PLATFORM SELECTION
+# =============================================================================
+#
+# There are two main things to set: "cobalt_config" and "target_platform".
+# These are set via `gn args`. The starboard platform path is then calculated
+# from target_platform. Finally, we import a file containing platform-specific
+# configuration (such as the default toolchain and target OS/architecture) that
+# must be set in BUILDCONFIG.gn.
+
+declare_args() {
+  # The current build configuration.
+  cobalt_config = "gold"
+
+  # The platform we are building for.
+  target_platform = ""
+}
+assert(target_platform != "", "You must specify a target platform")
+
+# The relative path from // to the directory containing the
+# BUILD.gn file defining the starboard_platform target
+starboard_path = rebase_path(exec_script("//cobalt/build/get_starboard_path.py",
+                                         [ target_platform ],
+                                         "trim string"),
+                             "//")
+
+# Import platform-specific build config variables
+import("//$starboard_path/buildconfig.gni")
+
+# =============================================================================
+# THE TARGET OS AND ARCHITECTURE
+# =============================================================================
+#
+# GN has three families of built in variables:
+#  - host_os, host_cpu, host_toolchain
+#  - target_os, target_cpu, default_toolchain
+#  - current_os, current_cpu, current_toolchain.
+#
+# There are three different types of each of these things: The "host"
+# represents the computer doing the compile and never changes. The "target"
+# represents the main thing we're trying to build. The "current" represents
+# which configuration is currently being defined, which can be either the
+# host, the target, or even (in theory) something completely different. GN will
+# run the same build file multiple times for the different required
+# configuration in the same build.
+#
+# Note the default_toolchain isn't symmetrical (you would expect
+# target_toolchain). This is because the "default" toolchain is a GN built-in
+# concept, and "target" is something our build sets up that's symmetrical with
+# its GYP counterpart. Potentially the built-in default_toolchain variable
+# could be renamed in the future.
+#
+# When writing build files, to do something only for the host:
+#   if (current_toolchain == host_toolchain) { ...
+
+if (defined(target_os_)) {
+  target_os = target_os_
+} else {
+  target_os = "unknown"
+}
+target_cpu = target_cpu_
+
+if (current_os == "") {
+  current_os = target_os
+}
+if (current_cpu == "") {
+  current_cpu = target_cpu
+}
+
+# =============================================================================
+# TARGET DEFAULTS
+# =============================================================================
+#
+# Set up the default configuration for every build target of the given type.
+# The values configured here will be automatically set on the scope of the
+# corresponding target. Target definitions can add or remove to the settings
+# here as needed.
+
+# All binary targets will get this list of configs by default.
+_shared_binary_target_configs = [
+  "//cobalt/build/config:compiler_defaults",
+  "//cobalt/build/config:compiler_defaults_$cobalt_config",
+
+  "//starboard/build/config:compiler_defaults",
+  "//$starboard_path:compiler_defaults",
+  "//$starboard_path:compiler_defaults_$cobalt_config",
+
+  "//cobalt/build/config:final_executable_target_config",
+
+  "//starboard/build/config:no_pedantic_warnings",
+  "//starboard/build/config:default_rtti",
+  "//starboard/build/config:default_optimizations",
+]
+
+# Apply that default list to the binary target types.
+set_defaults("executable") {
+  configs = _shared_binary_target_configs
+}
+set_defaults("static_library") {
+  configs = _shared_binary_target_configs
+}
+set_defaults("shared_library") {
+  configs = _shared_binary_target_configs
+}
+set_defaults("source_set") {
+  configs = _shared_binary_target_configs
+}
+
+# =============================================================================
+# TARGET TYPE SETUP
+# =============================================================================
+
+# Define some additional target types. These are useful on platforms where the
+# native code may require an additional packaging step (ex. Android).
+
+if (!defined(test_target_type)) {
+  test_target_type = "executable"
+}
+template("test") {
+  target(test_target_type, target_name) {
+    # Explicitly forward visibility, implicitly forward everything else.
+    # Forwarding "*" doesn't recurse into nested scopes (to avoid copying all
+    # globals into each template invocation), so won't pick up file-scoped
+    # variables. Normally this isn't too bad, but visibility is commonly
+    # defined at the file scope. Explicitly forwarding visibility and then
+    # excluding it from the "*" set works around this problem.
+    # See http://crbug.com/594610
+    forward_variables_from(invoker, [ "visibility" ])
+    forward_variables_from(invoker, "*", [ "visibility" ])
+  }
+}
+
+if (!defined(final_executable_type)) {
+  final_executable_type = "executable"
+}
+template("final_executable") {
+  target(final_executable_type, target_name) {
+    # See comment above
+    forward_variables_from(invoker, [ "visibility" ])
+    forward_variables_from(invoker, "*", [ "visibility" ])
+  }
+}
+
+# Set the default toolchain and the host toolchain
+set_default_toolchain(target_toolchain)
+assert(defined(host_toolchain))
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni
new file mode 100644
index 0000000..21e6bef
--- /dev/null
+++ b/src/cobalt/build/config/base.gni
@@ -0,0 +1,581 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Import platform-specific defaults
+import("//$starboard_path/configuration.gni")
+# Import starboard variables
+import("//starboard/build/config/base.gni")
+
+# Cobalt variables.
+
+# Enable support for the map to mesh filter, which is primarily used to
+# implement spherical video playback.
+if (!defined(enable_map_to_mesh)) {
+  enable_map_to_mesh = false
+}
+
+# This variable defines what Cobalt's preferred strategy should be for
+# handling internally triggered application exit requests (e.g. the user
+# chooses to back out of the application).
+#   "stop"    -- The application should call SbSystemRequestStop() on exit,
+#                resulting in a complete shutdown of the application.
+#   "suspend" -- The application should call SbSystemRequestSuspend() on
+#                exit, resulting in the application being "minimized".
+#   "noexit"  -- The application should never allow the user to trigger an
+#                exit, this will be managed by the system.
+if (!defined(cobalt_user_on_exit_strategy)) {
+  cobalt_user_on_exit_strategy = "stop"
+}
+
+# Contains the current font package selection.  This can be used to trade
+# font quality, coverage, and latency for different font package sizes.
+# The font package can be one of the following options:
+#   "expanded" -- The largest package. It includes everything in the
+#                 'standard' package, along with 'bold' weight CJK. It is
+#                 recommended that 'local_font_cache_size_in_bytes' be
+#                 increased to 24MB when using this package to account for
+#                 the extra memory required by bold CJK. This package is
+#                 ~48.7MB.
+#   "standard" -- The default package. It includes all sans-serif, serif,
+#                 and FCC fonts, non-CJK fallback fonts in both 'normal' and
+#                 'bold' weights, and 'normal' weight CJK ('bold' weight CJK
+#                 is synthesized from it). This package is ~29.4MB.
+#   "limited_with_jp" -- A significantly smaller package than 'standard'.
+#                 This package removes all but 'normal' and 'bold' weighted
+#                 sans-serif and serif, removes the FCC fonts (which must be
+#                 provided by the system or downloaded from the web),
+#                 removes the 'bold' weighted non-CJK fallback fonts (the
+#                 'normal' weight is still included and is used to
+#                 synthesize bold), and replaces standard CJK with low
+#                 quality CJK. However, higher quality Japanese is still
+#                 included. Because low quality CJK cannot synthesize bold,
+#                 bold glyphs are unavailable in Chinese and Korean. This
+#                 package is ~10.9MB.
+#   "limited"  -- A smaller package than 'limited_with_jp'. The two packages
+#                 are identical with the exception that 'limited' does not
+#                 include the higher quality Japanese font; instead it
+#                 relies on low quality CJK for all CJK characters. Because
+#                 low quality CJK cannot synthesize bold, bold glyphs are
+#                 unavailable in Chinese, Japanese, and Korean. This package
+#                 is ~7.7MB.
+#   "minimal"  -- The smallest possible font package. It only includes
+#                 Roboto's Basic Latin characters. Everything else must be
+#                 provided by the system or downloaded from the web. This
+#                 package is ~16.4KB.
+# NOTE: When bold is needed, but unavailable, it is typically synthesized,
+#       resulting in lower quality glyphs than those generated directly from
+#       a bold font. However, this does not occur with low quality CJK,
+#       which is not high enough quality to synthesize. Its glyphs always
+#       have a 'normal' weight.
+if (!defined(cobalt_font_package)) {
+  cobalt_font_package = "standard"
+}
+
+# Font package overrides can be used to modify the files included within the
+# selected package. The following values are available:
+#   -1 -- The package value for the specified category is not overridden.
+#    0 -- The package value is overridden and no fonts for the specified
+#         category are included.
+#    1 -- The package value is overridden and fonts from the specified
+#         category with a weight of 'normal' and a style of 'normal' are
+#         included.
+#    2 -- The package value is overridden and fonts from the specified
+#         category with a weight of either 'normal' or bold' and a style of
+#         'normal' are included.
+#    3 -- The package value is overridden and fonts from the specified
+#         category with a weight of either 'normal' or 'bold' and a style of
+#         either 'normal' or 'italic' are included.
+#    4 -- The package value is overridden and all available fonts from the
+#         specified category are included. This may include additional
+#         weights beyond 'normal' and 'bold'.
+# See content/fonts/README.md for details on the specific values used by
+# each of the packages use for the various font categories.
+if (!defined(cobalt_font_package_override_named_sans_serif)) {
+  cobalt_font_package_override_named_sans_serif = -1
+}
+if (!defined(cobalt_font_package_override_named_serif)) {
+  cobalt_font_package_override_named_serif = -1
+}
+if (!defined(cobalt_font_package_override_named_fcc_fonts)) {
+  cobalt_font_package_override_named_fcc_fonts = -1
+}
+if (!defined(cobalt_font_package_override_fallback_lang_non_cjk)) {
+  cobalt_font_package_override_fallback_lang_non_cjk = -1
+}
+if (!defined(cobalt_font_package_override_fallback_lang_cjk)) {
+  cobalt_font_package_override_fallback_lang_cjk = -1
+}
+if (!defined(cobalt_font_package_override_fallback_lang_cjk_low_quality)) {
+  cobalt_font_package_override_fallback_lang_cjk_low_quality = -1
+}
+if (!defined(cobalt_font_package_override_fallback_lang_jp)) {
+  cobalt_font_package_override_fallback_lang_jp = -1
+}
+if (!defined(cobalt_font_package_override_fallback_emoji)) {
+  cobalt_font_package_override_fallback_emoji = -1
+}
+if (!defined(cobalt_font_package_override_fallback_symbols)) {
+  cobalt_font_package_override_fallback_symbols = -1
+}
+
+# The target platform id as a string, like "ps4", etc..
+# TODO: eliminate when all platforms are fully starboard.
+if (!defined(sb_target_platform)) {
+  sb_target_platform = ""
+}
+
+# Defines what kind of rasterizer will be used.  This can be adjusted to
+# force a stub graphics implementation or software 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
+#                    rasterizer for some render tree node types, but is
+#                    generally faster on the CPU and GPU. This can handle
+#                    360 rendering.
+#   '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.
+if (!defined(rasterizer_type)) {
+  rasterizer_type = "hardware"
+}
+
+# If set to true, will enable support for rendering only the regions of the
+# display that are modified due to animations, instead of re-rendering the
+# entire scene each frame.  This feature can reduce startup time where
+# usually there is a small loading spinner animating on the screen.  On GLES
+# renderers, Cobalt will attempt to implement this support by using
+# eglSurfaceAttrib(..., EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED), otherwise
+# the dirty region will be silently disabled.  On Blitter API platforms,
+# if this is enabled, we explicitly create an extra offscreen full-size
+# intermediate surface to render into.  Note that some GLES driver
+# implementations may internally allocate an extra full screen surface to
+# support this feature, and many have been noticed to not properly support
+# this functionality (but they report that they do), and for these reasons
+# this value is defaulted to false.
+if (!defined(render_dirty_region_only)) {
+  render_dirty_region_only = false
+}
+
+# Modify this value to adjust the default rasterizer setting for your
+# platform.
+if (!defined(default_renderer_options_dependency)) {
+  default_renderer_options_dependency = "//cobalt/renderer:default_options"
+}
+
+# Allow throttling of the frame rate. This is expressed in terms of
+# milliseconds and can be a floating point number. Keep in mind that
+# swapping frames may take some additional processing time, so it may be
+# better to specify a lower delay. For example, "33" instead of "33.33"
+# for 30 Hz refresh.
+if (!defined(cobalt_minimum_frame_time_in_milliseconds)) {
+  cobalt_minimum_frame_time_in_milliseconds = "16.4"
+}
+
+# Cobalt will call eglSwapInterval() and specify this value before calling
+# eglSwapBuffers() each frame.
+if (!defined(cobalt_egl_swap_interval)) {
+  cobalt_egl_swap_interval = 1
+}
+
+# Set to true to build with DIAL support.
+if (!defined(enable_in_app_dial)) {
+  enable_in_app_dial = false
+}
+
+# Set to true to enable a custom MediaSessionClient.
+if (!defined(enable_custom_media_session_client)) {
+  enable_custom_media_session_client = false
+}
+
+# Set to true to enable H5vccAccountManager.
+if (!defined(enable_account_manager)) {
+  enable_account_manager = false
+}
+
+# Set to true to enable H5vccCrashLog.
+if (!defined(enable_crash_log)) {
+  enable_crash_log = false
+}
+
+# Set to true to enable H5vccSSO (Single Sign On).
+if (!defined(enable_sso)) {
+  enable_sso = false
+}
+
+# Set to true to compile with SPDY support.
+if (!defined(enable_spdy)) {
+  enable_spdy = false
+}
+
+# Set to true to enable filtering of HTTP headers before sending.
+if (!defined(enable_xhr_header_filtering)) {
+  enable_xhr_header_filtering = false
+}
+
+# Used by //cobalt/media to pick a proper media platform.
+if (!defined(sb_media_platform)) {
+  sb_media_platform = "starboard"
+}
+
+# List of platform-specific targets that get compiled into cobalt.
+if (!defined(cobalt_platform_dependencies)) {
+  cobalt_platform_dependencies = []
+}
+
+# The URL of default build time splash screen - see
+#   cobalt/doc/splash_screen.md for information about this.
+if (!defined(fallback_splash_screen_url)) {
+  fallback_splash_screen_url = "h5vcc-embedded://youtube_splash_screen.html"
+}
+
+# Cache parameters
+
+# The following set of parameters define how much memory is reserved for
+# different Cobalt caches.  These caches affect CPU *and* GPU memory usage.
+#
+# The sum of the following caches effectively describes the maximum GPU
+# texture memory usage (though it doesn't consider video textures and
+# display color buffers):
+#   - skia_cache_size_in_bytes (GLES2 rasterizer only)
+#   - scratch_surface_cache_size_in_bytes
+#   - surface_cache_size_in_bytes
+#   - image_cache_size_in_bytes
+#   - skia_glyph_atlas_width * skia_glyph_atlas_height
+#
+# The other caches affect CPU memory usage.
+
+# Determines the capacity of the skia cache.  The Skia cache is maintained
+# within Skia and is used to cache the results of complicated effects such
+# as shadows, so that Skia draw calls that are used repeatedly across
+# frames can be cached into surfaces.  This setting is only relevant when
+# using the hardware-accelerated Skia rasterizer (e.g. as opposed to the
+# Blitter API).
+if (!defined(skia_cache_size_in_bytes)) {
+  skia_cache_size_in_bytes = "(4 * 1024 * 1024)"
+}
+
+# Determines the capacity of the scratch surface cache.  The scratch
+# surface cache facilitates the reuse of temporary offscreen surfaces
+# within a single frame.  This setting is only relevant when using the
+# hardware-accelerated Skia rasterizer.
+if (!defined(scratch_surface_cache_size_in_bytes)) {
+  scratch_surface_cache_size_in_bytes = 0
+}
+
+# Determines the capacity of the surface cache.  The surface cache tracks
+# which render tree nodes are being re-used across frames and stores the
+# nodes that are most CPU-expensive to render into surfaces.
+if (!defined(surface_cache_size_in_bytes)) {
+  surface_cache_size_in_bytes = 0
+}
+
+# Determines the amount of GPU memory the offscreen target atlases will
+# use. This is specific to the direct-GLES rasterizer and serves a similar
+# purpose as the surface_cache_size_in_bytes, but caches any render tree
+# nodes which require skia for rendering. Two atlases will be allocated
+# from this memory or multiple atlases of the frame size if the limit
+# allows. It is recommended that enough memory be reserved for two RGBA
+# atlases about a quarter of the frame size.
+if (!defined(offscreen_target_cache_size_in_bytes)) {
+  offscreen_target_cache_size_in_bytes = -1
+}
+
+# Determines the capacity of the image cache, which manages image surfaces
+# downloaded from a web page.  While it depends on the platform, often (and
+# ideally) these images are cached within GPU memory.
+# Set to -1 to automatically calculate the value at runtime, based on
+# features like windows dimensions and the value of
+# SbSystemGetTotalGPUMemory().
+if (!defined(image_cache_size_in_bytes)) {
+  image_cache_size_in_bytes = -1
+}
+
+# Determines the capacity of the local font cache, which manages all fonts
+# loaded from local files. Newly encountered sections of font files are
+# lazily loaded into the cache, enabling subsequent requests to the same
+# file sections to be handled via direct memory access. Once the limit is
+# reached, further requests are handled via file stream.
+# Setting the value to 0 disables memory caching and causes all font file
+# accesses to be done using file streams.
+if (!defined(local_font_cache_size_in_bytes)) {
+  local_font_cache_size_in_bytes = "(16 * 1024 * 1024)"
+}
+
+# Determines the capacity of the remote font cache, which manages all
+# fonts downloaded from a web page.
+if (!defined(remote_font_cache_size_in_bytes)) {
+  remote_font_cache_size_in_bytes = "(4 * 1024 * 1024)"
+}
+
+# Determines the capacity of the mesh cache. Each mesh is held compressed
+# in main memory, to be inflated into a GPU buffer when needed for
+# projection. When set to "auto", will be adjusted according to whether
+# the enable_map_to_mesh is true or not.  If enable_map_to_mesh is false,
+# then the mesh cache size will be set to 0.
+if (!defined(mesh_cache_size_in_bytes)) {
+  mesh_cache_size_in_bytes = "auto"
+}
+
+# Only relevant if you are using the Blitter API.
+# Determines the capacity of the software surface cache, which is used to
+# cache all surfaces that are rendered via a software rasterizer to avoid
+# re-rendering them.
+if (!defined(software_surface_cache_size_in_bytes)) {
+  software_surface_cache_size_in_bytes = "(8 * 1024 * 1024)"
+}
+
+# Modifying this value to be non-1.0f will result in the image cache
+# capacity being cleared and then temporarily reduced for the duration that
+# a video is playing.  This can be useful for some platforms if they are
+# particularly constrained for (GPU) memory during video playback.  When
+# playing a video, the image cache is reduced to:
+# image_cache_size_in_bytes *
+#     image_cache_capacity_multiplier_when_playing_video.
+if (!defined(image_cache_capacity_multiplier_when_playing_video)) {
+  image_cache_capacity_multiplier_when_playing_video = "1.0f"
+}
+
+# Determines the size in pixels of the glyph atlas where rendered glyphs are
+# cached. The resulting memory usage is 2 bytes of GPU memory per pixel.
+# When a value is used that is too small, thrashing may occur that will
+# result in visible stutter. Such thrashing is more likely to occur when CJK
+# language glyphs are rendered and when the size of the glyphs in pixels is
+# larger, such as for higher resolution displays.
+# The negative default values indicates to the engine that these settings
+# should be automatically set.
+if (!defined(skia_glyph_atlas_width)) {
+  skia_glyph_atlas_width = "-1"
+}
+if (!defined(skia_glyph_atlas_height)) {
+  skia_glyph_atlas_height = "-1"
+}
+
+# Determines the size of garbage collection threshold. After this many bytes
+# have been allocated, the mozjs garbage collector will run. Lowering this
+# has been found to reduce performance and decrease JavaScript memory usage.
+# For example, we have measured on at least one platform that performance
+# becomes 7% worse on average in certain cases when adjusting this number
+# from 8MB to 1MB.
+if (!defined(mozjs_garbage_collection_threshold_in_bytes)) {
+  mozjs_garbage_collection_threshold_in_bytes = "(8 * 1024 * 1024)"
+}
+
+# Max Cobalt CPU usage specifies that the cobalt program should
+# keep it's size below the specified size. A value of -1 causes this
+# value to be assumed from the starboard API function:
+# SbSystemGetTotalCPUMemory().
+if (!defined(max_cobalt_cpu_usage)) {
+  max_cobalt_cpu_usage = -1
+}
+
+if (!defined(max_cobalt_gpu_usage)) {
+  # Max Cobalt GPU usage specifies that the cobalt program should
+  # keep it's size below the specified size. A value of -1 causes this
+  # value to be assumed from the starboard API function:
+  # SbSystemGetTotalGPUMemory().
+  max_cobalt_gpu_usage = -1
+}
+
+# When specified this value will reduce the cpu memory consumption by
+# the specified amount. -1 disables the value.
+# When this value is specified then max_cobalt_cpu_usage will not be
+# used in memory_constrainer, but will still be used for triggering
+# a warning if the engine consumes more memory than this value specifies.
+if (!defined(reduce_cpu_memory_by)) {
+  reduce_cpu_memory_by = -1
+}
+
+# When specified this value will reduce the gpu memory consumption by
+# the specified amount. -1 disables the value.
+# When this value is specified then max_cobalt_gpu_usage will not be
+# used in memory_constrainer, but will still be used for triggering
+# a warning if the engine consumes more memory than this value specifies.
+if (!defined(reduce_gpu_memory_by)) {
+  reduce_gpu_memory_by = -1
+}
+
+# The only currently-supported Javascript engine is "mozjs-45". (However,
+# this may potentially change in the future.)
+if (!defined(javascript_engine)) {
+  javascript_engine = "mozjs-45"
+}
+
+# Enable jit instead of running in interpreter-only mode by default.
+# We have found that disabling jit often results in faster JavaScript
+# execution and lower memory usage.
+# Setting this to true on a platform or engine for which there is no JIT
+# implementation has no effect.
+if (!defined(cobalt_enable_jit)) {
+  cobalt_enable_jit = false
+}
+
+# Use media source extension implementation that is conformed to the
+# Candidate Recommandation of July 5th 2016.
+if (!defined(cobalt_use_media_source_2016)) {
+  cobalt_use_media_source_2016 = true
+}
+
+# Note that the following media buffer related variables are only used when
+# |cobalt_use_media_source_2016| is set to true.
+
+# This can be set to "memory" or "file".  When it is set to "memory", the
+# media buffers will be stored in main memory allocated by SbMemory
+# functions.  When it is set to "file", the media buffers will be stored in
+# a temporary file in the system cache folder acquired by calling
+# SbSystemGetPath() with "kSbSystemPathCacheDirectory".  Note that when its
+# value is "file" the media stack will still allocate memory to cache the
+# the buffers in use.
+if (!defined(cobalt_media_buffer_storage_type)) {
+  cobalt_media_buffer_storage_type = "memory"
+}
+# When either |cobalt_media_buffer_initial_capacity| or
+# |cobalt_media_buffer_allocation_unit| isn't zero, media buffers will be
+# allocated using a memory pool.  Set the following variable to true to
+# allocate the media buffer pool memory on demand and return all memory to
+# the system when there is no media buffer allocated.  Setting the following
+# value to false results in that Cobalt will allocate
+# |cobalt_media_buffer_initial_capacity| bytes for media buffer on startup
+# and will not release any media buffer memory back to the system even if
+# there is no media buffers allocated.
+if (!defined(cobalt_media_buffer_pool_allocate_on_demand)) {
+  cobalt_media_buffer_pool_allocate_on_demand = true
+}
+# The amount of memory that will be used to store media buffers allocated
+# during system startup.  To allocate a large chunk at startup helps with
+# reducing fragmentation and can avoid failures to allocate incrementally.
+# This can be set to 0.
+if (!defined(cobalt_media_buffer_initial_capacity)) {
+  cobalt_media_buffer_initial_capacity = "(21 * 1024 * 1024)"
+}
+# When the media stack needs more memory to store media buffers, it will
+# allocate extra memory in units of |cobalt_media_buffer_allocation_unit|.
+# This can be set to 0, in which case the media stack will allocate extra
+# memory on demand.  When |cobalt_media_buffer_initial_capacity| and this
+# value are both set to 0, the media stack will allocate individual buffers
+# directly using SbMemory functions.
+if (!defined(cobalt_media_buffer_allocation_unit)) {
+  cobalt_media_buffer_allocation_unit = "(1 * 1024 * 1024)"
+}
+
+# The media buffer will be allocated using the following alignment.  Set
+# this to a larger value may increase the memory consumption of media
+# buffers.
+if (!defined(cobalt_media_buffer_alignment)) {
+  cobalt_media_buffer_alignment = 0
+}
+# Extra bytes allocated at the end of a media buffer to ensure that the
+# buffer can be use optimally by specific instructions like SIMD.  Set to 0
+# to remove any padding.
+if (!defined(cobalt_media_buffer_padding)) {
+  cobalt_media_buffer_padding = 0
+}
+
+# The memory used when playing mp4 videos that is not in DASH format.  The
+# resolution of such videos shouldn't go beyond 1080p.  Its value should be
+# less than the sum of 'cobalt_media_buffer_non_video_budget' and
+# 'cobalt_media_buffer_video_budget_1080p' but not less than 8 MB.
+if (!defined(cobalt_media_buffer_progressive_budget)) {
+  cobalt_media_buffer_progressive_budget = "(12 * 1024 * 1024)"
+}
+
+# Specifies the maximum amount of memory used by audio or text buffers of
+# media source before triggering a garbage collection.  A large value will
+# cause more memory being used by audio buffers but will also make
+# JavaScript app less likely to re-download audio data.  Note that the
+# JavaScript app may experience significant difficulty if this value is too
+# low.
+if (!defined(cobalt_media_buffer_non_video_budget)) {
+  cobalt_media_buffer_non_video_budget = "(5 * 1024 * 1024)"
+}
+
+# Specifies the maximum amount of memory used by video buffers of media
+# source before triggering a garbage collection when the video resolution is
+# lower than 1080p (1920x1080).  A large value will cause more memory being
+# used by video buffers but will also make JavaScript app less likely to
+# re-download video data.  Note that the JavaScript app may experience
+# significant difficulty if this value is too low.
+if (!defined(cobalt_media_buffer_video_budget_1080p)) {
+  cobalt_media_buffer_video_budget_1080p = "(16 * 1024 * 1024)"
+}
+# Specifies the maximum amount of memory used by video buffers of media
+# source before triggering a garbage collection when the video resolution is
+# lower than 4k (3840x2160).  A large value will cause more memory being
+# used by video buffers but will also make JavaScript app less likely to
+# re-download video data.  Note that the JavaScript app may experience
+# significant difficulty if this value is too low.
+if (!defined(cobalt_media_buffer_video_budget_4k)) {
+  cobalt_media_buffer_video_budget_4k = "(60 * 1024 * 1024)"
+}
+
+# Set to true to enable MediaKeySession::keyStatuses and
+# MediaKeySession::onkeystatuseschange support.  This requires that
+# SB_API_VERSION is greater than or equal to
+# SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION.
+if (!defined(cobalt_encrypted_media_extension_enable_key_statuses_update)) {
+  cobalt_encrypted_media_extension_enable_key_statuses_update = true
+}
+
+# Enables embedding Cobalt as a shared library within another app. This
+# requires a 'lib' starboard implementation for the corresponding platform.
+if (!defined(cobalt_enable_lib)) {
+  cobalt_enable_lib = sb_enable_lib
+}
+
+# For configurations other than Gold, set the flag that lets test data files be
+# copied and carried along with the build.
+# Clients must copy over all content; to avoid having to copy over extra data,
+# we omit the test data
+_copy_test_data = (cobalt_config != "gold" && !cobalt_enable_lib)
+if (!defined(cobalt_copy_debug_console)) {
+  cobalt_copy_debug_console = _copy_test_data
+}
+if (!defined(cobalt_copy_test_data)) {
+  cobalt_copy_test_data = _copy_test_data
+}
+if (!defined(enable_about_scheme)) {
+  enable_about_scheme = _copy_test_data
+}
+if (!defined(enable_fake_microphone)) {
+  enable_fake_microphone = _copy_test_data
+}
+if (!defined(enable_file_scheme)) {
+  enable_file_scheme = _copy_test_data
+}
+if (!defined(enable_network_logging)) {
+  enable_network_logging = _copy_test_data
+}
+if (!defined(enable_remote_debugging)) {
+  enable_remote_debugging = _copy_test_data
+}
+if (!defined(enable_screenshot)) {
+  enable_screenshot = _copy_test_data
+}
+if (!defined(enable_webdriver)) {
+  enable_webdriver = _copy_test_data
+}
+
+
+# Use system libjpeg.
+if (!defined(use_system_libjpeg)) {
+  use_system_libjpeg = false
+}
+
+# TODO: add ARM compilation flags to the starboard platform config
+
+# TODO: implement remaining flags like clang_type_profiler, build_for_tool, order_profiling, etc.
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index 0f974b5..a3243c1 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -12,12 +12,31 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+#####################################################################
+# If you modify this file, PLEASE REMEMBER TO UPDATE
+# //cobalt/build/config/base.gni or //starboard/build/config/base.gni
+# AS WELL
+#####################################################################
+
 # This file should be included in all .gyp files used by Cobalt. Normally,
 # this should be done automatically by gyp_cobalt.
 {
   'variables': {
     # Cobalt variables.
 
+    # We need to define some variables inside of an inner 'variables' scope
+    # so that they can be referenced by other outer variables here.  Also, it
+    # allows for the specification of default values that get referenced by
+    # a top level scope.
+    'variables': {
+      # 'sb_enable_lib' is initially defined inside this inner 'variables' dict so
+      # that it can be accessed by 'cobalt_enable_lib' below.
+      'sb_enable_lib%': 0,
+
+      'cobalt_webapi_extension_source_idl_files%': [],
+      'cobalt_webapi_extension_generated_header_idl_files%': [],
+    },
+
     # Whether Cobalt is being built.
     'cobalt': 1,
 
@@ -33,13 +52,8 @@
     # implement spherical video playback.
     'enable_map_to_mesh%': 0,
 
-    # 'sb_enable_lib' is initially defined inside this inner 'variables' dict so
-    # that it can be accessed by 'cobalt_enable_lib' below here.
-    'variables': {
-      # Enables embedding Cobalt as a shared library within another app. This
-      # requires a 'lib' starboard implementation for the corresponding platform.
-      'sb_enable_lib%': 0,
-    },
+    # Enables embedding Cobalt as a shared library within another app. This
+    # requires a 'lib' starboard implementation for the corresponding platform.
     'sb_enable_lib%': '<(sb_enable_lib)',
     'cobalt_enable_lib': '<(sb_enable_lib)',
 
@@ -177,10 +191,31 @@
     # this value is defaulted to 0.
     'render_dirty_region_only%': 0,
 
-    # Modify this value to adjust the default rasterizer setting for your
+    # Override this value to adjust the default rasterizer setting for your
     # platform.
     'default_renderer_options_dependency%': '<(DEPTH)/cobalt/renderer/default_options_starboard.gyp:default_options',
 
+    # Override this to inject a custom interface into Cobalt's JavaScript
+    # `window` global object.  This implies that you will have to provide your
+    # own IDL files to describe that interface and all interfaces that it
+    # references.  See cobalt/doc/webapi_extension.md for more information.
+    'cobalt_webapi_extension_source_idl_files%': [
+      '<@(cobalt_webapi_extension_source_idl_files)'\
+    ],
+    # Override this to have Cobalt build IDL files that result in generated
+    # header files that may need to be included from other C++ source files.
+    # This includes, for example, IDL enumerations.  See
+    # cobalt/doc/webapi_extension.md for more information.
+    'cobalt_webapi_extension_generated_header_idl_files%': [
+      '<@(cobalt_webapi_extension_generated_header_idl_files)'
+    ],
+
+    # This gyp target must implement the functions defined in
+    # <(DEPTH)/cobalt/browser/idl_extensions.h.  See
+    # cobalt/doc/webapi_extension.md for more information.
+    'cobalt_webapi_extension_gyp_target%':
+        '<(DEPTH)/cobalt/browser/null_webapi_extension.gyp:null_webapi_extension',
+
     # Allow throttling of the frame rate. This is expressed in terms of
     # milliseconds and can be a floating point number. Keep in mind that
     # swapping frames may take some additional processing time, so it may be
@@ -518,6 +553,12 @@
     # re-download video data.  Note that the JavaScript app may experience
     # significant difficulty if this value is too low.
     'cobalt_media_buffer_video_budget_4k%': 60 * 1024 * 1024,
+
+    # Set to 1 to enable MediaKeySession::keyStatuses and
+    # MediaKeySession::onkeystatuseschange support.  This requires that
+    # SB_API_VERSION is greater than or equal to
+    # SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION.
+    'cobalt_encrypted_media_extension_enable_key_statuses_update%': 1,
   },
 
   'target_defaults': {
@@ -544,6 +585,7 @@
       'COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=<(cobalt_media_buffer_non_video_budget)',
       'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=<(cobalt_media_buffer_video_budget_1080p)',
       'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=<(cobalt_media_buffer_video_budget_4k)',
+      'COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE=<(cobalt_encrypted_media_extension_enable_key_statuses_update)',
     ],
     'cflags': [ '<@(compiler_flags)' ],
     'ldflags': [ '<@(linker_flags)' ],
diff --git a/src/cobalt/build/get_build_id.py b/src/cobalt/build/get_build_id.py
new file mode 100755
index 0000000..e2e5ad7
--- /dev/null
+++ b/src/cobalt/build/get_build_id.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Prints out the Cobalt Build ID."""
+
+import os.path
+import sys
+
+sys.path.append(
+    os.path.abspath(
+        os.path.join(os.pardir, os.pardir, 'cobalt', 'build')))
+from gyp_utils import GetBuildNumber  # pylint: disable=g-import-not-at-top
+
+
+def main():
+  print GetBuildNumber()
+
+if __name__ == '__main__':
+  main()
diff --git a/src/cobalt/build/get_starboard_path.py b/src/cobalt/build/get_starboard_path.py
new file mode 100755
index 0000000..1d32707
--- /dev/null
+++ b/src/cobalt/build/get_starboard_path.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Prints the path to the given starboard platform's configuration directory.
+"""
+
+import sys
+
+import gyp_utils
+
+
+def main():
+  platforms = gyp_utils.GetAllPlatforms()
+  print platforms[sys.argv[1]].path
+
+
+if __name__ == '__main__':
+  main()
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index 6e4d467..36e8e8a 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -6370,7 +6370,6 @@
     std::string property_name = $1.ToString();
     DCHECK_GT(property_name.size(), 0U);
 
-#ifdef __LB_SHELL__FORCE_LOGGING__
     // Do not warn about non-standard or non-WebKit properties.
     if (property_name[0] != '-') {
       base::AutoLock lock(non_trivial_static_fields.Get().lock);
@@ -6383,7 +6382,6 @@
         parser_impl->LogWarning(@1, "unsupported property " + property_name);
       }
     }
-#endif  // __LB_SHELL__FORCE_LOGGING__
 
     $$ = NULL;
   }
diff --git a/src/cobalt/css_parser/parser.cc b/src/cobalt/css_parser/parser.cc
index b8b9047..98b1521 100644
--- a/src/cobalt/css_parser/parser.cc
+++ b/src/cobalt/css_parser/parser.cc
@@ -229,7 +229,6 @@
 };
 
 // TODO: Stop deduplicating warnings.
-#ifdef __LB_SHELL__FORCE_LOGGING__
 namespace {
 
 struct NonTrivialStaticFields {
@@ -242,7 +241,6 @@
     LAZY_INSTANCE_INITIALIZER;
 
 }  // namespace
-#endif  // __LB_SHELL__FORCE_LOGGING__
 
 ParserImpl::ParserImpl(const std::string& input,
                        const base::SourceLocation& input_location,
diff --git a/src/cobalt/debug/debug_web_server.cc b/src/cobalt/debug/debug_web_server.cc
index 76b371f..6ffd152 100644
--- a/src/cobalt/debug/debug_web_server.cc
+++ b/src/cobalt/debug/debug_web_server.cc
@@ -80,21 +80,11 @@
   return base::nullopt;
 }
 
-std::string GetLocalIpAddress() {
+base::optional<std::string> GetLocalIpAddress() {
   net::IPEndPoint ip_addr;
-#if defined(__LB_SHELL__)
-  struct sockaddr_in addr = {0};
-  addr.sin_family = AF_INET;
-  lb_get_local_ip_address(&addr.sin_addr);
-  bool result =
-      ip_addr.FromSockAddr(reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
-  DCHECK(result);
-#elif defined(OS_STARBOARD)
-
-#if SB_API_VERSION >= 4
   SbSocketAddress local_ip;
-  SbMemorySet(&(local_ip.address), 0, sizeof(local_ip.address));
-
+  SbMemorySet(&local_ip, 0, sizeof(local_ip));
+#if SB_API_VERSION >= 4
   bool result = false;
 
   // Prefer IPv4 addresses, as they're easier to type for debugging.
@@ -115,17 +105,24 @@
     }
   }
 
-  DCHECK(result);
+  if (!result) {
+    DLOG(WARNING) << "Unable to get a local interface address.";
+    return base::nullopt;
+  }
 #else
-  SbSocketAddress sb_address;
-  bool result = SbSocketGetLocalInterfaceAddress(&sb_address);
-  DCHECK(result);
-  result = ip_addr.FromSbSocketAddress(&sb_address);
+  bool result = SbSocketGetLocalInterfaceAddress(&local_ip);
+  if (!result) {
+    DLOG(WARNING) << "Unable to get a local interface address.";
+    return base::nullopt;
+  }
+
+  result = ip_addr.FromSbSocketAddress(&local_ip);
+  if (!result) {
+    LOG(WARNING) << "Got invalid local interface address.";
+    return base::nullopt;
+  }
 #endif  // SB_API_VERSION >= 4
 
-#else
-#error "Not Implemented."
-#endif
   return ip_addr.ToStringWithoutPort();
 }
 
@@ -339,8 +336,13 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Create http server
-  const std::string ip_addr = GetLocalIpAddress();
-  factory_.reset(new net::TCPListenSocketFactory(ip_addr, port));
+  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;
+  }
+  factory_.reset(new net::TCPListenSocketFactory(*ip_addr, port));
   server_ = new net::HttpServer(*factory_, this);
 
   std::string address;
diff --git a/src/cobalt/doc/gn_quick_start.md b/src/cobalt/doc/gn_quick_start.md
new file mode 100644
index 0000000..b858afe
--- /dev/null
+++ b/src/cobalt/doc/gn_quick_start.md
@@ -0,0 +1,349 @@
+# GN Quick Start Guide for Cobalt
+
+This guide was adapted from
+the [GN Quick Start Guide for Chromium][chromium-guide].
+
+[chromium-guide]: https://chromium.googlesource.com/chromium/src/+/master/tools/gn/docs/quick_start.md
+
+[TOC]
+
+## Running GN
+
+You just run `gn` from the command line. There is a script in
+`depot_tools`, which is presumably in your PATH, with this name. The
+script will find the binary in the source tree containing the current
+directory and run it.
+
+## Setting up a build
+
+In GYP, the system would generate `<platform>_debug`, `<platform>_devel`,
+`<platform>_qa` and `<platform>_gold` build directories for you and configure
+them accordingly. GN doesn't do this.  Instead, you set up whatever build
+directory you want with whatever configuration you want. The Ninja files will be
+automatically regenerated if they're out of date when you build in that
+directory.
+
+To make a build directory:
+
+```
+gn gen out/my_build
+```
+
+## Passing build arguments
+
+Set build arguments on your build directory by running:
+
+```
+gn args out/my_build
+```
+
+This will bring up an editor. Type build args into that file like this:
+
+```
+target_platform = "linux-x64x11"
+cobalt_config = "gold"
+```
+
+You can see the list of available arguments and their default values by
+typing
+
+```
+gn args --list out/my_build
+```
+
+on the command line. Note that you have to specify the build directory
+for this command because the available arguments can change according
+to what's set.
+
+## Configuring goma
+
+Run `gn args out/Default` (substituting your build directory as needed).
+Add:
+
+```
+use_goma = true
+goma_dir = "~/foo/bar/goma"
+```
+
+If your goma is in the default location (`~/goma`) then you can omit the
+`goma_dir` line.
+
+Currently, `use_goma` is enabled by default for stub and linux platforms, and
+disabled for others.
+
+## Step-by-step
+
+### Adding a build file
+
+Create a `cobalt/samples/gn_tutorial/BUILD.gn` file and enter the following:
+
+```
+executable("hello_world") {
+  sources = [
+    "hello_world.cc",
+  ]
+}
+```
+
+There should already be a `hello_world.cc` file in that directory,
+containing what you expect. That's it! Now we just need to tell the
+build about this file. Open the `BUILD.gn` file in the root directory
+and add the label of this target to the dependencies of one of the root
+groups (a "group" target is a meta-target that is just a collection of
+other targets):
+
+```
+group("all") {
+  deps = [
+    ...
+    "//starboard:all",
+    "//cobalt/samples/gn_tutorial:hello_world",
+  ]
+}
+```
+
+You can see the label of your target is "//" (indicating the source
+root), followed by the directory name, a colon, and the target name.
+
+### Testing your addition
+
+From the command line in the source root directory:
+
+```
+gn gen out/Default
+ninja -C out/Default hello_world
+out/Default/hello_world
+```
+
+(Remember to specify `target_platform` and `cobalt_config` in `gn args` if
+you haven't done so already.)
+
+GN encourages target names for static libraries that aren't globally
+unique. To build one of these, you can pass the label with no leading
+"//" to ninja:
+
+```
+ninja -C out/Default cobalt/samples/gn_tutorial:hello_world
+```
+
+### Declaring dependencies
+
+Let's make a static library that has a function to say hello to random
+people. There is a source file `hello.cc` in that directory which has a
+function to do this. Open the `cobalt/samples/gn_tutorial/BUILD.gn` file and add
+the static library to the bottom of the existing file:
+
+```
+static_library("hello") {
+  sources = [
+    "hello.cc",
+  ]
+}
+```
+
+Now let's add an executable that depends on this library:
+
+```
+executable("say_hello") {
+  sources = [
+    "say_hello.cc",
+  ]
+  deps = [
+    ":hello",
+  ]
+}
+```
+
+This executable includes one source file and depends on the previous
+static library. The static library is referenced by its label in the
+`deps`. You could have used the full label `//cobalt/samples/gn_tutorial:hello`
+but if you're referencing a target in the same build file, you can use
+the shortcut `:hello`.
+
+### Test the static library version
+
+From the command line in the source root directory:
+
+```
+ninja -C out/Default say_hello
+out/Default/say_hello
+```
+
+Note that you **didn't** need to re-run GN. GN will automatically rebuild
+the ninja files when any build file has changed. You know this happens
+when ninja prints `[1/1] Regenerating ninja files` at the beginning of
+execution.
+
+### Compiler settings
+
+Our hello library has a new feature, the ability to say hello to two
+people at once. This feature is controlled by defining `TWO_PEOPLE`. We
+can add defines like so:
+
+```
+static_library("hello") {
+  sources = [
+    "hello.cc",
+  ]
+  defines = [
+    "TWO_PEOPLE",
+  ]
+}
+```
+
+### Putting settings in a config
+
+However, users of the library also need to know about this define, and
+putting it in the static library target defines it only for the files
+there. If somebody else includes `hello.h`, they won't see the new
+definition. To see the new definition, everybody will have to define
+`TWO_PEOPLE`.
+
+GN has a concept called a "config" which encapsulates settings. Let's
+create one that defines our preprocessor define:
+
+```
+config("hello_config") {
+  defines = [
+    "TWO_PEOPLE",
+  ]
+}
+```
+
+To apply these settings to your target, you only need to add the
+config's label to the list of configs in the target:
+
+```
+static_library("hello") {
+  ...
+  configs += [
+    ":hello_config",
+  ]
+}
+```
+
+Note that you need "+=" here instead of "=" since the build
+configuration has a default set of configs applied to each target that
+set up the default build stuff. You want to add to this list rather than
+overwrite it. To see the default configs, you can use the `print`
+function in the build file or the `desc` command-line subcommand (see
+below for examples of both).
+
+### Dependent configs
+
+This nicely encapsulates our settings, but still requires everybody that
+uses our library to set the config on themselves. It would be nice if
+everybody that depends on our `hello` library can get this
+automatically. Change your library definition to:
+
+```
+static_library("hello") {
+  sources = [
+    "hello.cc",
+  ]
+  all_dependent_configs = [
+    ":hello_config"
+  ]
+}
+```
+
+This applies the `hello_config` to the `hello` target itself, plus all
+targets that transitively depend on the current one. Now everybody that
+depends on us will get our settings. You can also set `public_configs`
+which applies only to targets that directly depend on your target (not
+transitively).
+
+Now if you compile and run, you'll see the new version with two people:
+
+```
+> ninja -C out/Default say_hello
+ninja: Entering directory 'out/Default'
+[1/1] Regenerating ninja files
+[4/4] LINK say_hello
+> out/Default/say_hello
+Hello, Bill and Joy.
+```
+
+## Add a new build argument
+
+You declare which arguments you accept and specify default values via
+`declare_args`.
+
+```
+declare_args() {
+  enable_teleporter = true
+  enable_doom_melon = false
+}
+```
+
+See `gn help buildargs` for an overview of how this works.
+See `gn help declare_args` for specifics on declaring them.
+
+It is an error to declare a given argument more than once in a given scope, so
+care should be used in scoping and naming arguments.
+
+## Don't know what's going on?
+
+You can run GN in verbose mode to see lots of messages about what it's
+doing. Use `-v` for this.
+
+### Print debugging
+
+There is a `print` command which just writes to stdout:
+
+```
+static_library("hello") {
+  ...
+  print(configs)
+}
+```
+
+This will print all of the configs applying to your target (including
+the default ones).
+
+### The "desc" command
+
+You can run `gn desc <build_dir> <targetname>` to get information about
+a given target:
+
+```
+gn desc out/Default //cobalt/samples/gn_tutorial:say_hello
+```
+
+will print out lots of exciting information. You can also print just one
+section. Lets say you wanted to know where your `TWO_PEOPLE` define
+came from on the `say_hello` target:
+
+```
+> gn desc out/Default //cobalt/samples/gn_tutorial:say_hello defines --blame
+...lots of other stuff omitted...
+  From //cobalt/samples/gn_tutorial:hello_config
+       (Added by //cobalt/samples/gn_tutorial/BUILD.gn:12)
+    TWO_PEOPLE
+```
+
+You can see that `TWO_PEOPLE` was defined by a config, and you can also
+see the which line caused that config to be applied to your target (in
+this case, the `all_dependent_configs` line).
+
+Another particularly interesting variation:
+
+```
+gn desc out/Default //starboard:all deps --tree
+```
+
+See `gn help desc` for more.
+
+### Performance
+
+You can see what took a long time by running it with the `--time` command
+line flag. This will output a summary of timings for various things.
+
+You can also make a trace of how the build files were executed:
+
+```
+gn --tracelog=mylog.trace
+```
+
+and you can load the resulting file in Chrome's `about:tracing` page to
+look at everything.
diff --git a/src/cobalt/doc/gyp_gn_files.md b/src/cobalt/doc/gyp_gn_files.md
new file mode 100644
index 0000000..452bfa7
--- /dev/null
+++ b/src/cobalt/doc/gyp_gn_files.md
@@ -0,0 +1,19 @@
+# GYP files and their corresponding GN files
+
+Generally, `foo/bar/bar.gyp` corresponds to `foo/bar/BUILD.gn`, while
+`foo/bar/baz.gyp` corresponds to either `foo/bar/baz/BUILD.gn` or
+`foo/bar/BUILD.gn` (depending on whether it made sense to combine `baz.gyp` with
+`bar.gyp`). `foo/bar/quux.gypi` typically corresponds to
+`foo/bar/quux.gni`. Here is a table of irregular correspondences:
+
+GYP File                                            | GN File                                                                   | Notes
+--------------------------------------------------- | ------------------------------------------------------------------------- |------
+build/common.gypi                                   | cobalt/build/config/base.gni, starboard/build/config/base.gni (variables) | A few variables have been omitted, moved to `BUILDCONFIG.gn` instead, or refactored into configs. See the GYP -> GN cookbook for more info.
+build/common.gypi                                   | cobalt/build/config/BUILD.gn (target defaults)
+cobalt/build/all.gyp                                | BUILD.gn (in root directory)                                              | GN requires this location to be used
+cobalt/build/config/base.gypi                       | cobalt/build/config/base.gni, starboard/build/config/base.gni (variables) | See comments for `build/common.gypi`
+starboard/linux/shared/compiler_flags.gypi          | starboard/linux/shared/BUILD.gn                                           | "Compiler Defaults" section
+starboard/linux/shared/starboard_base_symbolize.gyp | starboard/linux/shared/BUILD.gn                                           | "starboard_platform Target" section
+starboard/linux/shared/starboard_platform.gypi      | starboard/linux/shared/BUILD.gn                                           | "starboard_platform Target" section
+starboard/linux/x64x11/libraries.gypi               | starboard/linux/x64x11/BUILD.gn                                           | `libs` variable of the `compiler_defaults` config
+starboard/starboard_base_target.gypi                | starboard/build/config/BUILD.gn                                           | "Compiler Defaults" section
diff --git a/src/cobalt/doc/gyp_to_gn.md b/src/cobalt/doc/gyp_to_gn.md
new file mode 100644
index 0000000..c7fbc74
--- /dev/null
+++ b/src/cobalt/doc/gyp_to_gn.md
@@ -0,0 +1,349 @@
+# GYP → GN Conversion Cookbook (Cobalt Edition)
+
+[TOC]
+
+## Foreword
+
+Read the GN docs to familiarize yourself with the GN build system. I recommend
+reading at least our [Quick Start][quick-start] guide, and skimming
+the [Language document][language] and [Style Guide][style-guide] too.
+The [GN and GYP Patterns][gn-gyp-patterns] document has some useful info too,
+although not all of it is applicable to Cobalt.
+
+Also read [Chromium's GYP → GN conversion cookbook][chromium-cookbook] if you
+haven't already. The guidelines in that cookbook generally apply here. This
+document consists of Cobalt-specific addenda to that document.
+
+If you are uncertain how a certain idiom is expressed in GN, I suggest
+consulting the Chromium code to see how they did it. Not all of Chromium's GN
+idioms are adaptable to Cobalt (primarily because Chromium has no Starboard
+concept), but it is a good place to start.
+
+[quick-start]: https://cobalt.googlesource.com/cobalt/+/master/src/cobalt/doc/gn_quick_start.md
+[language]: https://chromium.googlesource.com/chromium/src/+/master/tools/gn/docs/language.md
+[style-guide]: https://chromium.googlesource.com/chromium/src/+/master/tools/gn/docs/style_guide.md
+[gn-gyp-patterns]: https://docs.google.com/document/d/1xuInfaOjQQ00mtzaTPfiLH-Hw1wlxfH-60jHoa921Lc/edit
+[chromium-cookbook]: https://chromium.googlesource.com/experimental/chromium/src/+/refs/wip/bajones/webvr/tools/gn/docs/cookbook.md
+
+## gyp_to_gn.py
+
+There is a script to partially automate the GYP to GN conversion in
+`cobalt/tools/gyp_to_gn.py`. To run it, use:
+
+    cobalt/tools/gyp_to_gn.py path/to/module/module.gyp | gn format --stdin > path/to/module/BUILD.gn
+
+This script is able to produce reasonably close GN output for many GYP files.
+However, it is not perfect, and some manual inspection and revision of the
+output is often necessary. As of this writing, known issues in addition to the
+ones the script warns about are:
+
+1. It doesn't translate over comments. This is because the script uses `eval`
+   to load the GYP file, which naturally cuts out all of the comments.
+1. Sometimes the script will output `sources =` when it should have outputted
+   `sources +=`. This is because the GYP file had `'sources':` in both places,
+   but one of those places was inside a `conditions` block or similar. This bug
+   can be fixed, but it will take a little work.
+
+   The same problem can happen with other variables besides `sources`, e.g.
+   `defines`, `deps`, etc.
+
+The script relies on two helper files in its directory, `variable_rewrites.dict`
+and `deps_substitutions.txt`. `variable_rewrites.dict` is a Python dictionary
+containing variables which were renamed or changed into booleans.
+`deps_substitutions.txt` is a tab-delimited file containing a (non-exhaustive)
+list of targets which were renamed.
+
+## File Correspondences
+
+`gyp_gn_files.md` in this directory contains a list of GYP -> GN file
+correspondences. If you create a new irregular file correspondence, please add
+it to this list before you forget!
+
+## Variables vs Build Args
+
+GN distinguishes between variables and build args. Build args are parameters
+declared inside a `declare_args` block, that are intended for developers to
+specify at the time they run gn. Ordinary variables are specified outside of
+`declare_args` blocks, and they are intended to not change from build to build.
+
+An example of a build arg is `use_goma`; if this build arg is turned on,
+compilation is done with Goma instead of locally. (As of this writing,
+`use_goma` is by default on for stub and linux platforms, and off on the
+others.) An example of a variable is `gl_type`; this is something which depends
+on the Starboard platform, and it doesn't make sense for individual developers
+to change the value of this variable at compile time.
+
+Some build behaviors controlled by environment variables in GYP been refactored
+into GN build args. Here is a list of correspondences:
+
+GYP            | GN                     | Defined in
+-------------- | ---------------------- | -----------------------------------------
+`LB_FASTBUILD` | `cobalt_use_fastbuild` | `//starboard/build/config/fastbuild.gni`
+`USE_ASAN`     | `use_asan`             | `//starboard/build/config/sanitizers.gni`
+`USE_TSAN`     | `use_tsan`             | `//starboard/build/config/sanitizers.gni`
+
+## Feature Flags
+
+Most variables defined in `cobalt/build/config/base.gypi` and
+`build/common.gypi` have been moved to `cobalt/build/config/base.gni` or
+`starboard/build/config/base.gni`, depending on whether they are Cobalt or
+Starboard variables. (A few, like `cobalt_config` and `starboard_path`, have
+been moved to `BUILDCONFIG.gn`, and others may have been moved to other places
+as well.)
+
+Some variables have not been copied over. Most commonly this is because the
+variable in question has been replaced by a config, group or something in GN
+more appropriate.
+
+Also, since GN has a true boolean type, variables which took on 0/1 values in
+GYP have been converted to take on true/false values in GN.
+
+Variables in Starboard platforms' `gyp_configuration.gypi` file have been moved
+to `configuration.gni` under the Starboard platform's directory.
+`base.gni` automatically imports the `configuration.gni` file of the
+right Starboard platform.
+
+GN doesn't allow defining a build arg twice with different values or changing
+the value of a variable/build arg by importing a file. It also has no analog to
+GYP's %-variable. Unfortunately for us, this means the code of `base.gni`
+must necessarily check whether every variable/build arg it defines has already
+been defined by the Starboard platform in `configuration.gni`.
+
+Chromium frowns upon files with too many variables or build arg definitions.
+They prefer to keep such definitions close to the files which actually use them.
+In contrast, we keep a single `base.gni` file, because it would be inconvenient
+for porters to have to browse through dozens of files to find out which variables
+they should override.
+
+## Build Args with Platform-Specific Defaults
+
+There are some build args, like `use_goma`, which have different default values
+on different Starboard platforms. Here is the generic declaration of `use_goma`,
+located in `//starboard/shared/toolchain/goma.gni`:
+
+    if (!defined(use_goma)) {
+      # Set to true to enable distributed compilation using Goma. By default
+      # we use Goma for stub and linux.
+      use_goma = false
+    }
+
+Notice a few things:
+
+1. As with feature flags, `goma.gni` first checks to see if the Starboard
+   platform configuration has already defined it (with a different default)
+   in `configuration.gni`.
+2. The comment is located right above the variable, inside the if statement.
+   This looks weird, but it's intentionally done this way so that the comment
+   will be printed out in `gn args --list`.
+3. Starboard platforms which define alternative defaults are encouraged to
+   copy the comment when making their own definition of `use_goma`. (See the
+   stub platform configuration for an example.) It's not strictly necessary, but
+   if it's not done, then `gn args --list` won't print any documentation for
+   this build arg.
+
+## BUILDCONFIG Variables
+
+Some things which must necessarily be defined in `BUILDCONFIG.gn` may depend on
+values from the Starboard platform, like:
+
+ * The `target_os` and `target_cpu`
+ * The `test_target_type`, and `final_executable_type` (which correspond to the
+   GYP variables `gtest_target_type` and `final_executable_type`)
+ * The host and target toolchain
+
+These values are obtained from the Starboard platform's `buildconfig.gni`
+file.
+
+## Generic Compiler Options
+
+In GYP, we have variables like `sb_pedantic_warnings` and keys like `rtti`,
+which, if set for a target, turn on or off additional compiler flags. In GN,
+these variables have been refactored into configs.
+
+Consult the following examples:
+
+### Pedantic Warnings
+
+GYP:
+
+    'variables': {
+      'sb_pedantic_warnings': 1
+    }
+
+GN:
+
+    configs -= [ "//starboard/build/config:no_pedantic_warnings" ]
+    configs += [ "//starboard/build/config:pedantic_warnings" ]
+
+### Optimizations
+
+GYP:
+
+    'optimizations': 'debuggable'
+
+GN:
+
+    configs -= [ "//starboard/build/config:default_optimizations" ]
+    configs += [ "//starboard/build/config:debuggable_optimizations" ]
+
+Ditto for `none` and `full`.
+
+### RTTI
+
+GYP:
+
+    'rtti': 1
+
+GN:
+
+    configs -= [ "//starboard/build/config:default_rtti" ]
+    configs += [ "//starboard/build/config:rtti" ]
+
+GYP:
+
+    'rtti': 0
+
+GN:
+
+    configs -= [ "//starboard/build/config:default_rtti" ]
+    configs += [ "//starboard/build/config:no_rtti" ]
+
+### -Wexit-time-destructors
+
+GYP:
+
+    'enable_wexit_time_destructors': 1
+
+GN:
+
+    configs += [ "//starboard/build/config:wexit_time_destructors" ]
+
+There is no `//starboard/build/config:no_wexit_time_destructors` config.
+
+### Implementation Details
+
+Each Starboard generic compiler config is implemented by referencing a
+platform-dependent implementation as a subconfig. For instance, here is, in
+essence, the code for `//starboard/build/config:pedantic_warnings`:
+
+    config("pedantic_warnings") {
+        configs = [ "//$starboard_path/config:pedantic_warnings" ]
+    }
+
+Each Starboard platform is then expected to define a pedantic warnings config
+(and a no-pedantic-warnings config) in its `BUILD.gn` file. For instance, the
+stub platform's implementation is:
+
+    config("pedantic_warnings") {
+      cflags = [
+        "-Wall",
+        "-Wextra",
+        "-Wunreachable-code",
+      ]
+    }
+
+Similarly, each Starboard platform is expected to define configs for turning on
+and off RTTI, or what the default RTTI state should be; configs for turning on
+and off optimizations, and the like. In reality, Starboard platforms often in
+turn delegate to configs implemented by toolchains (which stub does for e.g.
+RTTI).
+
+## Variables Renamed
+
+In general, following Chromium's practice, booleans have been renamed to have a
+`use_`, `enable_` or `is_` prefix, if they did not already have such a prefix.
+These prefixes come after any `cobalt_` or `sb_` prefix.
+
+Here is a partial table of some renames:
+
+GYP                         | GN
+--------------------------- | --------------------------
+`cobalt_fastbuild`          | `cobalt_use_fastbuild`
+`cobalt_version`            | `cobalt_build_id`
+`sb_allows_memory_tracking` | `sb_allow_memory_tracking`
+`target_arch`               | `target_cpu`
+
+
+## Table of Variables Refactored into Configs
+
+Variable                                              | Config
+----------------------------------------------------- | --------------------------------------------------------------
+`sb_pedantic_warnings`                                | `//starboard/build/config:{no_}pedantic_warnings`
+`compiler_flags`, `linker_flags`                      | `//$starboard_path:compiler_defaults`
+`compiler_flags_debug`, `compiler_flags_c_debug`, ... | `//$starboard_path:compiler_defaults_debug`
+`compiler_flags_devel`, `compiler_flags_c_devel`, ... | `//$starboard_path:compiler_defaults_devel`
+`compiler_flags_qa`, `compiler_flags_c_qa`, ...       | `//$starboard_path:compiler_defaults_qa`
+`compiler_flags_gold`, `compiler_flags_c_gold`, ...   | `//$starboard_path:compiler_defaults_gold`
+`compiler_flags_host`, `compiler_flags_c_host`, ...   | `//$starboard_path:compiler_defaults($host_toolchain)`
+`platform_libraries`                                  | `//$starboard_path:compiler_defaults` (in the `libs` variable)
+
+## gtest_target_type and friends
+
+The `gtest_target_type` variable has been renamed `test_target_type`. Platforms
+wishing to override the default value of this variable (e.g. Android) should
+put the override in `buildconfig.gni`. `final_executable_type` is similar.
+
+To use these types in targets, consult the following conversion table:
+
+GYP                                                  | GN
+---------------------------------------------------- | ---------------------------
+`'type': '<(gtest_target_type)', 'name': 'foo',`     | `test("foo") {`
+`'type': '<(final_executable_type)', 'name': 'foo',` | `final_executable("foo") {`
+
+## Deploy
+
+Replace
+
+    {
+      'target_name': 'target_deploy',
+      'type': 'none',
+      'dependencies': [
+        'target',
+      ],
+      'variables': {
+        'executable_name': 'target',
+      },
+      'includes': [ '../build/deploy.gypi' ],
+    },
+
+with
+
+    deploy("deploy") {
+      executable_name = "target"
+
+      deps = [
+        ":target",
+      ]
+    }
+
+and import `//starboard/build/deploy.gni` at the top of the file.
+
+## Runner Script
+
+The GYP build system has a runner script, `gyp_cobalt`, which does a ton of
+preprocessing, then runs GYP four times, once for each Cobalt configuration.
+
+GN does not use a runner script. The preprocessing that `gyp_cobalt` does is
+being moved into the actual GN build itself, making a runner script mostly
+unnecessary. Furthermore, a nontrivial runner script would potentially interfere
+with Ninja correctly rerunning GN when GN build files have changed.
+
+## Generic GN Advice for Build Args
+
+If you're adding a flag inside a `declare_args` block, read these tidbits of
+advice:
+
+ * Use boolean values when possible. If you need a default value that expands
+   to some complex thing in the default case (like the location of the
+   compiler which would be computed by a script), use a default value of -1 or
+   the empty string. Outside of the `declare_args` block, conditionally expand
+   the default value as necessary.
+
+ * Use a name like `use_foo` or `is_foo` (whatever is more appropriate for
+   your feature) rather than just `foo`.
+
+ * Write good comments directly above the declaration with no blank line.
+   These comments will appear as documentation in `gn args --list`.
+
+ * Don't call `exec_script` inside `declare_args`. This will execute the script
+   even if the value is overridden, which is wasteful. See first bullet.
diff --git a/src/cobalt/doc/resources/webapi_extension_example.jpg b/src/cobalt/doc/resources/webapi_extension_example.jpg
new file mode 100644
index 0000000..e9b43d1
--- /dev/null
+++ b/src/cobalt/doc/resources/webapi_extension_example.jpg
Binary files differ
diff --git a/src/cobalt/doc/webapi_extension.md b/src/cobalt/doc/webapi_extension.md
new file mode 100644
index 0000000..4173d3a
--- /dev/null
+++ b/src/cobalt/doc/webapi_extension.md
@@ -0,0 +1,99 @@
+# Cobalt Web Extension Support
+
+Cobalt provides a facility for extending the JavaScript Web API.  This allows
+custom web apps running on Cobalt to make calls through a custom API to
+C++ Cobalt code defined per Starboard platform.  This can allow for closer
+integration between the web app hosted by Cobalt and the system on which that
+web app is running.
+
+The Cobalt Web Extension support will allow you to attach an instance of a
+custom class to the JavaScript `window` global object so that it can be
+referenced from a web app in JavaScript as in the following example:
+
+```
+window.myInterface.RunMyFunction()
+```
+
+## Build-level modifications
+
+In order to extend the interface, one should add the following lines to the
+`variables` section of their platform's gyp_configuration.gypi file:
+
+1. `cobalt_webapi_extension_source_idl_files`  
+   This should be a list of [IDL files](https://en.wikipedia.org/wiki/Web_IDL)
+   that define the collection of new interfaces introduced by your extensions.
+   One of these new interfaces can be selected to be injected into the `window`
+   element (see 3. `cobalt_webapi_extension_gyp_target` for information on how
+   to do this).  Each IDL file listed here simultaneously defines a JavaScript
+   and a C++ interface.  For each IDL file, you will be expected to also provide
+   a header file in the same directory that re-declares (in C++) the interface
+   declared in the IDL file, as well as an implementation of all the methods
+   within it (either inline in the header file or in a corresponding source
+   file).
+2. `cobalt_webapi_extension_generated_header_idl_files`  
+   This is a list of all files that may result in automatic header file
+   generation that might be referenced from other C++ code.  An example of
+   this is the definition of `enum`s that may then be referenced as types in
+   a file from 1. `cobalt_webapi_extension_source_idl_files`.
+3. `cobalt_webapi_extension_gyp_target`  
+   This is the gyp target that will provide the IDL interface implementations,
+   as well as any necessary auxiliary code.  It will be added as a dependency of
+   [browser/cobalt.gyp:cobalt](../browser/cobalt.gyp).  It is expected that
+   this target will implement the interface defined in
+   [browser/webapi_extension.h](../browser/webapi_extension.h), which let you
+   name the injected window property, and provide a function to instantiate it
+   (i.e. to let you select which IDL object is the "entry point").
+
+The first two lists get included by
+[cobalt/browser/browser_bindings_gen.gyp](cobalt/browser/browser_bindings_gen.gyp),
+where you can look to see many examples of existing Cobalt IDL files that define
+the Web API available through Cobalt.  For each of these, you can also
+examine their corresponding `.h` files and in most cases their `.cc` files as
+well.
+
+An example configuration for these variables is available at
+[starboard/shared/test_webapi_extension/test_webapi_extension.gypi](../../starboard/shared/test_webapi_extension/test_webapi_extension.gypi), which
+contains the following variable definitions:
+
+```
+'cobalt_webapi_extension_source_idl_files': [
+  'my_new_interface.idl'
+],
+'cobalt_webapi_extension_generated_header_idl_files': [
+  'my_new_enum.idl'
+],
+'cobalt_webapi_extension_gyp_target':
+  '<(DEPTH)/starboard/shared/test_webapi_extension/webapi_extension.gyp:cobalt_test_webapi_extension',
+```
+
+## Implementing the [webapi_extension.h](../browser/webapi_extension.h) interface
+
+As discussed above in 3. `cobalt_webapi_extension_gyp_target`, you must provide
+an implementation of the two functions declared in 
+[browser/webapi_extension.h](../browser/webapi_extension.h).
+
+### `GetWebAPIExtensionObjectPropertyName()`
+You should implement `GetWebAPIExtensionObjectPropertyName()` to return the name
+of the injected `window` property.  For example, in the example from the
+beginning of this document, `window.myInterface.RunMyFunction()`, we would have
+the function return `std::string("myInterface")`.  If you return `nullopt` from
+this function, it is assumed that you do not wish to extend the web interface.
+
+Note that you should NOT name your `window` property the same as your class name
+as described in the IDL file, it will result in a name collision in the
+JavaScript environment.
+
+### `CreateWebAPIExtensionObject()`
+This function should instantiate and return the object to be accessed from
+`window`.  The object must be defined by an IDL file.
+
+## Debugging
+You may find the Cobalt debug console to be particularly useful for debugging
+IDL additions and changes.  In it, you can enter arbitrary JavaScript and then
+hit enter to execute it.  You can toggle it open by hitting either CTRL+O or
+F1, and you may have to hit the key twice to skip past the HUD mode.
+
+Here is an example of an example interface being exercised through the
+debug console:
+
+![Debug console web extension example](resources/webapi_extension_example.jpg)
\ No newline at end of file
diff --git a/src/cobalt/dom/buffer_source.cc b/src/cobalt/dom/buffer_source.cc
new file mode 100644
index 0000000..e94d6a1
--- /dev/null
+++ b/src/cobalt/dom/buffer_source.cc
@@ -0,0 +1,47 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/buffer_source.h"
+
+#include "base/logging.h"
+#include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/array_buffer_view.h"
+
+namespace cobalt {
+namespace dom {
+
+void GetBufferAndSize(const BufferSource& buffer_source, const uint8** buffer,
+                      int* buffer_size) {
+  DCHECK(buffer);
+  DCHECK(buffer_size);
+
+  if (buffer_source.IsType<scoped_refptr<ArrayBufferView> >()) {
+    scoped_refptr<ArrayBufferView> array_buffer_view =
+        buffer_source.AsType<scoped_refptr<ArrayBufferView> >();
+    *buffer = static_cast<const uint8*>(array_buffer_view->base_address());
+    *buffer_size = array_buffer_view->byte_length();
+  } else if (buffer_source.IsType<scoped_refptr<ArrayBuffer> >()) {
+    scoped_refptr<ArrayBuffer> array_buffer =
+        buffer_source.AsType<scoped_refptr<ArrayBuffer> >();
+    *buffer = array_buffer->data();
+    *buffer_size = array_buffer->byte_length();
+  } else {
+    NOTREACHED();
+    *buffer = NULL;
+    *buffer_size = 0;
+  }
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/buffer_source.h b/src/cobalt/dom/buffer_source.h
index a796675..cb8e6a5 100644
--- a/src/cobalt/dom/buffer_source.h
+++ b/src/cobalt/dom/buffer_source.h
@@ -26,6 +26,9 @@
 typedef script::UnionType2<scoped_refptr<ArrayBufferView>,
                            scoped_refptr<ArrayBuffer> > BufferSource;
 
+void GetBufferAndSize(const BufferSource& buffer_source, const uint8** buffer,
+                      int* buffer_size);
+
 }  // namespace dom
 }  // namespace cobalt
 
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index e516263..5711b9b 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -37,6 +37,7 @@
         'blob.cc',
         'blob.h',
         'blob_property_bag.h',
+        'buffer_source.cc',
         'buffer_source.h',
         'camera_3d.cc',
         'camera_3d.h',
@@ -292,6 +293,8 @@
             'eme/media_key_message_event.h',
             'eme/media_key_session.cc',
             'eme/media_key_session.h',
+            'eme/media_key_status_map.cc',
+            'eme/media_key_status_map.h',
             'eme/media_key_system_access.cc',
             'eme/media_key_system_access.h',
             'eme/media_keys.cc',
diff --git a/src/cobalt/dom/eme/media_key_session.cc b/src/cobalt/dom/eme/media_key_session.cc
index 5c62383..b2d8205 100644
--- a/src/cobalt/dom/eme/media_key_session.cc
+++ b/src/cobalt/dom/eme/media_key_session.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/dom/eme/media_key_session.h"
 
+#include <type_traits>
+
 #include "cobalt/dom/array_buffer.h"
 #include "cobalt/dom/array_buffer_view.h"
 #include "cobalt/dom/dom_exception.h"
@@ -32,15 +34,23 @@
     const scoped_refptr<media::DrmSystem>& drm_system,
     script::ScriptValueFactory* script_value_factory,
     const ClosedCallback& closed_callback)
-    : drm_system_(drm_system),
-      drm_system_session_(drm_system->CreateSession()),
+    : ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
+      drm_system_(drm_system),
+      drm_system_session_(drm_system->CreateSession(
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+          base::Bind(&MediaKeySession::OnSessionUpdateKeyStatuses,
+                     base::AsWeakPtr(this))
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+              )),
       script_value_factory_(script_value_factory),
       uninitialized_(true),
       callable_(false),
+      key_status_map_(new MediaKeyStatusMap),
       closed_callback_(closed_callback),
       ALLOW_THIS_IN_INITIALIZER_LIST(closed_promise_reference_(
           this, script_value_factory->CreateBasicPromise<void>())),
-      initiated_by_generate_request_(false) {}
+      initiated_by_generate_request_(false) {
+}
 
 // According to the step 3.1 of
 // https://www.w3.org/TR/encrypted-media/#dom-mediakeys-createsession,
@@ -55,6 +65,20 @@
   return &closed_promise_reference_.referenced_value();
 }
 
+const scoped_refptr<MediaKeyStatusMap>& MediaKeySession::key_statuses() const {
+  return key_status_map_;
+}
+
+const EventTarget::EventListenerScriptValue*
+MediaKeySession::onkeystatuseschange() const {
+  return GetAttributeEventListener(base::Tokens::keystatuseschange());
+}
+
+void MediaKeySession::set_onkeystatuseschange(
+    const EventListenerScriptValue& event_listener) {
+  SetAttributeEventListener(base::Tokens::keystatuseschange(), event_listener);
+}
+
 const EventTarget::EventListenerScriptValue* MediaKeySession::onmessage()
     const {
   return GetAttributeEventListener(base::Tokens::message());
@@ -65,27 +89,6 @@
   SetAttributeEventListener(base::Tokens::message(), event_listener);
 }
 
-namespace {
-
-void GetBufferAndSize(const BufferSource& buffer_source, const uint8** buffer,
-                      int* buffer_size) {
-  if (buffer_source.IsType<scoped_refptr<ArrayBufferView> >()) {
-    scoped_refptr<ArrayBufferView> array_buffer_view =
-        buffer_source.AsType<scoped_refptr<ArrayBufferView> >();
-    *buffer = static_cast<const uint8*>(array_buffer_view->base_address());
-    *buffer_size = array_buffer_view->byte_length();
-  } else if (buffer_source.IsType<scoped_refptr<ArrayBuffer> >()) {
-    scoped_refptr<ArrayBuffer> array_buffer =
-        buffer_source.AsType<scoped_refptr<ArrayBuffer> >();
-    *buffer = array_buffer->data();
-    *buffer_size = array_buffer->byte_length();
-  } else {
-    NOTREACHED();
-  }
-}
-
-}  // namespace
-
 // See
 // https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-generaterequest.
 scoped_ptr<MediaKeySession::VoidPromiseValue> MediaKeySession::GenerateRequest(
@@ -252,7 +255,7 @@
   //
   // TODO: Implement Event.isTrusted as per
   //       https://www.w3.org/TR/dom/#dom-event-istrusted and set it to true.
-  DispatchEvent(
+  event_queue_.Enqueue(
       new MediaKeyMessageEvent("message", media_key_message_event_init));
 
   // 10.5. Resolve promise.
@@ -306,6 +309,60 @@
   promise_reference->value().Reject(new DOMException(DOMException::kNone));
 }
 
+// See https://www.w3.org/TR/encrypted-media/#update-key-statuses.
+void MediaKeySession::OnSessionUpdateKeyStatuses(
+    const std::vector<std::string>& key_ids,
+    const std::vector<SbDrmKeyStatus>& key_statuses) {
+#define CHECK_KEY_STATUS_ENUM(starboard_value, dom_value)                  \
+  static_assert(static_cast<MediaKeyStatus>(starboard_value) == dom_value, \
+                "key status enum value mismatch");
+
+  CHECK_KEY_STATUS_ENUM(kSbDrmKeyStatusUsable, kMediaKeyStatusUsable);
+  CHECK_KEY_STATUS_ENUM(kSbDrmKeyStatusExpired, kMediaKeyStatusExpired);
+  CHECK_KEY_STATUS_ENUM(kSbDrmKeyStatusReleased, kMediaKeyStatusReleased);
+  CHECK_KEY_STATUS_ENUM(kSbDrmKeyStatusRestricted,
+                        kMediaKeyStatusOutputRestricted);
+  CHECK_KEY_STATUS_ENUM(kSbDrmKeyStatusDownscaled,
+                        kMediaKeyStatusOutputDownscaled);
+  CHECK_KEY_STATUS_ENUM(kSbDrmKeyStatusPending, kMediaKeyStatusStatusPending);
+  CHECK_KEY_STATUS_ENUM(kSbDrmKeyStatusError, kMediaKeyStatusInternalError);
+
+  DCHECK_EQ(key_ids.size(), key_statuses.size());
+
+  // 1. Let the session be the associated MediaKeySession object.
+  // 2. Let the input statuses be the sequence of pairs key ID and associated
+  //    MediaKeyStatus pairs.
+  // 3. Let the statuses be session's keyStatuses attribute.
+  // 4. Run the following steps to replace the contents of statuses:
+  // 4.1. Empty statuses.
+  key_status_map_->Clear();
+
+  // 4.2. For each pair in input statuses.
+  for (size_t i = 0; i < key_ids.size(); ++i) {
+    // 4.2.1. Let pair be the pair.
+    // 4.2.2. Insert an entry for pair's key ID into statuses with the value of
+    //        pair's MediaKeyStatus value.
+    DCHECK_GE(key_statuses[i], kSbDrmKeyStatusUsable);
+    DCHECK_LE(key_statuses[i], kSbDrmKeyStatusError);
+
+    if (key_statuses[i] < kSbDrmKeyStatusUsable ||
+        key_statuses[i] > kSbDrmKeyStatusError) {
+      key_status_map_->Add(key_ids[i], kMediaKeyStatusInternalError);
+    } else {
+      key_status_map_->Add(key_ids[i],
+                           static_cast<MediaKeyStatus>(key_statuses[i]));
+    }
+  }
+
+  // 5. Queue a task to fire a simple event named keystatuseschange at the
+  //    session.
+  event_queue_.Enqueue(new Event(base::Tokens::keystatuseschange()));
+
+  // 6. Queue a task to run the Attempt to Resume Playback If Necessary
+  //    algorithm on each of the media element(s) whose mediaKeys attribute is
+  //    the MediaKeys object that created the session.
+}
+
 // See https://www.w3.org/TR/encrypted-media/#session-closed.
 void MediaKeySession::OnClosed() {
   // 2. Run the Update Key Statuses algorithm on the session, providing an empty
diff --git a/src/cobalt/dom/eme/media_key_session.h b/src/cobalt/dom/eme/media_key_session.h
index cad6671..bda41aa 100644
--- a/src/cobalt/dom/eme/media_key_session.h
+++ b/src/cobalt/dom/eme/media_key_session.h
@@ -16,14 +16,19 @@
 #define COBALT_DOM_EME_MEDIA_KEY_SESSION_H_
 
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "cobalt/dom/buffer_source.h"
+#include "cobalt/dom/eme/media_key_status.h"
+#include "cobalt/dom/eme/media_key_status_map.h"
+#include "cobalt/dom/event_queue.h"
 #include "cobalt/dom/event_target.h"
 #include "cobalt/media/base/drm_system.h"
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value_factory.h"
+#include "starboard/drm.h"
 
 namespace cobalt {
 namespace dom {
@@ -47,6 +52,9 @@
   // Web IDL: MediaKeySession.
   std::string session_id() const;
   const VoidPromiseValue* closed() const;
+  const scoped_refptr<MediaKeyStatusMap>& key_statuses() const;
+  const EventListenerScriptValue* onkeystatuseschange() const;
+  void set_onkeystatuseschange(const EventListenerScriptValue& event_listener);
   const EventListenerScriptValue* onmessage() const;
   void set_onmessage(const EventListenerScriptValue& event_listener);
   scoped_ptr<VoidPromiseValue> GenerateRequest(
@@ -66,8 +74,13 @@
       VoidPromiseValue::Reference* promise_reference);
   void OnSessionUpdated(VoidPromiseValue::Reference* promise_reference);
   void OnSessionDidNotUpdate(VoidPromiseValue::Reference* promise_reference);
+  void OnSessionUpdateKeyStatuses(
+      const std::vector<std::string>& key_ids,
+      const std::vector<SbDrmKeyStatus>& key_statuses);
   void OnClosed();
 
+  EventQueue event_queue_;
+
   // Although it doesn't make much sense, it's possible to call session methods
   // when |MediaKeys| are destroyed. This behavior is underspecified but is
   // consistent with Chromium. For this reason we need to hold to |drm_system_|
@@ -77,6 +90,7 @@
   script::ScriptValueFactory* const script_value_factory_;
   bool uninitialized_;
   bool callable_;
+  scoped_refptr<MediaKeyStatusMap> key_status_map_;
 
   // TODO: Remove |closed_callback_| and change call sites to use closed()
   //       promise instead, once Cobalt switches to native SpiderMonkey
diff --git a/src/cobalt/dom/eme/media_key_session.idl b/src/cobalt/dom/eme/media_key_session.idl
index c458e27..3813c47 100644
--- a/src/cobalt/dom/eme/media_key_session.idl
+++ b/src/cobalt/dom/eme/media_key_session.idl
@@ -19,10 +19,9 @@
   // TODO: Implement |expiration|.
   // readonly attribute unrestricted double expiration;
   readonly attribute Promise<void> closed;
-  // TODO: Implement |keyStatuses|.
-  // readonly attribute MediaKeyStatusMap keyStatuses;
-  // TODO: Implement |onkeystatuseschange|.
-  // attribute EventHandler onkeystatuseschange;
+  [Conditional=COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE]
+  readonly attribute MediaKeyStatusMap keyStatuses;
+  attribute EventHandler onkeystatuseschange;
   attribute EventHandler onmessage;
   Promise<void> generateRequest(DOMString initDataType,
                                 BufferSource initData);
diff --git a/src/starboard/win/lib/atomic_public.h b/src/cobalt/dom/eme/media_key_status.idl
similarity index 73%
copy from src/starboard/win/lib/atomic_public.h
copy to src/cobalt/dom/eme/media_key_status.idl
index be4e805..37284a0 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/cobalt/dom/eme/media_key_status.idl
@@ -12,9 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+// https://www.w3.org/TR/encrypted-media/#idl-def-mediakeystatus
 
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+enum MediaKeyStatus {
+  "usable",
+  "expired",
+  "released",
+  "output-restricted",
+  "output-downscaled",
+  "status-pending",
+  "internal-error"
+};
diff --git a/src/cobalt/dom/eme/media_key_status_map.cc b/src/cobalt/dom/eme/media_key_status_map.cc
new file mode 100644
index 0000000..33e0bcc
--- /dev/null
+++ b/src/cobalt/dom/eme/media_key_status_map.cc
@@ -0,0 +1,71 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/eme/media_key_status_map.h"
+
+#include "base/logging.h"
+#include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/array_buffer_view.h"
+
+namespace cobalt {
+namespace dom {
+namespace eme {
+
+namespace {
+
+std::string ConvertKeyStatusToString(MediaKeyStatus key_status) {
+  switch (key_status) {
+    case kMediaKeyStatusUsable:
+      return "usable";
+    case kMediaKeyStatusExpired:
+      return "expired";
+    case kMediaKeyStatusReleased:
+      return "released";
+    case kMediaKeyStatusOutputRestricted:
+      return "output-restricted";
+    case kMediaKeyStatusOutputDownscaled:
+      return "output-downscaled";
+    case kMediaKeyStatusStatusPending:
+      return "status-pending";
+    case kMediaKeyStatusInternalError:
+      return "internal-error";
+    default:
+      break;
+  }
+
+  NOTREACHED();
+  return "internal-error";
+}
+
+BufferSource ConvertStringToBufferSource(const std::string& str) {
+  scoped_refptr<ArrayBuffer> array_buffer =
+      new ArrayBuffer(NULL, reinterpret_cast<const uint8*>(str.c_str()),
+                      static_cast<uint32>(str.size()));
+  return BufferSource(array_buffer);
+}
+
+}  // namespace
+
+void MediaKeyStatusMap::ForEach(const ForEachCallbackArg& callback) {
+  ForEachCallbackArg::Reference reference(this, callback);
+
+  for (auto& key_status : key_statuses_) {
+    reference.value().Run(ConvertKeyStatusToString(key_status.second),
+                          ConvertStringToBufferSource(key_status.first), this);
+  }
+}
+
+}  // namespace eme
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/eme/media_key_status_map.h b/src/cobalt/dom/eme/media_key_status_map.h
new file mode 100644
index 0000000..a2808c6
--- /dev/null
+++ b/src/cobalt/dom/eme/media_key_status_map.h
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_EME_MEDIA_KEY_STATUS_MAP_H_
+#define COBALT_DOM_EME_MEDIA_KEY_STATUS_MAP_H_
+
+#include <map>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/buffer_source.h"
+#include "cobalt/dom/eme/media_key_status.h"
+#include "cobalt/script/callback_function.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+namespace eme {
+
+// Represents a read-only map of key IDs to the current status of the associated
+// key.
+//   https://www.w3.org/TR/encrypted-media/#mediakeystatusmap-interface
+class MediaKeyStatusMap : public script::Wrappable {
+ public:
+  typedef script::CallbackFunction<void(
+      const std::string&, const BufferSource&,
+      const scoped_refptr<MediaKeyStatusMap>&)>
+      ForEachCallback;
+  typedef script::ScriptValue<ForEachCallback> ForEachCallbackArg;
+
+  // Custom, not in any spec.
+  //
+  MediaKeyStatusMap() {}
+
+  void Clear() { key_statuses_.clear(); }
+
+  void Add(const std::string& key_id, MediaKeyStatus key_status) {
+    key_statuses_[key_id] = key_status;
+  }
+
+  // Web IDL: MediaKeyStatusMap.
+  //
+  uint32_t size() const { return static_cast<uint32_t>(key_statuses_.size()); }
+  MediaKeyStatus Get(const BufferSource& key_id) const {
+    std::string key_id_copy = GetStringFromBufferSource(key_id);
+    const auto& iter = key_statuses_.find(key_id_copy);
+    // TODO: Return "undefined" if `key_id` cannot be found.
+    return iter == key_statuses_.end() ? kMediaKeyStatusInternalError
+                                       : iter->second;
+  }
+
+  bool Has(const BufferSource& key_id) const {
+    std::string key_id_copy = GetStringFromBufferSource(key_id);
+    return key_statuses_.find(key_id_copy) != key_statuses_.end();
+  }
+
+  void ForEach(const ForEachCallbackArg& callback);
+
+  DEFINE_WRAPPABLE_TYPE(MediaKeyStatusMap);
+
+ private:
+  static std::string GetStringFromBufferSource(
+      const BufferSource& buffer_source) {
+    const uint8* buffer;
+    int buffer_size;
+
+    GetBufferAndSize(buffer_source, &buffer, &buffer_size);
+
+    DCHECK(buffer);
+    DCHECK_GE(buffer_size, 0);
+    if (buffer && buffer_size > 0) {
+      return std::string(buffer, buffer + buffer_size);
+    }
+    return "";
+  }
+
+  std::map<std::string, MediaKeyStatus> key_statuses_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaKeyStatusMap);
+};
+
+}  // namespace eme
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_EME_MEDIA_KEY_STATUS_MAP_H_
diff --git a/src/cobalt/dom/eme/media_key_status_map.idl b/src/cobalt/dom/eme/media_key_status_map.idl
new file mode 100644
index 0000000..60b1279
--- /dev/null
+++ b/src/cobalt/dom/eme/media_key_status_map.idl
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/encrypted-media/#mediakeystatusmap-interface
+
+[Conditional=COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE]
+interface MediaKeyStatusMap {
+  iterable<BufferSource, MediaKeyStatus>;
+  readonly attribute unsigned long size;
+  boolean has(BufferSource keyId);
+  any get(BufferSource keyId);
+  void forEach(ForEachCallback callback);
+};
+
+callback ForEachCallback = void (DOMString status, BufferSource key, MediaKeyStatusMap map);
diff --git a/src/cobalt/dom/html_image_element.cc b/src/cobalt/dom/html_image_element.cc
index aedb344..4d8dfce 100644
--- a/src/cobalt/dom/html_image_element.cc
+++ b/src/cobalt/dom/html_image_element.cc
@@ -140,13 +140,13 @@
   // img element. Otherwise, queue a task to first fire a simple event named
   // error at the img element.
   PreventGarbageCollection();
+  node_document()->IncreaseLoadingCounter();
   cached_image_loaded_callback_handler_.reset(
       new loader::image::CachedImage::OnLoadedCallbackHandler(
           cached_image, base::Bind(&HTMLImageElement::OnLoadingSuccess,
                                    base::Unretained(this)),
           base::Bind(&HTMLImageElement::OnLoadingError,
                      base::Unretained(this))));
-  node_document()->IncreaseLoadingCounter();
 }
 
 void HTMLImageElement::OnLoadingSuccess() {
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc
index ac0f16f..8c09292 100644
--- a/src/cobalt/dom/html_link_element.cc
+++ b/src/cobalt/dom/html_link_element.cc
@@ -116,6 +116,13 @@
       &CspDelegate::CanLoad, base::Unretained(document->csp_delegate()),
       GetCspResourceTypeForRel(rel()));
 
+  if (IsRelContentCriticalResource(rel())) {
+    // The element must delay the load event of the element's document until all
+    // the attempts to obtain the resource and its critical subresources are
+    // complete.
+    document->IncreaseLoadingCounter();
+  }
+
   loader_ = make_scoped_ptr(new loader::Loader(
       base::Bind(
           &loader::FetcherFactory::CreateSecureFetcher,
@@ -124,13 +131,6 @@
       scoped_ptr<loader::Decoder>(new loader::TextDecoder(
           base::Bind(&HTMLLinkElement::OnLoadingDone, base::Unretained(this)))),
       base::Bind(&HTMLLinkElement::OnLoadingError, base::Unretained(this))));
-
-  if (IsRelContentCriticalResource(rel())) {
-    // The element must delay the load event of the element's document until all
-    // the attempts to obtain the resource and its critical subresources are
-    // complete.
-    document->IncreaseLoadingCounter();
-  }
 }
 
 void HTMLLinkElement::OnLoadingDone(const std::string& content) {
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index e140ed6..652c504 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -285,6 +285,12 @@
       std::deque<HTMLScriptElement*>* scripts_to_be_executed =
           document_->scripts_to_be_executed();
       scripts_to_be_executed->push_back(this);
+
+      // Fetching an external script must delay the load event of the element's
+      // document until the task that is queued by the networking task source
+      // once the resource has been fetched (defined above) has been run.
+      document_->IncreaseLoadingCounter();
+
       loader_.reset(new loader::Loader(
           base::Bind(
               &loader::FetcherFactory::CreateSecureFetcher,
@@ -294,11 +300,6 @@
               &HTMLScriptElement::OnLoadingDone, base::Unretained(this)))),
           base::Bind(&HTMLScriptElement::OnLoadingError,
                      base::Unretained(this))));
-
-      // Fetching an external script must delay the load event of the element's
-      // document until the task that is queued by the networking task source
-      // once the resource has been fetched (defined above) has been run.
-      document_->IncreaseLoadingCounter();
     } break;
     case 5: {
       // This is an asynchronous script. Prevent garbage collection until
@@ -307,6 +308,11 @@
 
       // If the element has a src attribute.
 
+      // Fetching an external script must delay the load event of the element's
+      // document until the task that is queued by the networking task source
+      // once the resource has been fetched (defined above) has been run.
+      document_->IncreaseLoadingCounter();
+
       // The element must be added to the set of scripts that will execute as
       // soon as possible of the Document of the script element at the time the
       // prepare a script algorithm started.
@@ -319,11 +325,6 @@
               &HTMLScriptElement::OnLoadingDone, base::Unretained(this)))),
           base::Bind(&HTMLScriptElement::OnLoadingError,
                      base::Unretained(this))));
-
-      // Fetching an external script must delay the load event of the element's
-      // document until the task that is queued by the networking task source
-      // once the resource has been fetched (defined above) has been run.
-      document_->IncreaseLoadingCounter();
     } break;
     case 6: {
       // Otherwise.
diff --git a/src/cobalt/dom/ui_event.h b/src/cobalt/dom/ui_event.h
index 46fb6b2..48655a1 100644
--- a/src/cobalt/dom/ui_event.h
+++ b/src/cobalt/dom/ui_event.h
@@ -17,7 +17,6 @@
 
 #include <string>
 
-#include "base/string_piece.h"
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/event.h"
 #include "cobalt/dom/ui_event_init.h"
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index a65f43f..1c3663f 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -142,6 +142,38 @@
   }
 }
 
+LayoutUnit Box::GetContainingBlockLeftOffset(bool stop_at_transform) const {
+  // If the box is absolutely positioned, then its containing block is formed by
+  // the padding box instead of the content box, as described in
+  // http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
+  // NOTE: While not explicitly stated in the spec, which specifies that the
+  // containing block of a 'fixed' position element must always be the viewport,
+  // all major browsers use the padding box of a transformed ancestor as the
+  // containing block for 'fixed' position elements.
+  return parent_ ? IsAbsolutelyPositioned()
+                       ? GetContainingBlock()->GetPaddingBoxLeftEdge(
+                             stop_at_transform)
+                       : GetContainingBlock()->GetContentBoxLeftEdge(
+                             stop_at_transform)
+                 : LayoutUnit();
+}
+
+LayoutUnit Box::GetContainingBlockTopOffset(bool stop_at_transform) const {
+  // If the box is absolutely positioned, then its containing block is formed by
+  // the padding box instead of the content box, as described in
+  // http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
+  // NOTE: While not explicitly stated in the spec, which specifies that the
+  // containing block of a 'fixed' position element must always be the viewport,
+  // all major browsers use the padding box of a transformed ancestor as the
+  // containing block for 'fixed' position elements.
+  return parent_ ? IsAbsolutelyPositioned()
+                       ? GetContainingBlock()->GetPaddingBoxTopEdge(
+                             stop_at_transform)
+                       : GetContainingBlock()->GetContentBoxTopEdge(
+                             stop_at_transform)
+                 : LayoutUnit();
+}
+
 void Box::SetStaticPositionLeftFromParent(LayoutUnit left) {
   if (left != static_position_offset_from_parent_.x()) {
     static_position_offset_from_parent_.set_x(left);
@@ -215,16 +247,20 @@
   return margin_top() + GetBorderBoxHeight() + margin_bottom();
 }
 
-LayoutUnit Box::GetMarginBoxLeftEdge() const {
-  LayoutUnit left_from_containing_block =
-      parent_ ? GetContainingBlock()->GetContentBoxLeftEdge() : LayoutUnit();
-  return left() + left_from_containing_block;
+LayoutUnit Box::GetMarginBoxLeftEdge(bool stop_at_transform) const {
+  LayoutUnit containing_block_left_offset =
+      (!stop_at_transform || !IsTransformed())
+          ? GetContainingBlockLeftOffset(stop_at_transform)
+          : LayoutUnit();
+  return containing_block_left_offset + left();
 }
 
-LayoutUnit Box::GetMarginBoxTopEdge() const {
-  LayoutUnit top_from_containing_block =
-      parent_ ? GetContainingBlock()->GetContentBoxTopEdge() : LayoutUnit();
-  return top() + top_from_containing_block;
+LayoutUnit Box::GetMarginBoxTopEdge(bool stop_at_transform) const {
+  LayoutUnit containing_block_top_offset =
+      (!stop_at_transform || !IsTransformed())
+          ? GetContainingBlockTopOffset(stop_at_transform)
+          : LayoutUnit();
+  return containing_block_top_offset + top();
 }
 
 LayoutUnit Box::GetMarginBoxRightEdgeOffsetFromContainingBlock() const {
@@ -257,8 +293,9 @@
   return border_top_width() + GetPaddingBoxHeight() + border_bottom_width();
 }
 
-RectLayoutUnit Box::GetBorderBox() const {
-  return RectLayoutUnit(GetBorderBoxLeftEdge(), GetBorderBoxTopEdge(),
+RectLayoutUnit Box::GetBorderBox(bool stop_at_transform) const {
+  return RectLayoutUnit(GetBorderBoxLeftEdge(stop_at_transform),
+                        GetBorderBoxTopEdge(stop_at_transform),
                         GetBorderBoxWidth(), GetBorderBoxHeight());
 }
 
@@ -266,12 +303,12 @@
   return SizeLayoutUnit(GetBorderBoxWidth(), GetBorderBoxHeight());
 }
 
-LayoutUnit Box::GetBorderBoxLeftEdge() const {
-  return GetMarginBoxLeftEdge() + margin_left();
+LayoutUnit Box::GetBorderBoxLeftEdge(bool stop_at_transform) const {
+  return GetMarginBoxLeftEdge(stop_at_transform) + margin_left();
 }
 
-LayoutUnit Box::GetBorderBoxTopEdge() const {
-  return GetMarginBoxTopEdge() + margin_top();
+LayoutUnit Box::GetBorderBoxTopEdge(bool stop_at_transform) const {
+  return GetMarginBoxTopEdge(stop_at_transform) + margin_top();
 }
 
 LayoutUnit Box::GetPaddingBoxWidth() const {
@@ -286,12 +323,12 @@
   return SizeLayoutUnit(GetPaddingBoxWidth(), GetPaddingBoxHeight());
 }
 
-LayoutUnit Box::GetPaddingBoxLeftEdge() const {
-  return GetBorderBoxLeftEdge() + border_left_width();
+LayoutUnit Box::GetPaddingBoxLeftEdge(bool stop_at_transform) const {
+  return GetBorderBoxLeftEdge(stop_at_transform) + border_left_width();
 }
 
-LayoutUnit Box::GetPaddingBoxTopEdge() const {
-  return GetBorderBoxTopEdge() + border_top_width();
+LayoutUnit Box::GetPaddingBoxTopEdge(bool stop_at_transform) const {
+  return GetBorderBoxTopEdge(stop_at_transform) + border_top_width();
 }
 
 Vector2dLayoutUnit Box::GetContentBoxOffsetFromMarginBox() const {
@@ -333,12 +370,12 @@
   return Vector2dLayoutUnit(padding_left(), padding_top());
 }
 
-LayoutUnit Box::GetContentBoxLeftEdge() const {
-  return GetPaddingBoxLeftEdge() + padding_left();
+LayoutUnit Box::GetContentBoxLeftEdge(bool stop_at_transform) const {
+  return GetPaddingBoxLeftEdge(stop_at_transform) + padding_left();
 }
 
-LayoutUnit Box::GetContentBoxTopEdge() const {
-  return GetPaddingBoxTopEdge() + padding_top();
+LayoutUnit Box::GetContentBoxTopEdge(bool stop_at_transform) const {
+  return GetPaddingBoxTopEdge(stop_at_transform) + padding_top();
 }
 
 LayoutUnit Box::GetInlineLevelBoxHeight() const { return GetMarginBoxHeight(); }
@@ -582,10 +619,10 @@
   }
 }
 
-Box::RenderSequence Box::GetRenderSequence() {
+Box::RenderSequence Box::GetRenderSequence() const {
   std::vector<size_t> render_sequence;
-  Box* ancestor_box = this;
-  Box* box = NULL;
+  const Box* ancestor_box = this;
+  const Box* box = NULL;
   while (ancestor_box && (box != ancestor_box)) {
     box = ancestor_box;
     if (box->cached_render_tree_node_info_) {
@@ -660,7 +697,8 @@
 
   if (rounded_corners) {
     rect_node_builder->rounded_corners =
-        scoped_ptr<RoundedCorners>(new RoundedCorners(*rounded_corners));
+        scoped_ptr<RoundedCorners>(new RoundedCorners(
+            rounded_corners->Normalize(rect_node_builder->rect)));
   }
 }
 
@@ -749,7 +787,8 @@
 
   if (rounded_corners) {
     rect_node_builder->rounded_corners =
-        scoped_ptr<RoundedCorners>(new RoundedCorners(*rounded_corners));
+        scoped_ptr<RoundedCorners>(new RoundedCorners(
+            rounded_corners->Normalize(rect_node_builder->rect)));
   }
 }
 
@@ -883,7 +922,7 @@
 }
 
 bool Box::IsUnderCoordinate(const Vector2dLayoutUnit& coordinate) const {
-  RectLayoutUnit rect = GetBorderBox();
+  RectLayoutUnit rect = GetBorderBox(true /*stop_at_transform*/);
   bool res =
       coordinate.x() >= rect.x() && coordinate.x() <= rect.x() + rect.width() &&
       coordinate.y() >= rect.y() && coordinate.y() <= rect.y() + rect.height();
@@ -1192,7 +1231,10 @@
       render_tree::RectShadowNode::Builder shadow_builder(
           math::RectF(rect_offset, shadow_rect_size), shadow,
           shadow_value->has_inset(), spread_radius);
-      shadow_builder.rounded_corners = rounded_corners;
+      if (rounded_corners) {
+        shadow_builder.rounded_corners = rounded_corners->Normalize(
+            shadow_builder.rect);
+      }
 
       // Finally, create our shadow node.
       scoped_refptr<render_tree::RectShadowNode> shadow_node(
@@ -1346,7 +1388,8 @@
         // Apply rounded viewport filter to the background image.
         FilterNode::Builder filter_node_builder(background_node);
         filter_node_builder.viewport_filter =
-            ViewportFilter(image_frame, *rounded_corners);
+            ViewportFilter(image_frame,
+                           rounded_corners->Normalize(image_frame));
         background_node = new FilterNode(filter_node_builder);
       }
 
@@ -1448,7 +1491,9 @@
                   border_node_offset.y() + border_top_width().toFloat(),
                   padding_size.width(), padding_size.height()));
   if (rounded_corners) {
-    filter_node_builder.viewport_filter->set_rounded_corners(*rounded_corners);
+    filter_node_builder.viewport_filter->set_rounded_corners(
+        rounded_corners->Normalize(
+            filter_node_builder.viewport_filter->viewport()));
   }
 
   return scoped_refptr<render_tree::Node>(new FilterNode(filter_node_builder));
@@ -1552,20 +1597,32 @@
   const scoped_refptr<cssom::PropertyValue>& transform =
       computed_style()->transform();
   if (transform != cssom::KeywordValue::GetNone()) {
+    LayoutUnit containing_block_left_offset =
+        GetContainingBlockLeftOffset(true /*stop_at_transform*/);
+    LayoutUnit containing_block_top_offset =
+        GetContainingBlockTopOffset(true /*stop_at_transform*/);
+
     math::Vector2dF border_box_offset(
-        left().toFloat() + margin_left().toFloat(),
-        top().toFloat() + margin_top().toFloat());
+        (containing_block_left_offset + left() + margin_left()).toFloat(),
+        (containing_block_top_offset + top() + margin_top()).toFloat());
+
     math::RectF rect = math::RectF(PointAtOffsetFromOrigin(border_box_offset),
                                    GetBorderBoxSize());
     math::Matrix3F matrix =
         GetCSSTransform(transform, computed_style()->transform_origin(), rect);
     if (!matrix.IsIdentity()) {
-      // transform the coordinate.
+      // Transform the coordinate.
       math::PointF transformed_point =
           matrix.Inverse() * math::PointF(coordinate->x(), coordinate->y());
       coordinate->set_x(transformed_point.x());
       coordinate->set_y(transformed_point.y());
     }
+
+    // The transformed box forms a new coordinate system and its containing
+    // block's offset is considered (0,0) within it. Convert the coordinate to
+    // the new system.
+    *coordinate -= math::Vector2dF(containing_block_left_offset.toFloat(),
+                                   containing_block_top_offset.toFloat());
   }
 }
 
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index ddc61a0..7686c84 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -213,6 +213,14 @@
   // out-of-flow descendants. Does not update the position of the box.
   void UpdateSize(const LayoutParams& layout_params);
 
+  // Returns the left offset from root (or a transform if |stop_at_transform| is
+  // true) to this box's containing block.
+  LayoutUnit GetContainingBlockLeftOffset(bool stop_at_transform) const;
+
+  // Returns the top offset from root (or a transform if |stop_at_transform| is
+  // true) to this box's containing block.
+  LayoutUnit GetContainingBlockTopOffset(bool stop_at_transform) const;
+
   // Used values of "left" and "top" are publicly readable and writable so that
   // they can be calculated and adjusted by the formatting context of
   // the parent box.
@@ -267,8 +275,8 @@
   const Vector2dLayoutUnit& margin_box_offset_from_containing_block() const {
     return margin_box_offset_from_containing_block_;
   }
-  LayoutUnit GetMarginBoxLeftEdge() const;
-  LayoutUnit GetMarginBoxTopEdge() const;
+  LayoutUnit GetMarginBoxLeftEdge(bool stop_at_transform) const;
+  LayoutUnit GetMarginBoxTopEdge(bool stop_at_transform) const;
   LayoutUnit GetMarginBoxRightEdgeOffsetFromContainingBlock() const;
   LayoutUnit GetMarginBoxBottomEdgeOffsetFromContainingBlock() const;
   LayoutUnit GetMarginBoxStartEdgeOffsetFromContainingBlock(
@@ -283,17 +291,17 @@
   // Border box.
   LayoutUnit GetBorderBoxWidth() const;
   LayoutUnit GetBorderBoxHeight() const;
-  RectLayoutUnit GetBorderBox() const;
+  RectLayoutUnit GetBorderBox(bool stop_at_transform) const;
   SizeLayoutUnit GetBorderBoxSize() const;
-  LayoutUnit GetBorderBoxLeftEdge() const;
-  LayoutUnit GetBorderBoxTopEdge() const;
+  LayoutUnit GetBorderBoxLeftEdge(bool stop_at_transform) const;
+  LayoutUnit GetBorderBoxTopEdge(bool stop_at_transform) const;
 
   // Padding box.
   LayoutUnit GetPaddingBoxWidth() const;
   LayoutUnit GetPaddingBoxHeight() const;
   SizeLayoutUnit GetPaddingBoxSize() const;
-  LayoutUnit GetPaddingBoxLeftEdge() const;
-  LayoutUnit GetPaddingBoxTopEdge() const;
+  LayoutUnit GetPaddingBoxLeftEdge(bool stop_at_transform) const;
+  LayoutUnit GetPaddingBoxTopEdge(bool stop_at_transform) const;
 
   // Content box.
   LayoutUnit width() const { return content_size_.width(); }
@@ -309,8 +317,8 @@
       BaseDirection base_direction) const;
   LayoutUnit GetContentBoxEndEdgeOffsetFromContainingBlock(
       BaseDirection base_direction) const;
-  LayoutUnit GetContentBoxLeftEdge() const;
-  LayoutUnit GetContentBoxTopEdge() const;
+  LayoutUnit GetContentBoxLeftEdge(bool stop_at_transform) const;
+  LayoutUnit GetContentBoxTopEdge(bool stop_at_transform) const;
 
   // The height of each inline-level box in the line box is calculated. For
   // replaced elements, inline-block elements, and inline-table elements, this
@@ -533,7 +541,7 @@
   bool IsUnderCoordinate(const Vector2dLayoutUnit& coordinate) const;
 
   // Returns a data structure that can be used by Box::IsRenderedLater().
-  RenderSequence GetRenderSequence();
+  RenderSequence GetRenderSequence() const;
 
   // Returns true if the box for the given render_sequence is rendered after
   // the box for the other_render_sequence. The boxes must be from the same
diff --git a/src/cobalt/layout/layout_boxes.cc b/src/cobalt/layout/layout_boxes.cc
index dc1b5ba..6068bac 100644
--- a/src/cobalt/layout/layout_boxes.cc
+++ b/src/cobalt/layout/layout_boxes.cc
@@ -61,8 +61,10 @@
       // boxes. Our current clients don't currently rely on GetClientRects() to
       // do that.
 
-      dom_rect->set_x(box->GetBorderBoxLeftEdge().toFloat());
-      dom_rect->set_y(box->GetBorderBoxTopEdge().toFloat());
+      dom_rect->set_x(
+          box->GetBorderBoxLeftEdge(false /*stop_at_transform*/).toFloat());
+      dom_rect->set_y(
+          box->GetBorderBoxTopEdge(false /*stop_at_transform*/).toFloat());
       SizeLayoutUnit box_size = box->GetBorderBoxSize();
       dom_rect->set_width(box_size.width().toFloat());
       dom_rect->set_height(box_size.height().toFloat());
@@ -119,12 +121,16 @@
 
 float LayoutBoxes::GetPaddingEdgeLeft() const {
   DCHECK(!boxes_.empty());
-  return boxes_.front()->GetPaddingBoxLeftEdge().toFloat();
+  return boxes_.front()
+      ->GetPaddingBoxLeftEdge(false /*stop_at_transform*/)
+      .toFloat();
 }
 
 float LayoutBoxes::GetPaddingEdgeTop() const {
   DCHECK(!boxes_.empty());
-  return boxes_.front()->GetPaddingBoxTopEdge().toFloat();
+  return boxes_.front()
+      ->GetPaddingBoxTopEdge(false /*stop_at_transform*/)
+      .toFloat();
 }
 
 float LayoutBoxes::GetPaddingEdgeWidth() const {
@@ -187,7 +193,7 @@
        box_iterator != boxes_.end(); ++box_iterator) {
     Box* box = *box_iterator;
     do {
-      bounding_rectangle.Union(box->GetBorderBox());
+      bounding_rectangle.Union(box->GetBorderBox(false /*stop_at_transform*/));
       box = box->GetSplitSibling();
     } while (box != NULL);
   }
diff --git a/src/cobalt/layout/topmost_event_target.cc b/src/cobalt/layout/topmost_event_target.cc
index a74dfaf..83cd9cc 100644
--- a/src/cobalt/layout/topmost_event_target.cc
+++ b/src/cobalt/layout/topmost_event_target.cc
@@ -74,15 +74,6 @@
       if (!boxes.empty()) {
         const Box* box = boxes.front();
         box->UpdateCoordinateForTransform(&element_coordinate);
-
-        if (box->computed_style()->position() ==
-            cssom::KeywordValue::GetAbsolute()) {
-          // The containing block for position:absolute elements is formed by
-          // the padding box instead of the content box, as described in
-          // http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
-          element_coordinate +=
-              box->GetContainingBlock()->GetContentBoxOffsetFromPaddingBox();
-        }
         ConsiderBoxes(html_element, layout_boxes, element_coordinate);
       }
     }
@@ -105,15 +96,18 @@
                                        LayoutUnit(coordinate.y()));
   for (Boxes::const_iterator box_iterator = boxes.begin();
        box_iterator != boxes.end(); ++box_iterator) {
-    const scoped_refptr<Box>& box = *box_iterator;
-    if (box->IsUnderCoordinate(layout_coordinate)) {
-      Box::RenderSequence render_sequence = box->GetRenderSequence();
-      if (Box::IsRenderedLater(render_sequence, render_sequence_)) {
-        html_element_ = html_element;
-        box_ = box;
-        render_sequence_.swap(render_sequence);
+    Box* box = *box_iterator;
+    do {
+      if (box->IsUnderCoordinate(layout_coordinate)) {
+        Box::RenderSequence render_sequence = box->GetRenderSequence();
+        if (Box::IsRenderedLater(render_sequence, render_sequence_)) {
+          html_element_ = html_element;
+          box_ = box;
+          render_sequence_.swap(render_sequence);
+        }
       }
-    }
+      box = box->GetSplitSibling();
+    } while (box != NULL);
   }
 }
 
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index 100b919..0df113a 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -50,7 +50,6 @@
       frame_count_(0),
       loop_count_(kLoopInfinite),
       current_frame_index_(0),
-      next_frame_index_(0),
       should_dispose_previous_frame_to_background_(false),
       resource_provider_(resource_provider),
       frame_provider_(new FrameProvider()) {
@@ -58,7 +57,7 @@
                "AnimatedWebPImage::AnimatedWebPImage()");
 }
 
-scoped_refptr<const AnimatedImage::FrameProvider>
+scoped_refptr<AnimatedImage::FrameProvider>
 AnimatedWebPImage::GetFrameProvider() {
   TRACE_EVENT0("cobalt::loader::image",
                "AnimatedWebPImage::GetFrameProvider()");
@@ -110,14 +109,14 @@
     received_first_frame_ = true;
     loop_count_ = WebPDemuxGetI(demux_, WEBP_FF_LOOP_COUNT);
 
-    // The default background color of the canvas in [Blue, Green, Red, Alpha]
-    // byte order. It is read in little endian order as an 32bit int.
+    // The default background color of the canvas in [Blue, Green, Red, Alpha],
+    // from most significant byte to least significant byte.
     uint32_t background_color = WebPDemuxGetI(demux_, WEBP_FF_BACKGROUND_COLOR);
     background_color_ =
-        render_tree::ColorRGBA((background_color >> 16 & 0xff) / 255.0f,
-                               (background_color >> 8 & 0xff) / 255.0f,
-                               (background_color & 0xff) / 255.0f,
-                               (background_color >> 24 & 0xff) / 255.0f);
+        render_tree::ColorRGBA((background_color >> 8 & 0xff) / 255.0f,
+                               (background_color >> 16 & 0xff) / 255.0f,
+                               (background_color >> 24 & 0xff) / 255.0f,
+                               (background_color >> 0 & 0xff) / 255.0f);
 
     if (is_playing_) {
       PlayInternal();
@@ -172,25 +171,25 @@
         base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
   }
 
-  UpdateTimelineInfo();
-
-  // Decode the frames from current frame to next frame and blend the results.
-  for (int frame_index = current_frame_index_ + 1;
-       frame_index <= next_frame_index_; ++frame_index) {
-    if (!DecodeOneFrame(frame_index)) {
-      break;
-    }
+  if (AdvanceFrame()) {
+    // Decode the frames from current frame to next frame and blend the results.
+    DecodeOneFrame(current_frame_index_);
   }
-  current_frame_index_ = next_frame_index_;
 
   // Set up the next time to call the decode callback.
   if (is_playing_) {
-    base::TimeDelta delay = next_frame_time_ - base::TimeTicks::Now();
     const base::TimeDelta min_delay =
         base::TimeDelta::FromMilliseconds(kMinimumDelayInMilliseconds);
-    if (delay < min_delay) {
+    base::TimeDelta delay;
+    if (next_frame_time_) {
+      delay = *next_frame_time_ - base::TimeTicks::Now();
+      if (delay < min_delay) {
+        delay = min_delay;
+      }
+    } else {
       delay = min_delay;
     }
+
     message_loop_->PostDelayedTask(FROM_HERE, decode_closure_.callback(),
                                    delay);
   }
@@ -288,44 +287,78 @@
   return true;
 }
 
-void AnimatedWebPImage::UpdateTimelineInfo() {
-  TRACE_EVENT0("cobalt::loader::image",
-               "AnimatedWebPImage::UpdateTimelineInfo()");
+bool AnimatedWebPImage::AdvanceFrame() {
+  TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::AdvanceFrame()");
   TRACK_MEMORY_SCOPE("Rendering");
   DCHECK(message_loop_->BelongsToCurrentThread());
   lock_.AssertAcquired();
 
   base::TimeTicks current_time = base::TimeTicks::Now();
-  next_frame_index_ = current_frame_index_ ? current_frame_index_ : 1;
-  while (true) {
-    // Decode frames, until a frame such that the duration covers the current
-    // time, i.e. the next frame should be displayed in the future.
-    WebPIterator webp_iterator;
-    WebPDemuxGetFrame(demux_, next_frame_index_, &webp_iterator);
-    next_frame_time_ = current_frame_time_ + base::TimeDelta::FromMilliseconds(
-                                                 webp_iterator.duration);
-    WebPDemuxReleaseIterator(&webp_iterator);
-    if (current_time < next_frame_time_) {
-      break;
+
+  // If the WebP image hasn't been fully fetched, then stop on the current
+  // frame.
+  if (demux_state_ == WEBP_DEMUX_PARSED_HEADER) {
+    return false;
+  }
+
+  // If we're done playing the animation, do nothing.
+  if (LoopingFinished()) {
+    return false;
+  }
+
+  // If it's still not time to advance to the next frame, do nothing.
+  if (next_frame_time_ && current_time < *next_frame_time_) {
+    return false;
+  }
+
+  // Always wait for a consumer to consume the previous frame before moving
+  // forward with decoding the next frame.
+  if (!frame_provider_->FrameConsumed()) {
+    return false;
+  }
+
+  if (next_frame_time_) {
+    current_frame_time_ = *next_frame_time_;
+  } else {
+    current_frame_time_ = current_time;
+  }
+
+  ++current_frame_index_;
+  if (current_frame_index_ == frame_count_) {
+    // Check if we have finished looping, and if so return indicating that there
+    // is no additional frame available.
+    if (LoopingFinished()) {
+      next_frame_time_ = base::nullopt;
+      return false;
     }
 
-    current_frame_time_ = next_frame_time_;
-    if (next_frame_index_ < frame_count_) {
-      next_frame_index_++;
-    } else {
-      DCHECK_EQ(next_frame_index_, frame_count_);
-      // If the WebP image hasn't been fully fetched, or we've reached the end
-      // of the last loop, then stop on the current frame.
-      if (demux_state_ == WEBP_DEMUX_PARSED_HEADER || loop_count_ == 1) {
-        break;
-      }
-      next_frame_index_ = 1;
-      current_frame_index_ = 0;
-      if (loop_count_ != kLoopInfinite) {
-        loop_count_--;
-      }
+    // Loop around to the beginning
+    current_frame_index_ = 0;
+    if (loop_count_ != kLoopInfinite) {
+      loop_count_--;
     }
   }
+
+  // Update the time in the future at which point we should switch to the
+  // frame after the new current frame.
+  next_frame_time_ =
+      current_frame_time_ + GetFrameDuration(current_frame_index_);
+  if (next_frame_time_ < current_time) {
+    // Don't let the animation fall back for more than a frame.
+    next_frame_time_ = current_time;
+  }
+
+  return true;
+}
+
+base::TimeDelta AnimatedWebPImage::GetFrameDuration(int frame_index) {
+  lock_.AssertAcquired();
+  WebPIterator webp_iterator;
+  WebPDemuxGetFrame(demux_, frame_index, &webp_iterator);
+  base::TimeDelta frame_duration =
+      base::TimeDelta::FromMilliseconds(webp_iterator.duration);
+  WebPDemuxReleaseIterator(&webp_iterator);
+  return frame_duration;
 }
 
 scoped_ptr<render_tree::ImageData> AnimatedWebPImage::AllocateImageData(
@@ -340,6 +373,10 @@
   return image_data.Pass();
 }
 
+bool AnimatedWebPImage::LoopingFinished() const {
+  return loop_count_ == 1 && current_frame_index_ == frame_count_;
+}
+
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index 179d42e..8714c46 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -45,18 +45,21 @@
   const math::Size& GetSize() const OVERRIDE { return size_; }
 
   uint32 GetEstimatedSizeInBytes() const OVERRIDE {
-    return static_cast<uint32>(data_buffer_.size());
+    // Return the size of 2 frames of images, since we can have two frames in
+    // memory at a time (the previous decode image passed to the frame provider
+    // and the next frame that is composed from the previous frame).
+    return size_.GetArea() * 4 * 2 + static_cast<uint32>(data_buffer_.size());
   }
 
   bool IsOpaque() const OVERRIDE { return is_opaque_; }
 
-  scoped_refptr<const FrameProvider> GetFrameProvider() OVERRIDE;
+  scoped_refptr<FrameProvider> GetFrameProvider() OVERRIDE;
 
   void Play(const scoped_refptr<base::MessageLoopProxy>& message_loop) OVERRIDE;
 
   void Stop() OVERRIDE;
 
-  void AppendChunk(const uint8* data, size_t size);
+  void AppendChunk(const uint8* data, size_t input_byte);
 
  private:
   ~AnimatedWebPImage() OVERRIDE;
@@ -72,11 +75,17 @@
   // Decodes the frame with the given index, returns if it succeeded.
   bool DecodeOneFrame(int frame_index);
 
-  // Updates the index and time info of the current and next frames.
-  void UpdateTimelineInfo();
-
   scoped_ptr<render_tree::ImageData> AllocateImageData(const math::Size& size);
 
+  // If the time is right, updates the index and time info of the current frame.
+  bool AdvanceFrame();
+
+  // Returns the duration of the given frame index.
+  base::TimeDelta GetFrameDuration(int frame_index);
+
+  // Returns true if the animation loop is finished.
+  bool LoopingFinished() const;
+
   const math::Size size_;
   const bool is_opaque_;
   const render_tree::PixelFormat pixel_format_;
@@ -89,7 +98,6 @@
   // looping infinitely.
   int loop_count_;
   int current_frame_index_;
-  int next_frame_index_;
   bool should_dispose_previous_frame_to_background_;
   render_tree::ResourceProvider* resource_provider_;
   scoped_refptr<base::MessageLoopProxy> message_loop_;
@@ -98,7 +106,7 @@
   math::RectF previous_frame_rect_;
   base::CancelableClosure decode_closure_;
   base::TimeTicks current_frame_time_;
-  base::TimeTicks next_frame_time_;
+  base::optional<base::TimeTicks> next_frame_time_;
   // The original encoded data.
   std::vector<uint8> data_buffer_;
   scoped_refptr<render_tree::Image> current_canvas_;
diff --git a/src/cobalt/loader/image/image.h b/src/cobalt/loader/image/image.h
index fe77dac..2b72010 100644
--- a/src/cobalt/loader/image/image.h
+++ b/src/cobalt/loader/image/image.h
@@ -88,13 +88,22 @@
   // that was in there.
   class FrameProvider : public base::RefCountedThreadSafe<FrameProvider> {
    public:
+    FrameProvider() : frame_consumed_(true) {}
+
     void SetFrame(const scoped_refptr<render_tree::Image>& frame) {
       base::AutoLock lock(mutex_);
       frame_ = frame;
+      frame_consumed_ = false;
     }
 
-    scoped_refptr<render_tree::Image> GetFrame() const {
+    bool FrameConsumed() const {
       base::AutoLock lock(mutex_);
+      return frame_consumed_;
+    }
+
+    scoped_refptr<render_tree::Image> GetFrame() {
+      base::AutoLock lock(mutex_);
+      frame_consumed_ = true;
       return frame_;
     }
 
@@ -103,6 +112,9 @@
     friend class base::RefCountedThreadSafe<FrameProvider>;
 
     mutable base::Lock mutex_;
+    // True if a call to FrameConsumed() has been made after the last call to
+    // SetFrame().
+    bool frame_consumed_;
     scoped_refptr<render_tree::Image> frame_;
   };
 
@@ -121,11 +133,11 @@
   // Returns a FrameProvider object from which frames can be pulled out of.
   // The AnimatedImage object is expected to push frames into the FrameProvider
   // as it generates them.
-  virtual scoped_refptr<const FrameProvider> GetFrameProvider() = 0;
+  virtual scoped_refptr<FrameProvider> GetFrameProvider() = 0;
 
   // This callback is intended to be used in a render_tree::AnimateNode.
   static void AnimateCallback(
-      scoped_refptr<const FrameProvider> frame_provider,
+      scoped_refptr<FrameProvider> frame_provider,
       const math::RectF& destination_rect,
       const math::Matrix3F& local_transform,
       render_tree::ImageNode::Builder* image_node_builder,
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc
index 7f995fc..fda87ed 100644
--- a/src/cobalt/loader/image/image_decoder_test.cc
+++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -674,7 +674,7 @@
   thread.Stop();
 
   // The image should contain the whole undecoded data from the file.
-  EXPECT_EQ(3224674u, animated_webp_image->GetEstimatedSizeInBytes());
+  EXPECT_EQ(4261474u, animated_webp_image->GetEstimatedSizeInBytes());
 
   EXPECT_EQ(math::Size(480, 270), animated_webp_image->GetSize());
   EXPECT_TRUE(animated_webp_image->IsOpaque());
@@ -705,7 +705,7 @@
   thread.Stop();
 
   // The image should contain the whole undecoded data from the file.
-  EXPECT_EQ(3224674u, animated_webp_image->GetEstimatedSizeInBytes());
+  EXPECT_EQ(4261474u, animated_webp_image->GetEstimatedSizeInBytes());
 
   EXPECT_EQ(math::Size(480, 270), animated_webp_image->GetSize());
   EXPECT_TRUE(animated_webp_image->IsOpaque());
diff --git a/src/cobalt/loader/image/webp_image_decoder.cc b/src/cobalt/loader/image/webp_image_decoder.cc
index 74afb79..90831fc 100644
--- a/src/cobalt/loader/image/webp_image_decoder.cc
+++ b/src/cobalt/loader/image/webp_image_decoder.cc
@@ -90,8 +90,20 @@
       if (status == VP8_STATUS_OK) {
         DCHECK(image_data());
         DCHECK(config_.output.u.RGBA.rgba);
-        SbMemoryCopy(image_data()->GetMemory(), config_.output.u.RGBA.rgba,
-                     config_.output.u.RGBA.size);
+
+        // Copy the image data over line by line.  We copy line by line instead
+        // of all at once so that we can adjust for differences in pitch between
+        // source and destination buffers.
+        uint8* cur_src = config_.output.u.RGBA.rgba;
+        uint8* cur_dest = image_data()->GetMemory();
+        int height = image_data()->GetDescriptor().size.height();
+        int num_pixel_bytes = image_data()->GetDescriptor().size.width() * 4;
+        for (int i = 0; i < height; ++i) {
+          SbMemoryCopy(cur_dest, cur_src, num_pixel_bytes);
+          cur_src += config_.output.u.RGBA.stride;
+          cur_dest += image_data()->GetDescriptor().pitch_in_bytes;
+        }
+
         set_state(kDone);
       } else if (status != VP8_STATUS_SUSPENDED) {
         DLOG(ERROR) << "WebPIAppend error, status code: " << status;
@@ -140,10 +152,6 @@
   config_.output.colorspace = pixel_format() == render_tree::kPixelFormatRGBA8
                                   ? (has_alpha ? MODE_rgbA : MODE_RGBA)
                                   : (has_alpha ? MODE_bgrA : MODE_BGRA);
-  config_.output.u.RGBA.stride = image_data()->GetDescriptor().pitch_in_bytes;
-  config_.output.u.RGBA.size =
-      static_cast<size_t>(config_.output.u.RGBA.stride *
-                          image_data()->GetDescriptor().size.height());
   // We don't use image buffer as the decoding buffer because libwebp will read
   // from it while we assume that our image buffer is write only.
   config_.output.is_external_memory = 0;
diff --git a/src/cobalt/media/base/drm_system.cc b/src/cobalt/media/base/drm_system.cc
index bd41763..c3f4d23 100644
--- a/src/cobalt/media/base/drm_system.cc
+++ b/src/cobalt/media/base/drm_system.cc
@@ -20,8 +20,19 @@
 namespace cobalt {
 namespace media {
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+DrmSystem::Session::Session(
+    DrmSystem* drm_system,
+    SessionUpdateKeyStatusesCallback update_key_statuses_callback)
+    : drm_system_(drm_system),
+      update_key_statuses_callback_(update_key_statuses_callback),
+      closed_(false) {
+  DCHECK(!update_key_statuses_callback_.is_null());
+}
+#else   // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 DrmSystem::Session::Session(DrmSystem* drm_system)
     : drm_system_(drm_system), closed_(false) {}
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 
 DrmSystem::Session::~Session() {
   if (id_ && !closed_) {
@@ -65,7 +76,12 @@
 DrmSystem::DrmSystem(const char* key_system)
     : wrapped_drm_system_(SbDrmCreateSystem(key_system, this,
                                             OnSessionUpdateRequestGeneratedFunc,
-                                            OnSessionUpdatedFunc)),
+                                            OnSessionUpdatedFunc
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+                                            ,
+                                            OnSessionKeyStatusesChangedFunc
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+                                            )),
       message_loop_(MessageLoop::current()),
       ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
       weak_this_(weak_ptr_factory_.GetWeakPtr()),
@@ -76,9 +92,17 @@
 
 DrmSystem::~DrmSystem() { SbDrmDestroySystem(wrapped_drm_system_); }
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+scoped_ptr<DrmSystem::Session> DrmSystem::CreateSession(
+    SessionUpdateKeyStatusesCallback session_update_key_statuses_callback) {
+  return make_scoped_ptr(
+      new Session(this, session_update_key_statuses_callback));
+}
+#else   // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 scoped_ptr<DrmSystem::Session> DrmSystem::CreateSession() {
   return make_scoped_ptr(new Session(this));
 }
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 
 void DrmSystem::GenerateSessionUpdateRequest(
     Session* session, const std::string& type, const uint8_t* init_data,
@@ -202,6 +226,23 @@
   ticket_to_session_update_map_.erase(session_update_iterator);
 }
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+void DrmSystem::OnSessionKeyStatusChanged(
+    const std::string& session_id, const std::vector<std::string>& key_ids,
+    const std::vector<SbDrmKeyStatus>& key_statuses) {
+  // Find the session by ID.
+  IdToSessionMap::iterator session_iterator =
+      id_to_session_map_.find(session_id);
+  if (session_iterator == id_to_session_map_.end()) {
+    LOG(ERROR) << "Unknown session id: " << session_id << ".";
+    return;
+  }
+  Session* session = session_iterator->second;
+
+  session->update_key_statuses_callback().Run(key_ids, key_statuses);
+}
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 // static
 void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
     SbDrmSystem wrapped_drm_system, void* context, int ticket,
@@ -242,5 +283,39 @@
                             drm_system->weak_this_, ticket, succeeded));
 }
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+// static
+void DrmSystem::OnSessionKeyStatusesChangedFunc(
+    SbDrmSystem wrapped_drm_system, void* context, const void* session_id,
+    int session_id_size, int number_of_keys, const SbDrmKeyId* key_ids,
+    const SbDrmKeyStatus* key_statuses) {
+  DCHECK(context);
+  DrmSystem* drm_system = static_cast<DrmSystem*>(context);
+  DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
+
+  DCHECK(session_id != NULL);
+
+  std::string session_id_copy =
+      std::string(static_cast<const char*>(session_id),
+                  static_cast<const char*>(session_id) + session_id_size);
+
+  std::vector<std::string> key_ids_copy(number_of_keys);
+  std::vector<SbDrmKeyStatus> key_statuses_copy(number_of_keys);
+
+  for (int i = 0; i < number_of_keys; ++i) {
+    const char* identifier =
+        reinterpret_cast<const char*>(key_ids[i].identifier);
+    std::string key_id(identifier, identifier + key_ids[i].identifier_size);
+    key_ids_copy[i] = key_id;
+    key_statuses_copy[i] = key_statuses[i];
+  }
+
+  drm_system->message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&DrmSystem::OnSessionKeyStatusChanged, drm_system->weak_this_,
+                 session_id_copy, key_ids_copy, key_statuses_copy));
+}
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
index 7a400c8..00dfa4a 100644
--- a/src/cobalt/media/base/drm_system.h
+++ b/src/cobalt/media/base/drm_system.h
@@ -16,6 +16,7 @@
 #define COBALT_MEDIA_BASE_DRM_SYSTEM_H_
 
 #include <string>
+#include <vector>
 
 #include "base/hash_tables.h"
 #include "base/memory/ref_counted.h"
@@ -43,6 +44,11 @@
   typedef base::Callback<void()> SessionUpdateRequestDidNotGenerateCallback;
   typedef base::Callback<void()> SessionUpdatedCallback;
   typedef base::Callback<void()> SessionDidNotUpdateCallback;
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+  typedef base::Callback<void(const std::vector<std::string>& key_ids,
+                              const std::vector<SbDrmKeyStatus>& key_statuses)>
+      SessionUpdateKeyStatusesCallback;
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 
   // Flyweight that provides RAII semantics for sessions.
   // Most of logic is implemented by |DrmSystem| and thus sessions must be
@@ -86,18 +92,32 @@
 
    private:
     // Private API for |DrmSystem|.
-    explicit Session(DrmSystem* drm_system);
+    Session(DrmSystem* drm_system
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+            ,
+            SessionUpdateKeyStatusesCallback update_key_statuses_callback
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+            );
     void set_id(const std::string& id) { id_ = id; }
     const SessionUpdateRequestGeneratedCallback&
     update_request_generated_callback() const {
       return update_request_generated_callback_;
     }
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+    const SessionUpdateKeyStatusesCallback& update_key_statuses_callback()
+        const {
+      return update_key_statuses_callback_;
+    }
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 
     DrmSystem* const drm_system_;
     bool closed_;
     base::optional<std::string> id_;
     // Supports spontaneous invocations of |SbDrmSessionUpdateRequestFunc|.
     SessionUpdateRequestGeneratedCallback update_request_generated_callback_;
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+    SessionUpdateKeyStatusesCallback update_key_statuses_callback_;
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 
     friend class DrmSystem;
 
@@ -109,7 +129,11 @@
 
   SbDrmSystem wrapped_drm_system() { return wrapped_drm_system_; }
 
-  scoped_ptr<Session> CreateSession();
+  scoped_ptr<Session> CreateSession(
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+      SessionUpdateKeyStatusesCallback session_update_key_statuses_callback
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+      );
 
  private:
   // Stores context of |GenerateSessionUpdateRequest|.
@@ -149,6 +173,11 @@
       int ticket, const base::optional<std::string>& session_id,
       scoped_array<uint8> message, int message_size);
   void OnSessionUpdated(int ticket, bool succeeded);
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+  void OnSessionKeyStatusChanged(
+      const std::string& session_id, const std::vector<std::string>& key_ids,
+      const std::vector<SbDrmKeyStatus>& key_statuses);
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 
   // Called on any thread, parameters need to be copied immediately.
   static void OnSessionUpdateRequestGeneratedFunc(
@@ -159,6 +188,12 @@
                                    void* context, int ticket,
                                    const void* session_id,
                                    int session_id_length, bool succeeded);
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+  static void OnSessionKeyStatusesChangedFunc(
+      SbDrmSystem wrapped_drm_system, void* context, const void* session_id,
+      int session_id_size, int number_of_keys, const SbDrmKeyId* key_ids,
+      const SbDrmKeyStatus* key_statuses);
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
 
   const SbDrmSystem wrapped_drm_system_;
   MessageLoop* const message_loop_;
diff --git a/src/cobalt/render_tree/filter_node.cc b/src/cobalt/render_tree/filter_node.cc
index 84fd806..fa66bac 100644
--- a/src/cobalt/render_tree/filter_node.cc
+++ b/src/cobalt/render_tree/filter_node.cc
@@ -39,19 +39,19 @@
 
 FilterNode::FilterNode(const OpacityFilter& opacity_filter,
                        const scoped_refptr<render_tree::Node>& source)
-    : data_(opacity_filter, source) {}
+    : data_(opacity_filter, source) { AssertValid(); }
 
 FilterNode::FilterNode(const ViewportFilter& viewport_filter,
                        const scoped_refptr<render_tree::Node>& source)
-    : data_(viewport_filter, source) {}
+    : data_(viewport_filter, source) { AssertValid(); }
 
 FilterNode::FilterNode(const BlurFilter& blur_filter,
                        const scoped_refptr<render_tree::Node>& source)
-    : data_(blur_filter, source) {}
+    : data_(blur_filter, source) { AssertValid(); }
 
 FilterNode::FilterNode(const MapToMeshFilter& map_to_mesh_filter,
                        const scoped_refptr<render_tree::Node>& source)
-    : data_(map_to_mesh_filter, source) {}
+    : data_(map_to_mesh_filter, source) { AssertValid(); }
 
 void FilterNode::Accept(NodeVisitor* visitor) { visitor->Visit(this); }
 
@@ -83,5 +83,12 @@
   return destination_bounds;
 }
 
+void FilterNode::AssertValid() const {
+  if (data_.viewport_filter && data_.viewport_filter->has_rounded_corners()) {
+    DCHECK(data_.viewport_filter->rounded_corners().IsNormalized(
+        data_.viewport_filter->viewport()));
+  }
+}
+
 }  // namespace render_tree
 }  // namespace cobalt
diff --git a/src/cobalt/render_tree/filter_node.h b/src/cobalt/render_tree/filter_node.h
index 5ba8ac7..dc3df7c 100644
--- a/src/cobalt/render_tree/filter_node.h
+++ b/src/cobalt/render_tree/filter_node.h
@@ -74,7 +74,9 @@
     base::optional<MapToMeshFilter> map_to_mesh_filter;
   };
 
-  explicit FilterNode(const Builder& builder) : data_(builder) {}
+  explicit FilterNode(const Builder& builder) : data_(builder) {
+    AssertValid();
+  }
 
   FilterNode(const OpacityFilter& opacity_filter,
              const scoped_refptr<render_tree::Node>& source);
@@ -98,6 +100,8 @@
   const Builder& data() const { return data_; }
 
  private:
+  void AssertValid() const;
+
   const Builder data_;
 };
 
diff --git a/src/cobalt/render_tree/rect_node.cc b/src/cobalt/render_tree/rect_node.cc
index 7b6f656..17b046e 100644
--- a/src/cobalt/render_tree/rect_node.cc
+++ b/src/cobalt/render_tree/rect_node.cc
@@ -88,5 +88,11 @@
 
 math::RectF RectNode::GetBounds() const { return data_.rect; }
 
+void RectNode::AssertValid() const {
+  if (data_.rounded_corners) {
+    DCHECK(data_.rounded_corners->IsNormalized(data_.rect));
+  }
+}
+
 }  // namespace render_tree
 }  // namespace cobalt
diff --git a/src/cobalt/render_tree/rect_node.h b/src/cobalt/render_tree/rect_node.h
index a632890..a257cd8 100644
--- a/src/cobalt/render_tree/rect_node.h
+++ b/src/cobalt/render_tree/rect_node.h
@@ -69,31 +69,39 @@
 
   RectNode(const math::RectF& rect, scoped_ptr<Border> border)
       : data_(rect, border.Pass()) {
+    AssertValid();
   }
   RectNode(const math::RectF& rect, scoped_ptr<Border> border,
            scoped_ptr<RoundedCorners> rounded_corners)
       : data_(rect, border.Pass(), rounded_corners.Pass()) {
+    AssertValid();
   }
   RectNode(const math::RectF& rect, scoped_ptr<Brush> background_brush)
       : data_(rect, background_brush.Pass()) {
+    AssertValid();
   }
   RectNode(const math::RectF& rect, scoped_ptr<Brush> background_brush,
            scoped_ptr<Border> border)
       : data_(rect, background_brush.Pass(), border.Pass()) {
+    AssertValid();
   }
   RectNode(const math::RectF& rect, scoped_ptr<Brush> background_brush,
            scoped_ptr<RoundedCorners> rounded_corners)
       : data_(rect, background_brush.Pass(), rounded_corners.Pass()) {
+    AssertValid();
   }
   RectNode(const math::RectF& rect, scoped_ptr<Brush> background_brush,
            scoped_ptr<Border> border,
            scoped_ptr<RoundedCorners> rounded_corners)
       : data_(rect, background_brush.Pass(), border.Pass(),
               rounded_corners.Pass()) {
+    AssertValid();
   }
   explicit RectNode(const Builder& builder) : data_(builder) {
+    AssertValid();
   }
   explicit RectNode(Builder::Moved builder) : data_(builder) {
+    AssertValid();
   }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
@@ -106,6 +114,8 @@
   const Builder& data() const { return data_; }
 
  private:
+  void AssertValid() const;
+
   const Builder data_;
 };
 
diff --git a/src/cobalt/render_tree/rect_shadow_node.cc b/src/cobalt/render_tree/rect_shadow_node.cc
index 3e602a3..622d42c 100644
--- a/src/cobalt/render_tree/rect_shadow_node.cc
+++ b/src/cobalt/render_tree/rect_shadow_node.cc
@@ -31,5 +31,11 @@
   }
 }
 
+void RectShadowNode::AssertValid() const {
+  if (data_.rounded_corners) {
+    DCHECK(data_.rounded_corners->IsNormalized(data_.rect));
+  }
+}
+
 }  // namespace render_tree
 }  // namespace cobalt
diff --git a/src/cobalt/render_tree/rect_shadow_node.h b/src/cobalt/render_tree/rect_shadow_node.h
index 22344ad..072c80b 100644
--- a/src/cobalt/render_tree/rect_shadow_node.h
+++ b/src/cobalt/render_tree/rect_shadow_node.h
@@ -60,14 +60,16 @@
     float spread;
   };
 
-  explicit RectShadowNode(const Builder& builder) : data_(builder) {}
+  explicit RectShadowNode(const Builder& builder) : data_(builder) {
+    AssertValid();
+  }
 
   RectShadowNode(const math::RectF& rect, const Shadow& shadow)
-      : data_(rect, shadow) {}
+      : data_(rect, shadow) { AssertValid(); }
 
   RectShadowNode(const math::RectF& rect, const Shadow& shadow, bool inset,
                  float spread)
-      : data_(rect, shadow, inset, spread) {}
+      : data_(rect, shadow, inset, spread) { AssertValid(); }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
   math::RectF GetBounds() const OVERRIDE;
@@ -79,6 +81,8 @@
   const Builder& data() const { return data_; }
 
  private:
+  void AssertValid() const;
+
   const Builder data_;
 };
 
diff --git a/src/cobalt/render_tree/rounded_corners.cc b/src/cobalt/render_tree/rounded_corners.cc
index a74a30b..b0a3331 100644
--- a/src/cobalt/render_tree/rounded_corners.cc
+++ b/src/cobalt/render_tree/rounded_corners.cc
@@ -17,38 +17,59 @@
 namespace cobalt {
 namespace render_tree {
 
-void RoundedCorners::Normalize(const math::RectF& rect) {
+RoundedCorners RoundedCorners::Normalize(const math::RectF& rect) const {
   float scale = 1.0f;
   float size;
 
-  size = top_left.horizontal + top_right.horizontal;
+  // Normalize overlapping curves.
+  // https://www.w3.org/TR/css3-background/#corner-overlap
+  // Additionally, normalize opposing curves so the corners do not overlap.
+  size = top_left.horizontal +
+         std::max(top_right.horizontal, bottom_right.horizontal);
   if (size > rect.width()) {
     scale = rect.width() / size;
   }
 
-  size = bottom_left.horizontal + bottom_right.horizontal;
+  size = bottom_left.horizontal +
+         std::max(bottom_right.horizontal, top_right.horizontal);
   if (size > rect.width()) {
     scale = std::min(rect.width() / size, scale);
   }
 
-  size = top_left.vertical + bottom_left.vertical;
+  size = top_left.vertical +
+         std::max(bottom_left.vertical, bottom_right.vertical);
   if (size > rect.height()) {
     scale = std::min(rect.height() / size, scale);
   }
 
-  size = top_right.vertical + bottom_right.vertical;
+  size = top_right.vertical +
+         std::max(bottom_right.vertical, bottom_left.vertical);
   if (size > rect.height()) {
     scale = std::min(rect.height() / size, scale);
   }
 
-  top_left.horizontal *= scale;
-  top_left.vertical *= scale;
-  top_right.horizontal *= scale;
-  top_right.vertical *= scale;
-  bottom_left.horizontal *= scale;
-  bottom_left.vertical *= scale;
-  bottom_right.horizontal *= scale;
-  bottom_right.vertical *= scale;
+  return RoundedCorners(RoundedCorner(top_left.horizontal * scale,
+                                      top_left.vertical * scale),
+                        RoundedCorner(top_right.horizontal * scale,
+                                      top_right.vertical * scale),
+                        RoundedCorner(bottom_right.horizontal * scale,
+                                      bottom_right.vertical * scale),
+                        RoundedCorner(bottom_left.horizontal * scale,
+                                      bottom_left.vertical * scale));
+}
+
+bool RoundedCorners::IsNormalized(const math::RectF& rect) const {
+  return
+      // Adjacent corners must not overlap.
+      top_left.horizontal + top_right.horizontal <= rect.width() &&
+      bottom_left.horizontal + bottom_right.horizontal <= rect.width() &&
+      top_left.vertical + bottom_left.vertical <= rect.height() &&
+      top_right.vertical + bottom_right.vertical <= rect.height() &&
+      // Opposing corners must not overlap.
+      top_left.horizontal + bottom_right.horizontal <= rect.width() &&
+      bottom_left.horizontal + top_right.horizontal <= rect.width() &&
+      top_left.vertical + bottom_right.vertical <= rect.height() &&
+      top_right.vertical + bottom_left.vertical <= rect.height();
 }
 
 }  // namespace render_tree
diff --git a/src/cobalt/render_tree/rounded_corners.h b/src/cobalt/render_tree/rounded_corners.h
index 0a9a09d..cf1d03e 100644
--- a/src/cobalt/render_tree/rounded_corners.h
+++ b/src/cobalt/render_tree/rounded_corners.h
@@ -93,7 +93,8 @@
 
   // Ensure the rounded corners' radii do not exceed the length of the
   // corresponding edge of the given rect.
-  void Normalize(const math::RectF& rect);
+  RoundedCorners Normalize(const math::RectF& rect) const;
+  bool IsNormalized(const math::RectF& rect) const;
 
   bool AreSquares() const {
     return top_left.IsSquare() && top_right.IsSquare() &&
diff --git a/src/cobalt/renderer/backend/egl/graphics_system.cc b/src/cobalt/renderer/backend/egl/graphics_system.cc
index ae84a45..4b317b0 100644
--- a/src/cobalt/renderer/backend/egl/graphics_system.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_system.cc
@@ -69,27 +69,33 @@
 
   // Setup our configuration to support RGBA and compatibility with PBuffer
   // objects (for offscreen rendering).
-  EGLint attribute_list[] = {EGL_SURFACE_TYPE,    // this must be first
-                             EGL_WINDOW_BIT | EGL_PBUFFER_BIT
+  EGLint attribute_list[] = {
+    EGL_SURFACE_TYPE,  // this must be first
+    EGL_WINDOW_BIT | EGL_PBUFFER_BIT
 #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
-                                 | EGL_SWAP_BEHAVIOR_PRESERVED_BIT
+        | EGL_SWAP_BEHAVIOR_PRESERVED_BIT
 #endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
-                             ,
-                             EGL_RED_SIZE,
-                             8,
-                             EGL_GREEN_SIZE,
-                             8,
-                             EGL_BLUE_SIZE,
-                             8,
-                             EGL_ALPHA_SIZE,
-                             8,
+    ,
+    EGL_RED_SIZE,
+    8,
+    EGL_GREEN_SIZE,
+    8,
+    EGL_BLUE_SIZE,
+    8,
+    EGL_ALPHA_SIZE,
+    8,
 #if !SB_HAS_QUIRK(NO_EGL_BIND_TO_TEXTURE)
-                             EGL_BIND_TO_TEXTURE_RGBA,
-                             EGL_TRUE,
+    EGL_BIND_TO_TEXTURE_RGBA,
+    EGL_TRUE,
 #endif
-                             EGL_RENDERABLE_TYPE,
-                             EGL_OPENGL_ES2_BIT,
-                             EGL_NONE};
+    EGL_RENDERABLE_TYPE,
+#if defined(GLES3_SUPPORTED)
+    EGL_OPENGL_ES3_BIT,
+#else
+    EGL_OPENGL_ES2_BIT,
+#endif  // #if defined(GLES3_SUPPORTED)
+    EGL_NONE
+  };
 
   EGLint num_configs;
   eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs);
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
index 4a95847..3defd2e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -190,6 +190,11 @@
 
 void RenderTreeNodeVisitor::Visit(render_tree::ImageNode* image_node) {
   TRACE_EVENT0_IF_ENABLED("Visit(ImageNode)");
+  // The image_node may contain nothing. For example, when it represents a video
+  // or other kind of animated image element before any frame is decoded.
+  if (!image_node->data().source) {
+    return;
+  }
 
   // All Blitter API images derive from skia::Image (so that they can be
   // compatible with the Skia software renderer), so we start here by casting
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.cc b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
index 9dc8082..c2084cf 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
@@ -59,21 +59,11 @@
 void DrawObject::SetRRectUniforms(GLint rect_uniform, GLint corners_uniform,
     const math::RectF& rect, const render_tree::RoundedCorners& corners,
     float inset) {
-  // Ensure corner sizes are non-zero to allow generic handling of square and
-  // rounded corners.
-  const float kMinCornerSize = 0.01f;
-
   math::RectF inset_rect(rect);
   inset_rect.Inset(inset, inset);
   render_tree::RoundedCorners inset_corners =
       corners.Inset(inset, inset, inset, inset);
 
-  // The rect data is a vec4 representing (min.xy, max.xy).
-  float rect_data[4] = {
-    inset_rect.x(), inset_rect.y(), inset_rect.right(), inset_rect.bottom(),
-  };
-  GL_CALL(glUniform4fv(rect_uniform, 1, rect_data));
-
   // Tweak corners that are square-ish so they have values that play
   // nicely with the shader. Interpolating x^2 / a^2 + y^2 / b^2 does not
   // work well when |a| or |b| are very small.
@@ -98,26 +88,40 @@
     inset_corners.bottom_right.vertical = 0.0f;
   }
 
+  // Ensure corner sizes are non-zero to allow generic handling of square and
+  // rounded corners.
+  const float kMinCornerSize = 0.01f;
+  inset_rect.Outset(kMinCornerSize, kMinCornerSize);
+  inset_corners = inset_corners.Inset(-kMinCornerSize, -kMinCornerSize,
+      -kMinCornerSize, -kMinCornerSize);
+  inset_corners = inset_corners.Normalize(inset_rect);
+
+  // The rect data is a vec4 representing (min.xy, max.xy).
+  float rect_data[4] = {
+    inset_rect.x(), inset_rect.y(), inset_rect.right(), inset_rect.bottom(),
+  };
+  GL_CALL(glUniform4fv(rect_uniform, 1, rect_data));
+
   // The corners data is a mat4 with each vector representing a corner
   // (ordered top left, top right, bottom left, bottom right). Each corner
   // vec4 represents (start.xy, radius.xy).
   float corners_data[16] = {
     inset_rect.x() + inset_corners.top_left.horizontal,
     inset_rect.y() + inset_corners.top_left.vertical,
-    std::max(inset_corners.top_left.horizontal, kMinCornerSize),
-    std::max(inset_corners.top_left.vertical, kMinCornerSize),
+    inset_corners.top_left.horizontal,
+    inset_corners.top_left.vertical,
     inset_rect.right() - inset_corners.top_right.horizontal,
     inset_rect.y() + inset_corners.top_right.vertical,
-    std::max(inset_corners.top_right.horizontal, kMinCornerSize),
-    std::max(inset_corners.top_right.vertical, kMinCornerSize),
+    inset_corners.top_right.horizontal,
+    inset_corners.top_right.vertical,
     inset_rect.x() + inset_corners.bottom_left.horizontal,
     inset_rect.bottom() - inset_corners.bottom_left.vertical,
-    std::max(inset_corners.bottom_left.horizontal, kMinCornerSize),
-    std::max(inset_corners.bottom_left.vertical, kMinCornerSize),
+    inset_corners.bottom_left.horizontal,
+    inset_corners.bottom_left.vertical,
     inset_rect.right() - inset_corners.bottom_right.horizontal,
     inset_rect.bottom() - inset_corners.bottom_right.vertical,
-    std::max(inset_corners.bottom_right.horizontal, kMinCornerSize),
-    std::max(inset_corners.bottom_right.vertical, kMinCornerSize),
+    inset_corners.bottom_right.horizontal,
+    inset_corners.bottom_right.vertical,
   };
   GL_CALL(glUniformMatrix4fv(corners_uniform, 1, false, corners_data));
 }
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
index 7662e3c..6212f5f 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
@@ -130,7 +130,7 @@
     draw->draw_object->ExecuteUpdateVertexBuffer(
         graphics_state, program_manager);
   }
-  graphics_state->UpdateVertexData();
+  graphics_state->UpdateVertexBuffers();
 }
 
 void DrawObjectManager::ExecuteOnscreenRasterize(GraphicsState* graphics_state,
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
index c8031c5..eefc2fe 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
@@ -28,59 +28,55 @@
 namespace egl {
 
 namespace {
+const float kSqrt2 = 1.414213562f;
+const float kSqrtPi = 1.772453851f;
+
 // The blur kernel extends for a limited number of sigmas.
 const float kBlurExtentInSigmas = 3.0f;
 
-// The gaussian integral formula used to calculate blur intensity reaches 0
-// intensity at a distance of 1.5. To express pixels in terms of input for
-// this function, a distance of kBlurExtentInSigmas * |blur_sigma| should equal
-// kBlurDistance.
-const float kBlurDistance = 1.5f;
+// The error function is used to calculate the gaussian blur. Inputs for
+// the error function should be scaled by 1 / (sqrt(2) * sigma). Alternatively,
+// it can be viewed as kBlurDistance is the input value at which the blur
+// intensity is effectively 0.
+const float kBlurDistance = kBlurExtentInSigmas / kSqrt2;
 
-void SetSigmaTweak(GLint sigma_tweak_uniform, const math::RectF& spread_rect,
-      const DrawObject::OptionalRoundedCorners& spread_corners,
-      float blur_sigma) {
-  const float kBlurExtentInPixels = kBlurExtentInSigmas * blur_sigma;
+void SetBlurRRectUniforms(const ShaderFragmentColorBlurRrects& shader,
+    math::RectF rect, render_tree::RoundedCorners corners, float sigma) {
+  // Ensure a minimum radius for each corner to avoid division by zero.
+  const float kMinSize = 0.01f;
 
-  // If the blur kernel is larger than the spread rect, then adjust the input
-  // values to the blur calculations to better match the reference output.
-  const float kTweakScale = 0.15f;
-  float sigma_tweak[2] = {
-    std::max(kBlurExtentInPixels - spread_rect.width(), 0.0f) *
-        kTweakScale / blur_sigma,
-    std::max(kBlurExtentInPixels - spread_rect.height(), 0.0f) *
-        kTweakScale / blur_sigma,
-  };
+  rect.Outset(kMinSize, kMinSize);
+  corners = corners.Inset(-kMinSize, -kMinSize, -kMinSize, -kMinSize);
+  corners = corners.Normalize(rect);
 
-  if (spread_corners) {
-    // The shader for blur with rounded rects does not fully factor in
-    // proximity to rounded corners. For example, given a 15-pixel wide
-    // spread rect with two neighboring rounded corners with radius 7,
-    // the center pixels would not include the nearby corners in the
-    // calculation of distance to the edge -- it would always be 7.
-    float corner_gap[2] = {
-      std::min(spread_rect.width() - spread_corners->top_left.horizontal
-                                   - spread_corners->top_right.horizontal,
-               spread_rect.width() - spread_corners->bottom_left.horizontal
-                                   - spread_corners->bottom_right.horizontal),
-      std::min(spread_rect.height() - spread_corners->top_left.vertical
-                                    - spread_corners->bottom_left.vertical,
-               spread_rect.height() - spread_corners->top_right.vertical
-                                    - spread_corners->bottom_right.vertical),
-    };
+  // Specify the blur extent size and the (min.y, max.y) for the rect.
+  const float kBlurExtentInPixels = kBlurExtentInSigmas * sigma;
+  GL_CALL(glUniform3f(shader.u_blur_extent(),
+                      kBlurExtentInPixels, rect.y(), rect.bottom()));
 
-    // This tweak is limited because each edge can have different-sized
-    // gaps between corners. However, the shader is already pretty large,
-    // so this tweak will do for now. The situation is only noticeable with
-    // relatively large blur sigmas.
-    const float kCornerTweakScale = 0.03f;
-    sigma_tweak[0] += std::max(kBlurExtentInPixels - corner_gap[0], 0.0f) *
-        kCornerTweakScale / blur_sigma;
-    sigma_tweak[1] += std::max(kBlurExtentInPixels - corner_gap[1], 0.0f) *
-        kCornerTweakScale / blur_sigma;
-  }
-
-  GL_CALL(glUniform2fv(sigma_tweak_uniform, 1, sigma_tweak));
+  // Set the "start" and "scale" values so that (pos - start) * scale is in the
+  // first quadrant and normalized. Then specify "radius" so that normalized *
+  // radius + start specifies a point on the respective corner.
+  GL_CALL(glUniform4f(shader.u_spread_start_x(),
+                      rect.x() + corners.top_left.horizontal,
+                      rect.right() - corners.top_right.horizontal,
+                      rect.x() + corners.bottom_left.horizontal,
+                      rect.right() - corners.bottom_right.horizontal));
+  GL_CALL(glUniform4f(shader.u_spread_start_y(),
+                      rect.y() + corners.top_left.vertical,
+                      rect.y() + corners.top_right.vertical,
+                      rect.bottom() - corners.bottom_left.vertical,
+                      rect.bottom() - corners.bottom_right.vertical));
+  GL_CALL(glUniform4f(shader.u_spread_scale_y(),
+                      -1.0f / corners.top_left.vertical,
+                      -1.0f / corners.top_right.vertical,
+                      1.0f / corners.bottom_left.vertical,
+                      1.0f / corners.bottom_right.vertical));
+  GL_CALL(glUniform4f(shader.u_spread_radius_x(),
+                      -corners.top_left.horizontal,
+                      corners.top_right.horizontal,
+                      -corners.bottom_left.horizontal,
+                      corners.bottom_right.horizontal));
 }
 }  // namespace
 
@@ -125,9 +121,6 @@
     DCHECK(inner_corners_);
     DCHECK(outer_corners_);
     DCHECK(spread_corners_);
-    inner_corners_->Normalize(inner_rect_);
-    outer_corners_->Normalize(outer_rect_);
-    spread_corners_->Normalize(spread_rect_);
   } else {
     // Non-rounded rects specify vertex offset in terms of sigma from the
     // center of the spread rect.
@@ -152,8 +145,11 @@
     float sigma_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
     GL_CALL(glUniform2f(program->GetFragmentShader().u_sigma_scale(),
                         sigma_scale, sigma_scale));
-    SetSigmaTweak(program->GetFragmentShader().u_sigma_tweak(),
-                  spread_rect_, spread_corners_, blur_sigma_);
+
+    // Pre-calculate the scale values to calculate the normalized gaussian.
+    GL_CALL(glUniform2f(program->GetFragmentShader().u_gaussian_scale(),
+                        -1.0f / (2.0f * blur_sigma_ * blur_sigma_),
+                        1.0f / (kSqrt2 * kSqrtPi * blur_sigma_)));
     if (is_inset_) {
       // Set the outer rect to be an inclusive scissor, and invert the shadow.
       SetRRectUniforms(program->GetFragmentShader().u_scissor_rect(),
@@ -169,18 +165,14 @@
       GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
                           1.0f, 0.0f));
     }
-    SetRRectUniforms(program->GetFragmentShader().u_spread_rect(),
-                     program->GetFragmentShader().u_spread_corners(),
-                     spread_rect_, *spread_corners_, 0.0f);
+    SetBlurRRectUniforms(program->GetFragmentShader(),
+                         spread_rect_, *spread_corners_, blur_sigma_);
   } else {
     ShaderProgram<CommonVertexShader,
                   ShaderFragmentColorBlur>* program;
     program_manager->GetProgram(&program);
     graphics_state->UseProgram(program->GetHandle());
     SetupShader(program->GetVertexShader(), graphics_state);
-
-    SetSigmaTweak(program->GetFragmentShader().u_sigma_tweak(),
-                  spread_rect_, spread_corners_, blur_sigma_);
     if (is_inset_) {
       // Invert the shadow.
       GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
@@ -190,9 +182,11 @@
       GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
                           1.0f, 0.0f));
     }
-    GL_CALL(glUniform2f(program->GetFragmentShader().u_blur_radius(),
-                        spread_rect_.width() * 0.5f * offset_scale_,
-                        spread_rect_.height() * 0.5f * offset_scale_));
+    GL_CALL(glUniform4f(program->GetFragmentShader().u_blur_rect(),
+        (spread_rect_.x() - offset_center_.x()) * offset_scale_,
+        (spread_rect_.y() - offset_center_.y()) * offset_scale_,
+        (spread_rect_.right() - offset_center_.x()) * offset_scale_,
+        (spread_rect_.bottom() - offset_center_.y()) * offset_scale_));
   }
 
   GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count_));
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
index abbd45f..56b7427 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
@@ -49,8 +49,6 @@
     // rounded corner definitions.
     DCHECK(inner_corners_);
     DCHECK(outer_corners_);
-    inner_corners_->Normalize(inner_rect_);
-    outer_corners_->Normalize(outer_rect_);
   }
   graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
 }
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
index c56a4ba..f63e1b5 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
@@ -52,7 +52,6 @@
       corners_(corners),
       vertex_buffer_(NULL) {
   color_ = GetGLRGBA(color * base_state_.opacity);
-  corners_.Normalize(rect_);
   graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
 }
 
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
index d97d12a..64c3c54 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
@@ -45,23 +45,24 @@
     : render_target_handle_(0),
       render_target_serial_(0),
       frame_index_(0),
+      vertex_data_capacity_(0),
       vertex_data_reserved_(kVertexDataAlignment - 1),
       vertex_data_allocated_(0),
-      vertex_data_buffer_updated_(false) {
-  GL_CALL(glGenBuffers(kNumFramesBuffered, &vertex_data_buffer_handle_[0]));
+      vertex_index_capacity_(0),
+      vertex_index_reserved_(0),
+      vertex_index_allocated_(0),
+      vertex_buffers_updated_(false) {
+  GL_CALL(glGenBuffers(kNumFramesBuffered, vertex_data_buffer_handle_));
+  GL_CALL(glGenBuffers(kNumFramesBuffered, vertex_index_buffer_handle_));
   memset(clip_adjustment_, 0, sizeof(clip_adjustment_));
   SetDirty();
   blend_enabled_ = false;
   Reset();
-
-  // These settings should only need to be set once. Nothing should touch them.
-  GL_CALL(glDisable(GL_DITHER));
-  GL_CALL(glDisable(GL_CULL_FACE));
-  GL_CALL(glDisable(GL_STENCIL_TEST));
 }
 
 GraphicsState::~GraphicsState() {
-  GL_CALL(glDeleteBuffers(kNumFramesBuffered, &vertex_data_buffer_handle_[0]));
+  GL_CALL(glDeleteBuffers(kNumFramesBuffered, vertex_data_buffer_handle_));
+  GL_CALL(glDeleteBuffers(kNumFramesBuffered, vertex_index_buffer_handle_));
 }
 
 void GraphicsState::SetDirty() {
@@ -71,8 +72,14 @@
 
 void GraphicsState::BeginFrame() {
   DCHECK_EQ(vertex_data_allocated_, 0);
-  if (vertex_data_reserved_ > vertex_data_buffer_.capacity()) {
-    vertex_data_buffer_.reserve(vertex_data_reserved_);
+  if (vertex_data_reserved_ > vertex_data_capacity_) {
+    vertex_data_capacity_ = vertex_data_reserved_;
+    vertex_data_buffer_.reset(new uint8_t[vertex_data_capacity_]);
+  }
+  DCHECK_EQ(vertex_index_allocated_, 0);
+  if (vertex_index_reserved_ > vertex_index_capacity_) {
+    vertex_index_capacity_ = vertex_index_reserved_;
+    vertex_index_buffer_.reset(new uint16_t[vertex_index_capacity_]);
   }
 
   // Reset to default GL state. Assume the current state is dirty, so just
@@ -83,10 +90,12 @@
 }
 
 void GraphicsState::EndFrame() {
-  // Reset the vertex data buffer.
+  // Reset the vertex data and index buffers.
   vertex_data_reserved_ = kVertexDataAlignment - 1;
   vertex_data_allocated_ = 0;
-  vertex_data_buffer_updated_ = false;
+  vertex_index_reserved_ = 0;
+  vertex_index_allocated_ = 0;
+  vertex_buffers_updated_ = false;
   frame_index_ = (frame_index_ + 1) % kNumFramesBuffered;
 
   // Force default GL state. The current state may be marked dirty, so don't
@@ -123,10 +132,16 @@
     GL_CALL(glUseProgram(program));
   }
 
-  if (array_buffer_handle_ != vertex_data_buffer_handle_[frame_index_]) {
+  if (vertex_data_allocated_ > 0 &&
+      array_buffer_handle_ != vertex_data_buffer_handle_[frame_index_]) {
     array_buffer_handle_ = vertex_data_buffer_handle_[frame_index_];
     GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, array_buffer_handle_));
   }
+  if (vertex_index_allocated_ > 0 &&
+      index_buffer_handle_ != vertex_index_buffer_handle_[frame_index_]) {
+    index_buffer_handle_ = vertex_index_buffer_handle_[frame_index_];
+    GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_handle_));
+  }
 
   // Disable any vertex attribute arrays that will not be used.
   disable_vertex_attrib_array_mask_ = enabled_vertex_attrib_array_mask_;
@@ -282,13 +297,13 @@
 
 void GraphicsState::ReserveVertexData(size_t bytes) {
   DCHECK_EQ(vertex_data_allocated_, 0);
-  DCHECK(!vertex_data_buffer_updated_);
+  DCHECK(!vertex_buffers_updated_);
   vertex_data_reserved_ += bytes + (kVertexDataAlignment - 1) &
                            ~(kVertexDataAlignment - 1);
 }
 
 uint8_t* GraphicsState::AllocateVertexData(size_t bytes) {
-  DCHECK(!vertex_data_buffer_updated_);
+  DCHECK(!vertex_buffers_updated_);
 
   // Ensure the start address is aligned.
   uintptr_t start_address =
@@ -303,16 +318,50 @@
   return reinterpret_cast<uint8_t*>(start_address);
 }
 
-void GraphicsState::UpdateVertexData() {
-  DCHECK(!vertex_data_buffer_updated_);
-  vertex_data_buffer_updated_ = true;
-  if (array_buffer_handle_ != vertex_data_buffer_handle_[frame_index_]) {
-    array_buffer_handle_ = vertex_data_buffer_handle_[frame_index_];
-    GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, array_buffer_handle_));
-  }
+void GraphicsState::ReserveVertexIndices(size_t count) {
+  DCHECK_EQ(vertex_index_allocated_, 0);
+  DCHECK(!vertex_buffers_updated_);
+  vertex_index_reserved_ += count;
+}
+
+uint16_t* GraphicsState::AllocateVertexIndices(size_t count) {
+  uint16_t* client_pointer = &vertex_index_buffer_[vertex_index_allocated_];
+  vertex_index_allocated_ += count;
+  DCHECK_LE(vertex_index_allocated_, vertex_index_reserved_);
+  return client_pointer;
+}
+
+const void* GraphicsState::GetVertexIndexPointer(
+    const uint16_t* client_pointer) {
+  DCHECK_GE(client_pointer, &vertex_index_buffer_[0]);
+  DCHECK_LE(client_pointer, &vertex_index_buffer_[vertex_index_allocated_]);
+  const GLvoid* gl_pointer = reinterpret_cast<const GLvoid*>(
+      reinterpret_cast<const uint8_t*>(client_pointer) -
+      reinterpret_cast<const uint8_t*>(&vertex_index_buffer_[0]));
+  return gl_pointer;
+}
+
+void GraphicsState::UpdateVertexBuffers() {
+  DCHECK(!vertex_buffers_updated_);
+  vertex_buffers_updated_ = true;
+
   if (vertex_data_allocated_ > 0) {
+    if (array_buffer_handle_ != vertex_data_buffer_handle_[frame_index_]) {
+      array_buffer_handle_ = vertex_data_buffer_handle_[frame_index_];
+      GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, array_buffer_handle_));
+    }
     GL_CALL(glBufferData(GL_ARRAY_BUFFER, vertex_data_allocated_,
-                         &vertex_data_buffer_[0], GL_STREAM_DRAW));
+                         &vertex_data_buffer_[0], GL_DYNAMIC_DRAW));
+  }
+
+  if (vertex_index_allocated_ > 0) {
+    if (index_buffer_handle_ != vertex_index_buffer_handle_[frame_index_]) {
+      index_buffer_handle_ = vertex_index_buffer_handle_[frame_index_];
+      GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_handle_));
+    }
+    GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+                         vertex_index_allocated_ * sizeof(uint16_t),
+                         &vertex_index_buffer_[0], GL_DYNAMIC_DRAW));
   }
 }
 
@@ -346,12 +395,17 @@
 void GraphicsState::Reset() {
   program_ = 0;
 
+  GL_CALL(glDisable(GL_DITHER));
+  GL_CALL(glDisable(GL_STENCIL_TEST));
+  GL_CALL(glDisable(GL_CULL_FACE));
+
   GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, render_target_handle_));
   GLViewport(viewport_, render_target_size_);
   GLScissor(scissor_, render_target_size_);
   GL_CALL(glEnable(GL_SCISSOR_TEST));
 
   array_buffer_handle_ = 0;
+  index_buffer_handle_ = 0;
   texture_unit_ = 0;
   memset(&texunit_target_, 0, sizeof(texunit_target_));
   memset(&texunit_texture_, 0, sizeof(texunit_texture_));
@@ -359,9 +413,15 @@
   disable_vertex_attrib_array_mask_ = 0;
   clip_adjustment_dirty_ = true;
 
-  if (vertex_data_buffer_updated_) {
-    array_buffer_handle_ = vertex_data_buffer_handle_[frame_index_];
-    GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, array_buffer_handle_));
+  if (vertex_buffers_updated_) {
+    if (vertex_data_allocated_ > 0) {
+      array_buffer_handle_ = vertex_data_buffer_handle_[frame_index_];
+      GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, array_buffer_handle_));
+    }
+    if (vertex_index_allocated_ > 0) {
+      index_buffer_handle_ = vertex_index_buffer_handle_[frame_index_];
+      GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_handle_));
+    }
   }
 
   if (blend_enabled_) {
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.h b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
index dcc8c3c..51c8ec5 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.h
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
@@ -16,9 +16,9 @@
 #define COBALT_RENDERER_RASTERIZER_EGL_GRAPHICS_STATE_H_
 
 #include <GLES2/gl2.h>
-#include <vector>
 
 #include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect.h"
 #include "cobalt/math/size.h"
@@ -104,9 +104,23 @@
   // be done after all calls to ReserveVertexData for a given render frame.
   uint8_t* AllocateVertexData(size_t bytes);
 
-  // Update the GPU with the current contents of the vertex data buffer. This
-  // should only be called once, after BeginFrame().
-  void UpdateVertexData();
+  // Reserve the specified number of vertex indices for the upcoming frame.
+  // This must be called outside of a render frame.
+  void ReserveVertexIndices(size_t count);
+
+  // Returns a client-side pointer to the specified number of vertex indices.
+  // These indices should have been reserved using ReserveVertexIndices.
+  // Allocations can only be made after all calls to reserve indices for a
+  // given frame.
+  uint16_t* AllocateVertexIndices(size_t count);
+
+  // Return a pointer to a previously allocated vertex index buffer. The return
+  // value is suitable for use with glDrawElements.
+  const GLvoid* GetVertexIndexPointer(const uint16_t* client_pointer);
+
+  // Update the GPU with the current contents of the vertex data and index
+  // buffers. This should only be called once, after BeginFrame().
+  void UpdateVertexBuffers();
 
   // Specify vertex attribute data that the current program will use.
   // |client_pointer| should be within the range of addresses returned by
@@ -134,6 +148,7 @@
 
   GLuint program_;
   GLuint array_buffer_handle_;
+  GLuint index_buffer_handle_;
   GLenum texture_unit_;
   uint32_t enabled_vertex_attrib_array_mask_;
   uint32_t disable_vertex_attrib_array_mask_;
@@ -151,11 +166,17 @@
   int frame_index_;
 
   static const size_t kVertexDataAlignment = 4;
-  std::vector<uint8_t> vertex_data_buffer_;
+  scoped_array<uint8_t> vertex_data_buffer_;
+  size_t vertex_data_capacity_;
   size_t vertex_data_reserved_;
   size_t vertex_data_allocated_;
   GLuint vertex_data_buffer_handle_[kNumFramesBuffered];
-  bool vertex_data_buffer_updated_;
+  scoped_array<uint16_t> vertex_index_buffer_;
+  size_t vertex_index_capacity_;
+  size_t vertex_index_reserved_;
+  size_t vertex_index_allocated_;
+  GLuint vertex_index_buffer_handle_[kNumFramesBuffered];
+  bool vertex_buffers_updated_;
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index e0b33bb..bcd8e49 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -156,6 +156,10 @@
   RasterizeTree(render_tree, render_target_egl, content_rect);
 
   graphics_context_->SwapBuffers(render_target_egl);
+
+  // Reset the fallback context in case it is used between frames (e.g.
+  // to initialize images).
+  GetFallbackContext()->resetContext();
 }
 
 void HardwareRasterizer::Impl::SubmitToFallbackRasterizer(
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 8153798..c065daf 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -214,8 +214,6 @@
         draw_state_.rounded_scissor_rect = data.viewport_filter->viewport();
         draw_state_.rounded_scissor_corners =
             data.viewport_filter->rounded_corners();
-        draw_state_.rounded_scissor_corners->Normalize(
-            draw_state_.rounded_scissor_rect);
         data.source->Accept(this);
         draw_state_.rounded_scissor_corners = base::nullopt;
         return;
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
index d83bd62..50ebd6a 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
@@ -13,54 +13,19 @@
 // limitations under the License.

 

 precision mediump float;

-uniform vec2 u_blur_radius;

+uniform vec4 u_blur_rect;

 uniform vec2 u_scale_add;

 

-// Adjust the sigma input value to tweak the blur output so that it better

-// matches the reference. This is usually only needed for very large sigmas.

-uniform vec2 u_sigma_tweak;

-

-varying vec2 v_offset;    // Relative to the blur center.

+varying vec2 v_offset;

 varying vec4 v_color;

 

+#include "function_gaussian_integral.inc"

+

 void main() {

-  // Distance from the blur radius.

-  // Both v_offset and u_blur_radius are expressed in terms of the

-  //   blur sigma.

-  vec2 pos = abs(v_offset) - u_blur_radius + u_sigma_tweak;

-  vec2 pos2 = pos * pos;

-  vec2 pos3 = pos2 * pos;

-  vec4 posx = vec4(1.0, pos.x, pos2.x, pos3.x);

-  vec4 posy = vec4(1.0, pos.y, pos2.y, pos3.y);

-

-  // Approximation of the gaussian integral from [x, +inf).

-  // http://stereopsis.com/shadowrect/

-  const vec4 klower = vec4(0.4375, -1.125, -0.75, -0.1666666);

-  const vec4 kmiddle = vec4(0.5, -0.75, 0.0, 0.3333333);

-  const vec4 kupper = vec4(0.5625, -1.125, 0.75, -0.1666666);

-

-  float gaussx = 0.0;

-  if (pos.x < -1.5) {

-    gaussx = 1.0;

-  } else if (pos.x < -0.5) {

-    gaussx = dot(posx, klower);

-  } else if (pos.x < 0.5) {

-    gaussx = dot(posx, kmiddle);

-  } else if (pos.x < 1.5) {

-    gaussx = dot(posx, kupper);

-  }

-

-  float gaussy = 0.0;

-  if (pos.y < -1.5) {

-    gaussy = 1.0;

-  } else if (pos.y < -0.5) {

-    gaussy = dot(posy, klower);

-  } else if (pos.y < 0.5) {

-    gaussy = dot(posy, kmiddle);

-  } else if (pos.y < 1.5) {

-    gaussy = dot(posy, kupper);

-  }

-

-  float alpha_scale = gaussx * gaussy * u_scale_add.x + u_scale_add.y;

-  gl_FragColor = v_color * alpha_scale;

+  // Get the integral over the interval occupied by the rectangle. Both

+  // v_offset and u_blur_rect are already scaled for the integral function.

+  float integral = GaussianIntegral(u_blur_rect.xz - v_offset.xx) *

+                   GaussianIntegral(u_blur_rect.yw - v_offset.yy);

+  float blur_scale = integral * u_scale_add.x + u_scale_add.y;

+  gl_FragColor = v_color * blur_scale;

 }

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
index 1d17a97..ee8c4ae 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
@@ -20,8 +20,27 @@
 // represents (start.xy, radius.xy).

 uniform vec4 u_scissor_rect;

 uniform mat4 u_scissor_corners;

-uniform vec4 u_spread_rect;

-uniform mat4 u_spread_corners;

+

+// The rounded spread rect is represented in a way to optimize calculation of

+// the extents. Each element of a vec4 represents a corner's value -- order

+// is top left, top right, bottom left, bottom right. Extents for each corner

+// can be calculated as:

+//   extents_x = start_x + radius_x * sqrt(1 - scaled_y^2) where

+//   scaled_y = clamp((pos.yyyy - start_y) * scale_y, 0.0, 1.0)

+// To simplify handling left vs right and top vs bottom corners, the sign of

+// scale_y and radius_x handles negation as needed.

+uniform vec4 u_spread_start_x;

+uniform vec4 u_spread_start_y;

+uniform vec4 u_spread_scale_y;

+uniform vec4 u_spread_radius_x;

+

+// The blur extent specifies (3 * sigma, min_rect_y, max_rect_y). This is used

+// to clamp the interval over which integration should be evaluated.

+uniform vec3 u_blur_extent;

+

+// The gaussian scale uniform is used to simplify calculation of the gaussian

+// function at a particular point.

+uniform vec2 u_gaussian_scale;

 

 // The scale_add uniform is used to switch the shader between generating

 // outset shadows and inset shadows. It impacts the shadow gradient and

@@ -34,139 +53,81 @@
 // translate pixel distances into sigma distances.

 uniform vec2 u_sigma_scale;

 

-// Adjust the sigma input value to tweak the blur output so that it better

-// matches the reference. This is usually only needed for very large sigmas.

-uniform vec2 u_sigma_tweak;

-

 varying vec2 v_offset;

 varying vec4 v_color;

 

 #include "function_is_outside_rrect.inc"

+#include "function_gaussian_integral.inc"

 

-// Calculate the distance from a point in the first quadrant to an ellipse

-// centered at the origin.

-//

-// http://iquilezles.org/www/articles/ellipsedist/ellipsedist.htm

-float GetEllipseDistance(vec2 p, vec2 ab) {

-  if (abs(ab.x - ab.y) < 0.5) {

-    return length(p) - ab.x;

-  }

+vec2 GetXExtents(float y) {

+  // Use x^2 / a^2 + y^2 / b^2 = 1 to solve for the x value of each rounded

+  // corner at the given y.

+  vec4 scaled = clamp((y - u_spread_start_y) * u_spread_scale_y, 0.0, 1.0);

+  vec4 root = sqrt(1.0 - scaled * scaled);

+  vec4 extent = u_spread_start_x + u_spread_radius_x * root;

 

-  if (p.x > p.y) {

-    p = p.yx;

-    ab = ab.yx;

-  }

-

-  float lr = 1.0 / (ab.y * ab.y - ab.x * ab.x);

-  float m = ab.x * p.x * lr;

-  float m2 = m * m;

-  float n = ab.y * p.y * lr;

-  float n2 = n * n;

-  float c = (m2 + n2 - 1.0) * 0.333333333;

-  float c3 = c * c * c;

-  float q = c3 + m2 * n2 * 2.0;

-  float d = c3 + m2 * n2;

-  float g = m + m * n2;

-

-  float co;

-

-  if (d < 0.0) {

-    float p = acos(q / c3) * 0.333333333;

-    float s = cos(p);

-    float t = sin(p) * 1.732050808;

-    float rx = sqrt(-c * (s + t + 2.0) + m2);

-    float ry = sqrt(-c * (s - t + 2.0) + m2);

-    co = (ry + sign(lr) * rx + abs(g) / (rx * ry) - m) * 0.5;

-  } else {

-    float h = 2.0 * m * n * sqrt(d);

-    float s = sign(q + h) * pow(abs(q + h), 0.333333333);

-    float u = sign(q - h) * pow(abs(q - h), 0.333333333);

-    float rx = -s - u - c * 4.0 + 2.0 * m2;

-    float ry = (s - u) * 1.732050808;

-    float rm = sqrt(rx * rx + ry * ry);

-    float p = ry / sqrt(rm - rx);

-    co = (p + 2.0 * g / rm - m) * 0.5;

-  }

-

-  float si = sqrt(1.0 - co * co);

-  vec2 closest = vec2(ab.x * co, ab.y * si);

-  return length(closest - p) * sign(p.y - closest.y);

+  // If the y value was before a corner started, then the calculated extent

+  // would equal the unrounded rectangle's extents (since negative values were

+  // clamped to 0 in the above calculation). So smaller extents (i.e. extents

+  // closer to the rectangle center), represent the relevant corners' extents.

+  return vec2(max(extent.x, extent.z), min(extent.y, extent.w));

 }

 

-// Get the x and y distances from the nearest edges of the rounded rect.

-vec2 GetBlurPosition(vec4 rect, mat4 corners) {

-  vec2 pos = max(rect.xy - v_offset, v_offset - rect.zw);

+float GetXBlur(float x, float y) {

+  // Get the integral over the interval occupied by the rectangle.

+  vec2 pos = (GetXExtents(y) - x) * u_sigma_scale;

+  return GaussianIntegral(pos);

+}

 

-  vec4 select_corner = vec4(

-      step(v_offset.x, corners[0].x) * step(v_offset.y, corners[0].y),

-      step(corners[1].x, v_offset.x) * step(v_offset.y, corners[1].y),

-      step(v_offset.x, corners[2].x) * step(corners[2].y, v_offset.y),

-      step(corners[3].x, v_offset.x) * step(corners[3].y, v_offset.y));

-  if (dot(select_corner, vec4(1.0)) > 0.5) {

-    // Use distance from the closest point on the ellipse as the position

-    // for blur calculations.

-    vec4 corner = corners * select_corner;

-    float dist = GetEllipseDistance(abs(v_offset - corner.xy), corner.zw);

+vec3 GetGaussian(vec3 offset) {

+  // Evaluate the gaussian at the given offsets.

+  return exp(offset * offset * u_gaussian_scale.x);

+}

 

-    // Since the transition from non-corner to corner positions happens along

-    // the ellipse's x or y axis, either pos.x = -corner.z or pos.y = -corner.w

-    // when the transition happens. Keep one ordinate at a minimum so that

-    // distance from the ellipse smoothly changes blur intensity.

-    //

-    // The return vec2 is used as blur = gi(x) * gi(y) where gi = the guassian

-    // integral function. Swapping x and y has no impact on the final blur, so

-    // distance can be substituted for one ordinate as long as the other takes

-    // the right value. E.g. when pos = (-corner.z, 0), the point is on the top

-    // center of the ellipse, so dist = 0. When pos = (0, -corner.w), dist = 0.

-    // When pos = (-corner.z, -corner.w), the point is at the center of the

-    // ellipse, and dist = max(-corner.z, -corner.w). So dist can be used as

-    // long as the other ordinate is min(pos.x, pos.y).

-    return vec2(dist, min(min(pos.x, pos.y), max(-corner.z, -corner.w)));

-  }

+float GetBlur(vec2 pos) {

+  // Approximate the 2D gaussian filter using numerical integration. Sample

+  // points between the y extents of the rectangle.

+  float low = clamp(pos.y - u_blur_extent.x, u_blur_extent.y, u_blur_extent.z);

+  float high = clamp(pos.y + u_blur_extent.x, u_blur_extent.y, u_blur_extent.z);

 

-  return pos;

+  // Use the Gauss–Legendre quadrature with 6 points to numerically integrate.

+  // Using fewer samples will show artifacts with elliptical corners that are

+  // likely to be used.

+  const vec3 kStepScale1 = vec3(-0.932470, -0.661209, -0.238619);

+  const vec3 kStepScale2 = vec3( 0.932470,  0.661209,  0.238619);

+  const vec3 kWeight = vec3(0.171324, 0.360762, 0.467914);

+

+  float half_size = (high - low) * 0.5;

+  float middle = (high + low) * 0.5;

+  vec3 weight = half_size * kWeight;

+  vec3 pos1 = middle + half_size * kStepScale1;

+  vec3 pos2 = middle + half_size * kStepScale2;

+  vec3 offset1 = pos1 - pos.yyy;

+  vec3 offset2 = pos2 - pos.yyy;

+

+  // The integral along the x-axis is computed. The integral along the y-axis

+  // is roughly approximated. To get the 2D filter, multiply the two integrals.

+  // Visual artifacts appear when the computed integrals along the x-axis

+  // change rapidly between samples (e.g. elliptical corners that are much

+  // wider than they are tall).

+  vec3 xblur1 = vec3(GetXBlur(pos.x, pos1.x),

+                     GetXBlur(pos.x, pos1.y),

+                     GetXBlur(pos.x, pos1.z));

+  vec3 xblur2 = vec3(GetXBlur(pos.x, pos2.x),

+                     GetXBlur(pos.x, pos2.y),

+                     GetXBlur(pos.x, pos2.z));

+  vec3 yblur1 = GetGaussian(offset1) * weight;

+  vec3 yblur2 = GetGaussian(offset2) * weight;

+

+  // Since each yblur value should be scaled by u_gaussian_scale.y, save some

+  // cycles and multiply the sum by it.

+  return (dot(xblur1, yblur1) + dot(xblur2, yblur2)) * u_gaussian_scale.y;

 }

 

 void main() {

   float scissor_scale =

       IsOutsideRRect(v_offset, u_scissor_rect, u_scissor_corners) *

       u_scale_add.x + u_scale_add.y;

-

-  vec2 pos = GetBlurPosition(u_spread_rect, u_spread_corners) *

-             u_sigma_scale + u_sigma_tweak;

-  vec2 pos2 = pos * pos;

-  vec2 pos3 = pos2 * pos;

-  vec4 posx = vec4(1.0, pos.x, pos2.x, pos3.x);

-  vec4 posy = vec4(1.0, pos.y, pos2.y, pos3.y);

-

-  // Approximation of the gaussian integral from [x, +inf).

-  // http://stereopsis.com/shadowrect/

-  const vec4 klower = vec4(0.4375, -1.125, -0.75, -0.1666666);

-  const vec4 kmiddle = vec4(0.5, -0.75, 0.0, 0.3333333);

-  const vec4 kupper = vec4(0.5625, -1.125, 0.75, -0.1666666);

-

-  float gaussx = 0.0;

-  if (pos.x < -1.5) {

-    gaussx = 1.0;

-  } else if (pos.x < -0.5) {

-    gaussx = dot(posx, klower);

-  } else if (pos.x < 0.5) {

-    gaussx = dot(posx, kmiddle);

-  } else if (pos.x < 1.5) {

-    gaussx = dot(posx, kupper);

-  }

-

-  float gaussy = 0.0;

-  if (pos.y < -1.5) {

-    gaussy = 1.0;

-  } else if (pos.y < -0.5) {

-    gaussy = dot(posy, klower);

-  } else if (pos.y < 0.5) {

-    gaussy = dot(posy, kmiddle);

-  } else if (pos.y < 1.5) {

-    gaussy = dot(posy, kupper);

-  }

-

-  float alpha_scale = gaussx * gaussy * u_scale_add.x + u_scale_add.y;

-  gl_FragColor = v_color * (alpha_scale * scissor_scale);

+  float blur_scale = GetBlur(v_offset) * u_scale_add.x + u_scale_add.y;

+  gl_FragColor = v_color * (blur_scale * scissor_scale);

 }

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/function_gaussian_integral.inc b/src/cobalt/renderer/rasterizer/egl/shaders/function_gaussian_integral.inc
new file mode 100644
index 0000000..36f3236
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/function_gaussian_integral.inc
@@ -0,0 +1,35 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

+// Licensed under the Apache License, Version 2.0 (the "License");

+// you may not use this file except in compliance with the License.

+// You may obtain a copy of the License at

+//

+//     http://www.apache.org/licenses/LICENSE-2.0

+//

+// Unless required by applicable law or agreed to in writing, software

+// distributed under the License is distributed on an "AS IS" BASIS,

+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+// See the License for the specific language governing permissions and

+// limitations under the License.

+

+// Calculate the normalized gaussian integral from (pos.x * k, pos.y * k)

+// where k = sqrt(2) * sigma and pos.x <= pos.y. This is just a 1D filter --

+// the pos.x and pos.y values are expected to be on the same axis.

+float GaussianIntegral(vec2 pos) {

+  // Approximation of the error function.

+  // For x >= 0,

+  //   erf(x) = 1 - 1 / (1 + k1 * x + k2 * x^2 + k3 * x^3 + k4 * x^4)^4

+  //   where k1 = 0.278393, k2 = 0.230389, k3 = 0.000972, k4 = 0.078108.

+  // For y < 0,

+  //   erf(y) = -erf(-y).

+  vec2 s = sign(pos);

+  vec2 a = abs(pos);

+  vec2 t = 1.0 +

+           (0.278393 + (0.230389 + (0.000972 + 0.078108 * a) * a) * a) * a;

+  vec2 t2 = t * t;

+  vec2 erf = s - s / (t2 * t2);

+

+  // erf(x) = the integral of the normalized gaussian from [-x * k, x * k],

+  // where k = sqrt(2) * sigma. Find the integral from (pos.x * k, pos.y * k).

+  return dot(erf, vec2(-0.5, 0.5));

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
index 444d630..82e71d6 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
@@ -28,6 +28,7 @@
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_gaussian_integral.inc',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_offset.glsl',
diff --git a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
index b89a099..8df6fd1 100644
--- a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
@@ -489,8 +489,35 @@
       } break;
       case Image::YUV_2PLANE_BT709: {
         std::vector<TextureInfo> texture_infos;
+#if SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
+        switch (image.textures[0].texture->GetFormat()) {
+          case GL_ALPHA:
+            texture_infos.push_back(TextureInfo("y", "a"));
+            break;
+#if defined(GL_RED_EXT)
+          case GL_RED_EXT:
+            texture_infos.push_back(TextureInfo("y", "r"));
+            break;
+#endif
+          default:
+            NOTREACHED();
+        }
+        switch (image.textures[1].texture->GetFormat()) {
+          case GL_LUMINANCE_ALPHA:
+            texture_infos.push_back(TextureInfo("uv", "ba"));
+            break;
+#if defined(GL_RG_EXT)
+          case GL_RG_EXT:
+            texture_infos.push_back(TextureInfo("uv", "rg"));
+            break;
+#endif
+          default:
+            NOTREACHED();
+        }
+#else  // SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
         texture_infos.push_back(TextureInfo("y", "a"));
         texture_infos.push_back(TextureInfo("uv", "ba"));
+#endif  // SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
         result = MakeBlitProgram(
             color_matrix, texture_infos,
             CreateFragmentShader(texture_target, texture_infos));
diff --git a/src/cobalt/renderer/rasterizer/lib/exported/graphics.h b/src/cobalt/renderer/rasterizer/lib/exported/graphics.h
new file mode 100644
index 0000000..7b1d4bd
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/exported/graphics.h
@@ -0,0 +1,54 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// All imported functions defined below MUST be implemented by client
+// applications.
+
+#ifndef COBALT_RENDERER_RASTERIZER_LIB_EXPORTED_GRAPHICS_H_
+#define COBALT_RENDERER_RASTERIZER_LIB_EXPORTED_GRAPHICS_H_
+
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*CbLibGraphicsContextCreatedCallback)(void* context);
+typedef void (*CbLibGraphicsBeginRenderFrameCallback)(
+    void* context, intptr_t render_tree_texture_handle);
+typedef void (*CbLibGraphicsEndRenderFrameCallback)(void* context);
+
+// Sets a callback which will be called from the rasterization thread once the
+// graphics context has been created.
+SB_EXPORT_PLATFORM void CbLibGraphicsSetContextCreatedCallback(
+    void* context, CbLibGraphicsContextCreatedCallback callback);
+
+// Sets a callback which will be called as often as the platform can swap
+// buffers from the rasterization thread. |render_tree_texture_handle| which is
+// provided to the callback corresponds to a GLint texture ID for the current
+// RenderTree.
+SB_EXPORT_PLATFORM void CbLibGraphicsSetBeginRenderFrameCallback(
+    void* context, CbLibGraphicsBeginRenderFrameCallback callback);
+
+// Sets a callback which will be called at the end of rendering, after swap
+// buffers has been called.
+SB_EXPORT_PLATFORM void CbLibGraphicsSetEndRenderFrameCallback(
+    void* context, CbLibGraphicsEndRenderFrameCallback callback);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_RENDERER_RASTERIZER_LIB_EXPORTED_GRAPHICS_H_
diff --git a/src/cobalt/renderer/rasterizer/lib/exported/video.h b/src/cobalt/renderer/rasterizer/lib/exported/video.h
index 5cb080c..9f06902 100644
--- a/src/cobalt/renderer/rasterizer/lib/exported/video.h
+++ b/src/cobalt/renderer/rasterizer/lib/exported/video.h
@@ -22,11 +22,10 @@
 extern "C" {
 #endif
 
-// Functions to be called to set callbacks for the updates to video rendering
-// parameters. When the host application is notified that the graphics context
-// has been created, it should install the callbacks within the same call stack
-// of CbLibOnGraphicsContextCreated. Failing to do so will crash the application
-// on debug mode and no-op on production builds.
+// Functions to be called to set callbacks to receive updates to video rendering
+// parameters. By default, this callbacks are not set and warnings will be
+// when an unhandled parameter change happens. To unset them again, any of these
+// setter functions can be called with a null callback function pointer.
 
 typedef enum {
   kCbLibVideoProjectionTypeNone = 0,  // When no offscreen video is playing.
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
index 7b8415a..e1cb238 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -20,14 +20,15 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/lazy_instance.h"
 #include "base/threading/thread_checker.h"
 #include "cobalt/math/clamp.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 #include "cobalt/renderer/backend/egl/render_target.h"
 #include "cobalt/renderer/rasterizer/common/find_node.h"
+#include "cobalt/renderer/rasterizer/lib/exported/graphics.h"
 #include "cobalt/renderer/rasterizer/lib/exported/video.h"
-#include "cobalt/renderer/rasterizer/lib/imported/graphics.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_image.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_mesh.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
@@ -49,20 +50,80 @@
 
 const float kMaxRenderTargetSize = 15360.0f;
 
-ExternalRasterizer::Impl* g_external_rasterizer_impl_;
+// Matches the signatures of the callback setter functions in exported/video.h
+// and exported/graphics.h.
+template <typename Ret, typename... Args>
+using CallbackSetter = void(void*, Ret (*)(void*, Args...));
 
-void DefaultOnUpdateProjectionType(CbLibVideoProjectionType) {
-  LOG(DFATAL) << "CbLibVideoUpdateProjectionTypeCallback not set.";
-}
-void DefaultOnUpdateMeshes(CbLibVideoMesh, CbLibVideoMesh) {
-  LOG(DFATAL) << "CbLibVideoUpdateMeshesCallback not set.";
-}
-void DefaultOnUpdateStereoMode(CbLibVideoStereoMode) {
-  LOG(DFATAL) << "CbLibVideoStereoMode not set.";
-}
-void DefaultOnUpdateRgbTextureId(int) {
-  LOG(DFATAL) << "CbLibVideoUpdateRgbTextureIdCallback not set.";
-}
+template <typename T, T* t, const char* ErrorMessage>
+struct CallbackUpdate;
+
+// Defines useful base:: wrappers for callback setter functions.
+template <typename Ret, typename... Args, CallbackSetter<Ret, Args...>* Setter,
+          const char* ErrorMessage>
+struct CallbackUpdate<CallbackSetter<Ret, Args...>, Setter, ErrorMessage> {
+  // Equivalent to the callback types defined in exported/video.h and
+  // exported/graphics.h but with the context bound.
+  using Callback = base::Callback<Ret(Args...)>;
+
+  static Ret DefaultImplementation(Args...) { LOG(WARNING) << ErrorMessage; }
+
+  struct LazyTraits {
+    static const bool kRegisterOnExit = true;
+    static const bool kAllowedToAccessOnNonjoinableThread = false;
+
+    static Callback* New(void* instance) {
+      return new (instance) Callback(base::Bind(DefaultImplementation));
+    }
+    static void Delete(Callback* instance) {
+      return base::DefaultLazyInstanceTraits<Callback>::Delete(instance);
+    }
+  };
+
+  // This provides a default warning function for the callbacks and allows to
+  // set them even before the external rasterizer is created.
+  using LazyCallback = base::LazyInstance<Callback, LazyTraits>;
+};
+
+// Creates an instance for the above template for a given callback setter
+// function, with an error message.
+// We must use 'extern' in here as otherwise we get compiler error C2970 when
+// using MSVC for compilation.
+#define INSTANCE_CALLBACK_UPDATE(instance_name, callback_setter)  \
+  extern const char kWarningMessageDidNotSet##instance_name[] =   \
+      #callback_setter                                            \
+      "was never called to set a callback, yet Cobalt is "        \
+      "attempting to call it.";                                   \
+  using instance_name =                                           \
+      CallbackUpdate<decltype(callback_setter), &callback_setter, \
+                     kWarningMessageDidNotSet##instance_name>;
+
+INSTANCE_CALLBACK_UPDATE(UpdateMeshes, CbLibVideoSetOnUpdateMeshes);
+INSTANCE_CALLBACK_UPDATE(UpdateStereoMode, CbLibVideoSetOnUpdateStereoMode);
+INSTANCE_CALLBACK_UPDATE(UpdateRgbTextureId, CbLibVideoSetOnUpdateRgbTextureId);
+INSTANCE_CALLBACK_UPDATE(UpdateProjectionType,
+                         CbLibVideoSetOnUpdateProjectionType);
+INSTANCE_CALLBACK_UPDATE(GraphicsContextCreated,
+                         CbLibGraphicsSetContextCreatedCallback);
+INSTANCE_CALLBACK_UPDATE(BeginRenderFrame,
+                         CbLibGraphicsSetBeginRenderFrameCallback);
+INSTANCE_CALLBACK_UPDATE(EndRenderFrame,
+                         CbLibGraphicsSetEndRenderFrameCallback);
+#undef INSTANCE_CALLBACK_UPDATE
+
+UpdateMeshes::LazyCallback g_update_meshes_callback = LAZY_INSTANCE_INITIALIZER;
+UpdateStereoMode::LazyCallback g_update_stereo_mode_callback =
+    LAZY_INSTANCE_INITIALIZER;
+UpdateRgbTextureId::LazyCallback g_update_rgb_texture_id_callback =
+    LAZY_INSTANCE_INITIALIZER;
+UpdateProjectionType::LazyCallback g_update_projection_type_callback =
+    LAZY_INSTANCE_INITIALIZER;
+GraphicsContextCreated::LazyCallback g_graphics_context_created_callback =
+    LAZY_INSTANCE_INITIALIZER;
+BeginRenderFrame::LazyCallback g_begin_render_frame_callback =
+    LAZY_INSTANCE_INITIALIZER;
+EndRenderFrame::LazyCallback g_end_render_frame_callback =
+    LAZY_INSTANCE_INITIALIZER;
 
 }  // namespace
 
@@ -86,31 +147,6 @@
 
   void MakeCurrent() { hardware_rasterizer_.MakeCurrent(); }
 
-  // Equivalent to the callback types defined in exported/video.h but with the
-  // context bound.
-  typedef base::Callback<void(CbLibVideoMesh, CbLibVideoMesh)>
-      UpdateMeshesCallback;
-  typedef base::Callback<void(CbLibVideoStereoMode)> UpdateStereoModeCallback;
-  typedef base::Callback<void(int)> UpdateRgbTextureIdCallback;
-  typedef base::Callback<void(CbLibVideoProjectionType projection_type)>
-      UpdateProjectionTypeCallback;
-
-  void SetUpdateMeshesCallback(UpdateMeshesCallback update_meshes) {
-    update_meshes_ = update_meshes;
-  }
-  void SetUpdateStereoModeCallback(
-      UpdateStereoModeCallback update_stereo_mode) {
-    update_stereo_mode_ = update_stereo_mode;
-  }
-  void SetUpdateRgbTextureIdCallback(
-      UpdateRgbTextureIdCallback update_rgb_texture_id) {
-    update_rgb_texture_id_ = update_rgb_texture_id;
-  }
-  void SetUpdateProjectionTypeCallback(
-      UpdateProjectionTypeCallback update_projection_type) {
-    update_projection_type_ = update_projection_type;
-  }
-
  private:
   void RenderOffscreenVideo(render_tree::FilterNode* map_to_mesh_filter_node);
 
@@ -136,11 +172,6 @@
   scoped_refptr<skia::HardwareMesh> right_eye_video_mesh_;
   render_tree::StereoMode video_stereo_mode_;
   int video_texture_rgb_;
-
-  UpdateMeshesCallback update_meshes_;
-  UpdateStereoModeCallback update_stereo_mode_;
-  UpdateRgbTextureIdCallback update_rgb_texture_id_;
-  UpdateProjectionTypeCallback update_projection_type_;
 };
 
 ExternalRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
@@ -171,20 +202,10 @@
       make_scoped_refptr(base::polymorphic_downcast<backend::RenderTargetEGL*>(
           main_offscreen_render_target_.get()))));
 
-  DCHECK(!g_external_rasterizer_impl_);
-  g_external_rasterizer_impl_ = this;
-
-  // Default parameter update callbacks.
-  update_projection_type_ = base::Bind(&DefaultOnUpdateProjectionType);
-  update_meshes_ = base::Bind(&DefaultOnUpdateMeshes);
-  update_stereo_mode_ = base::Bind(&DefaultOnUpdateStereoMode);
-  update_rgb_texture_id_ = base::Bind(&DefaultOnUpdateRgbTextureId);
-
-  CbLibOnGraphicsContextCreated();
+  g_graphics_context_created_callback.Get().Run();
 }
 
 ExternalRasterizer::Impl::~Impl() {
-  g_external_rasterizer_impl_ = NULL;
   graphics_context_->MakeCurrent();
 }
 
@@ -225,12 +246,12 @@
 
     if (video_projection_type_ != new_projection_type) {
       video_projection_type_ = new_projection_type;
-      update_projection_type_.Run(video_projection_type_);
+      g_update_projection_type_callback.Get().Run(video_projection_type_);
     }
 
     if (filter->stereo_mode() != video_stereo_mode_) {
       video_stereo_mode_ = filter->stereo_mode();
-      update_stereo_mode_.Run(
+      g_update_stereo_mode_callback.Get().Run(
           static_cast<CbLibVideoStereoMode>(video_stereo_mode_));
     }
 
@@ -271,9 +292,9 @@
           right_mesh.draw_mode = static_cast<CbLibVideoMeshDrawMode>(
               right_eye_video_mesh_->GetDrawMode());
           right_mesh.vertices = right_eye_video_mesh_->GetVertices();
-          update_meshes_.Run(left_mesh, right_mesh);
+          g_update_meshes_callback.Get().Run(left_mesh, right_mesh);
         } else {
-          update_meshes_.Run(left_mesh, left_mesh);
+          g_update_meshes_callback.Get().Run(left_mesh, left_mesh);
         }
       }
     }
@@ -283,7 +304,7 @@
   } else {
     if (video_projection_type_ != kCbLibVideoProjectionTypeNone) {
       video_projection_type_ = kCbLibVideoProjectionTypeNone;
-      update_projection_type_.Run(video_projection_type_);
+      g_update_projection_type_callback.Get().Run(video_projection_type_);
     }
   }
 
@@ -296,9 +317,9 @@
   // TODO: Allow clients to specify arbitrary subtrees to render into
   // different textures?
   const intptr_t texture_handle = main_texture_->GetPlatformHandle();
-  CbLibRenderFrame(texture_handle);
-
+  g_begin_render_frame_callback.Get().Run(texture_handle);
   graphics_context_->SwapBuffers(render_target_egl);
+  g_end_render_frame_callback.Get().Run();
 }
 
 render_tree::ResourceProvider* ExternalRasterizer::Impl::GetResourceProvider() {
@@ -368,7 +389,7 @@
     const intptr_t video_texture_handle = video_texture_->GetPlatformHandle();
     if (video_texture_rgb_ != video_texture_handle) {
       video_texture_rgb_ = video_texture_handle;
-      update_rgb_texture_id_.Run(video_texture_handle);
+      g_update_rgb_texture_id_callback.Get().Run(video_texture_handle);
     }
   }
 }
@@ -408,36 +429,49 @@
 
 void CbLibVideoSetOnUpdateMeshes(void* context,
                                  CbLibVideoUpdateMeshesCallback callback) {
-  if (g_external_rasterizer_impl_) {
-    g_external_rasterizer_impl_->SetUpdateMeshesCallback(
-        callback ? base::Bind(callback, context)
-                 : base::Bind(&DefaultOnUpdateMeshes));
-  }
+  g_update_meshes_callback.Get() =
+      callback ? base::Bind(callback, context)
+               : base::Bind(&UpdateMeshes::DefaultImplementation);
 }
 
 void CbLibVideoSetOnUpdateStereoMode(
     void* context, CbLibVideoUpdateStereoModeCallback callback) {
-  if (g_external_rasterizer_impl_) {
-    g_external_rasterizer_impl_->SetUpdateStereoModeCallback(
-        callback ? base::Bind(callback, context)
-                 : base::Bind(&DefaultOnUpdateStereoMode));
-  }
+  g_update_stereo_mode_callback.Get() =
+      callback ? base::Bind(callback, context)
+               : base::Bind(&UpdateStereoMode::DefaultImplementation);
 }
 
 void CbLibVideoSetOnUpdateRgbTextureId(
     void* context, CbLibVideoUpdateRgbTextureIdCallback callback) {
-  if (g_external_rasterizer_impl_) {
-    g_external_rasterizer_impl_->SetUpdateRgbTextureIdCallback(
-        callback ? base::Bind(callback, context)
-                 : base::Bind(&DefaultOnUpdateRgbTextureId));
-  }
+  g_update_rgb_texture_id_callback.Get() =
+      callback ? base::Bind(callback, context)
+               : base::Bind(&UpdateRgbTextureId::DefaultImplementation);
 }
 
 void CbLibVideoSetOnUpdateProjectionType(
     void* context, CbLibVideoUpdateProjectionTypeCallback callback) {
-  if (g_external_rasterizer_impl_) {
-    g_external_rasterizer_impl_->SetUpdateProjectionTypeCallback(
-        callback ? base::Bind(callback, context)
-                 : base::Bind(&DefaultOnUpdateProjectionType));
-  }
+  g_update_projection_type_callback.Get() =
+      callback ? base::Bind(callback, context)
+               : base::Bind(&UpdateProjectionType::DefaultImplementation);
+}
+
+void CbLibGraphicsSetContextCreatedCallback(
+    void* context, CbLibGraphicsContextCreatedCallback callback) {
+  g_graphics_context_created_callback.Get() =
+      callback ? base::Bind(callback, context)
+               : base::Bind(&GraphicsContextCreated::DefaultImplementation);
+}
+
+void CbLibGraphicsSetBeginRenderFrameCallback(
+    void* context, CbLibGraphicsBeginRenderFrameCallback callback) {
+  g_begin_render_frame_callback.Get() =
+      callback ? base::Bind(callback, context)
+               : base::Bind(&BeginRenderFrame::DefaultImplementation);
+}
+
+void CbLibGraphicsSetEndRenderFrameCallback(
+    void* context, CbLibGraphicsEndRenderFrameCallback callback) {
+  g_end_render_frame_callback.Get() =
+      callback ? base::Bind(callback, context)
+               : base::Bind(&EndRenderFrame::DefaultImplementation);
 }
diff --git a/src/cobalt/renderer/rasterizer/lib/imported/graphics.h b/src/cobalt/renderer/rasterizer/lib/imported/graphics.h
deleted file mode 100644
index cde0573..0000000
--- a/src/cobalt/renderer/rasterizer/lib/imported/graphics.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// All imported functions defined below MUST be implemented by client
-// applications.
-
-#ifndef COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
-#define COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
-
-#include "starboard/export.h"
-#include "starboard/types.h"
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Invoked from the rasterization thread after the GL context has been created.
-SB_IMPORT_PLATFORM void CbLibOnGraphicsContextCreated();
-
-// Invoked as often as the platform can swap buffers from the rasterization
-// thread. |render_tree_texture_handle| corresponds to a GLint texture ID for
-// the current RenderTree.
-SB_IMPORT_PLATFORM void CbLibRenderFrame(intptr_t render_tree_texture_handle);
-
-#ifdef __cplusplus
-}  // extern "C"
-#endif
-
-#endif  // COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
diff --git a/src/cobalt/renderer/rasterizer/lib/imported/graphics_stub.cc b/src/cobalt/renderer/rasterizer/lib/imported/graphics_stub.cc
deleted file mode 100644
index 5d9ece0..0000000
--- a/src/cobalt/renderer/rasterizer/lib/imported/graphics_stub.cc
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "cobalt/renderer/rasterizer/lib/imported/graphics.h"
-
-// Empty implementations so that 'lib' targets can be compiled into executables
-// by the builder without missing symbol definitions.
-void CbLibOnGraphicsContextCreated() {}
-void CbLibRenderFrame(intptr_t render_tree_texture_handle) {
-  (void)render_tree_texture_handle;
-}
diff --git a/src/cobalt/renderer/rasterizer/lib/lib.gyp b/src/cobalt/renderer/rasterizer/lib/lib.gyp
index 7a3914a..0b5a2c5 100644
--- a/src/cobalt/renderer/rasterizer/lib/lib.gyp
+++ b/src/cobalt/renderer/rasterizer/lib/lib.gyp
@@ -21,19 +21,9 @@
         '../../renderer_parameters_setup.gypi',
       ],
       'sources': [
-        'external_rasterizer.h',
         'external_rasterizer.cc',
         'renderer_module_default_options_lib.cc'
       ],
-      'all_dependent_settings': {
-        'target_conditions': [
-          ['_type=="executable" and _toolset=="target"', {
-            'sources': [
-              'imported/graphics_stub.cc',
-            ],
-          }],
-        ],
-      },
        'dependencies': [
          '<(DEPTH)/base/base.gyp:base',
          '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 7315496..a3f66eb 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -236,8 +236,10 @@
   RoundedCorner bottom_left(70, 80);
   scoped_ptr<RoundedCorners> rounded_corners(
       new RoundedCorners(top_left, top_right, bottom_right, bottom_left));
+  math::RectF rect(ScaleSize(output_surface_size(), 0.5f, 0.5f));
+  *rounded_corners = rounded_corners->Normalize(rect);
   TestTree(new RectNode(
-      RectF(ScaleSize(output_surface_size(), 0.5f, 0.5f)),
+      rect,
       scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0, 0.0, 0.0, 1))),
       rounded_corners.Pass()));
 }
@@ -3627,6 +3629,12 @@
 
 #endif  // defined(ENABLE_MAP_TO_MESH)
 
+TEST_F(PixelTest, DrawNullImage) {
+  // An ImageNode with no source is legal, though it should result in nothing
+  // being drawn.
+  TestTree(new ImageNode(NULL, math::RectF(output_surface_size())));
+}
+
 }  // namespace rasterizer
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/skia/gl_format_conversions.cc b/src/cobalt/renderer/rasterizer/skia/gl_format_conversions.cc
index 3a6f853..af1cad0 100644
--- a/src/cobalt/renderer/rasterizer/skia/gl_format_conversions.cc
+++ b/src/cobalt/renderer/rasterizer/skia/gl_format_conversions.cc
@@ -48,9 +48,19 @@
       return kRGBA_8888_GrPixelConfig;
     case GL_BGRA_EXT:
       return kBGRA_8888_GrPixelConfig;
+    // Note GL_ALPHA and GL_RED_EXT probably don't really work.
+    // They're only for use w/ SbDecodeTargets that are never drawn via skia
     case GL_ALPHA:
+#if defined(GL_RED_EXT)
+    case GL_RED_EXT:
+#endif
       return kAlpha_8_GrPixelConfig;
+    // Note GL_LUMINANCE_ALPHA and GL_RG_EXT probably don't really work.
+    // They're only for use w/ SbDecodeTargets that are never drawn via skia
     case GL_LUMINANCE_ALPHA:
+#if defined(GL_RG_EXT)
+    case GL_RG_EXT:
+#endif
       return kRGBA_8888_GrPixelConfig;
     default: { NOTREACHED() << "Unsupported GL format."; }
   }
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index c385813..576969c 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -170,7 +170,8 @@
 }
 #endif  // SB_API_VERSION < SB_DECODE_TARGET_PLANES_FOR_FORMAT
 
-uint32_t DecodeTargetFormatToGLFormat(SbDecodeTargetFormat format, int plane) {
+uint32_t DecodeTargetFormatToGLFormat(SbDecodeTargetFormat format, int plane,
+    const SbDecodeTargetInfoPlane* plane_info) {
   switch (format) {
     case kSbDecodeTargetFormat1PlaneRGBA:
 #if SB_API_VERSION >= SB_DECODE_TARGET_UYVY_SUPPORT_API_VERSION
@@ -184,6 +185,31 @@
     } break;
     case kSbDecodeTargetFormat2PlaneYUVNV12: {
       DCHECK_LT(plane, 2);
+#if SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
+      // If this DCHECK fires, please set gl_texture_format, introduced
+      // in Starboard SB_DECODE_TARGET_FORMAT_VERSION.
+      //
+      // You probably want to set it to GL_ALPHA on plane 0 (luma) and
+      // GL_LUMINANCE_ALPHA on plane 1 (chroma), which was the default before.
+      DCHECK_NE(plane_info->gl_texture_format, 0);
+      switch (plane_info->gl_texture_format) {
+        case GL_ALPHA:
+        case GL_LUMINANCE_ALPHA:
+#if defined(GL_RED_EXT)
+        case GL_RED_EXT:
+#endif
+#if defined(GL_RG_EXT)
+        case GL_RG_EXT:
+#endif
+          return plane_info->gl_texture_format;
+        default:
+          // gl_texture_format is either unassigned or assigned to
+          // an invalud value. Please see comment above for
+          // gl_texture_format change, introduced in Starboard 7.
+          CHECK(false);
+          return 0;
+      }
+#else  // SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
       switch (plane) {
         case 0:
           return GL_ALPHA;
@@ -193,6 +219,7 @@
           NOTREACHED();
           return GL_RGBA;
       }
+#endif  // SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
     } break;
     case kSbDecodeTargetFormat3PlaneYUVI420: {
       DCHECK_LT(plane, 3);
@@ -280,7 +307,7 @@
           plane.content_region.bottom - plane.content_region.top));
     }
 
-    uint32_t gl_format = DecodeTargetFormatToGLFormat(info.format, i);
+    uint32_t gl_format = DecodeTargetFormatToGLFormat(info.format, i, &plane);
 
     scoped_ptr<backend::TextureEGL> texture(new backend::TextureEGL(
         cobalt_context_, gl_handle, math::Size(plane.width, plane.height),
diff --git a/src/cobalt/renderer/rasterizer/testdata/DrawNullImage-expected.png b/src/cobalt/renderer/rasterizer/testdata/DrawNullImage-expected.png
new file mode 100644
index 0000000..7f0bfcf
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/DrawNullImage-expected.png
Binary files differ
diff --git a/src/cobalt/samples/gn_tutorial/hello.cc b/src/cobalt/samples/gn_tutorial/hello.cc
new file mode 100644
index 0000000..34732fc
--- /dev/null
+++ b/src/cobalt/samples/gn_tutorial/hello.cc
@@ -0,0 +1,17 @@
+// Copyright 2013 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 "cobalt/samples/gn_tutorial/hello.h"
+
+#include <stdio.h>
+
+void Hello(const char* who) {
+  printf("Hello, %s.\n", who);
+}
+
+#if defined(TWO_PEOPLE)
+void Hello(const char* one, const char* two) {
+  printf("Hello, %s and %s.\n", one, two);
+}
+#endif
diff --git a/src/cobalt/samples/gn_tutorial/hello.h b/src/cobalt/samples/gn_tutorial/hello.h
new file mode 100644
index 0000000..812fa54
--- /dev/null
+++ b/src/cobalt/samples/gn_tutorial/hello.h
@@ -0,0 +1,14 @@
+// Copyright 2013 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 COBALT_SAMPLES_GN_TUTORIAL_HELLO_H_
+#define COBALT_SAMPLES_GN_TUTORIAL_HELLO_H_
+
+void Hello(const char* who);
+
+#if defined(TWO_PEOPLE)
+void Hello(const char* one, const char* two);
+#endif
+
+#endif  // COBALT_SAMPLES_GN_TUTORIAL_HELLO_H_
diff --git a/src/cobalt/samples/gn_tutorial/hello_world.cc b/src/cobalt/samples/gn_tutorial/hello_world.cc
new file mode 100644
index 0000000..84ffc3f
--- /dev/null
+++ b/src/cobalt/samples/gn_tutorial/hello_world.cc
@@ -0,0 +1,10 @@
+// Copyright 2013 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 <stdio.h>
+
+int main() {
+  printf("Hello, world.\n");
+  return 0;
+}
diff --git a/src/cobalt/samples/gn_tutorial/say_hello.cc b/src/cobalt/samples/gn_tutorial/say_hello.cc
new file mode 100644
index 0000000..c1bea2c
--- /dev/null
+++ b/src/cobalt/samples/gn_tutorial/say_hello.cc
@@ -0,0 +1,14 @@
+// Copyright 2013 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 "cobalt/samples/gn_tutorial/hello.h"
+
+int main() {
+#if defined(TWO_PEOPLE)
+  Hello("Bill", "Joy");
+#else
+  Hello("everyone");
+#endif
+  return 0;
+}
diff --git a/src/cobalt/script/value_handle.h b/src/cobalt/script/value_handle.h
index d00d7d0..373b15f 100644
--- a/src/cobalt/script/value_handle.h
+++ b/src/cobalt/script/value_handle.h
@@ -15,7 +15,6 @@
 #define COBALT_SCRIPT_VALUE_HANDLE_H_
 
 #include "cobalt/script/script_value.h"
-#include "cobalt/script/value_handle.h"
 
 namespace cobalt {
 namespace script {
diff --git a/src/cobalt/storage/savegame.h b/src/cobalt/storage/savegame.h
index d635403..fa7e88f 100644
--- a/src/cobalt/storage/savegame.h
+++ b/src/cobalt/storage/savegame.h
@@ -21,6 +21,7 @@
 #include "base/callback.h"
 #include "base/file_path.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
 #include "base/threading/thread_checker.h"
 #include "sql/connection.h"
 
@@ -39,12 +40,19 @@
   typedef scoped_ptr<Savegame>(*Factory)(const Options& options);
 
   struct Options {
-    Options() : factory(&Create), delete_on_destruction(false) {}
+    Options()
+        : factory(&Create),
+          fallback_to_default_id(false),
+          delete_on_destruction(false) {}
     scoped_ptr<Savegame> CreateSavegame() { return factory(*this); }
 
     // Factory method for constructing a Savegame instance.
     // Defaults to Savegame::Create() but can be overriden for tests.
     Savegame::Factory factory;
+    // The unique savegame ID for this Savegame, if any.
+    base::optional<std::string> id;
+    // Whether to fallback to the default ID if data for this ID doesn't exist.
+    bool fallback_to_default_id;
     // File path the Savegame should read/write from, rather than its default.
     std::string path_override;
     // Delete the savegame file when the Savegame object goes out of scope.
diff --git a/src/cobalt/storage/savegame_file.cc b/src/cobalt/storage/savegame_file.cc
deleted file mode 100644
index 7bc4667..0000000
--- a/src/cobalt/storage/savegame_file.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/storage/savegame.h"
-
-#include <algorithm>
-
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-
-namespace cobalt {
-namespace storage {
-
-namespace {
-const char kDefaultSavegamePath[] = ".cobalt.sav";
-
-FilePath GetSavegamePath() {
-  FilePath home_dir;
-  bool ok = PathService::Get(base::DIR_HOME, &home_dir);
-  DCHECK_EQ(true, ok);
-  return home_dir.Append(kDefaultSavegamePath);
-}
-
-}  // namespace
-
-// Savegame implementation that writes to a regular file, using file_util.
-class SavegameFile : public Savegame {
- public:
-  explicit SavegameFile(const Options& options);
-  ~SavegameFile() OVERRIDE;
-  bool PlatformRead(size_t max_to_read, ByteVector* bytes) OVERRIDE;
-  bool PlatformWrite(const ByteVector& bytes) OVERRIDE;
-  bool PlatformDelete() OVERRIDE;
-
- private:
-  FilePath savegame_path_;
-};
-
-SavegameFile::SavegameFile(const Options& options) : Savegame(options) {
-  if (options.path_override.length() > 0) {
-    savegame_path_ = FilePath(options.path_override);
-  } else {
-    savegame_path_ = GetSavegamePath();
-  }
-}
-
-SavegameFile::~SavegameFile() {
-  if (options_.delete_on_destruction) {
-    Delete();
-  }
-}
-
-bool SavegameFile::PlatformRead(ByteVector* bytes_ptr, size_t max_to_read) {
-  if (!file_util::PathExists(savegame_path_)) {
-    return false;
-  }
-
-  int64 file_size;
-  if (!file_util::GetFileSize(savegame_path_, &file_size)) {
-    DLOG(WARNING) << "GetFileSize of " << savegame_path_.value() << " failed";
-    return false;
-  }
-
-  if (max_to_read < static_cast<size_t>(file_size) {
-    DLOG(WARNING) << "Savegame larger than max allowed size";
-    return false;
-  }
-
-  ByteVector& bytes = *bytes_ptr;
-  bytes.resize(static_cast<size_t>(file_size));
-  if (!bytes.empty()) {
-    int bytes_read =
-        file_util::ReadFile(savegame_path_, reinterpret_cast<char*>(&bytes[0]),
-                            static_cast<int>(file_size));
-    bytes.resize(static_cast<size_t>(std::max(0, bytes_read)));
-    return bytes_read == file_size;
-  } else {
-    return true;
-  }
-}
-
-bool SavegameFile::PlatformWrite(const ByteVector& bytes) {
-  int byte_count = static_cast<int>(bytes.size());
-  int bytes_written = file_util::WriteFile(
-      savegame_path_, reinterpret_cast<const char*>(&bytes[0]), byte_count);
-  return bytes_written == byte_count;
-}
-
-bool SavegameFile::PlatformDelete() {
-  if (file_util::PathExists(savegame_path_)) {
-    return file_util::Delete(savegame_path_, false /* recursive */);
-  } else {
-    return false;
-  }
-}
-
-// static
-scoped_ptr<Savegame> Savegame::Create(const Options& options) {
-  scoped_ptr<Savegame> savegame(new SavegameFile(options));
-  return savegame.Pass();
-}
-
-}  // namespace storage
-}  // namespace cobalt
diff --git a/src/cobalt/storage/savegame_starboard.cc b/src/cobalt/storage/savegame_starboard.cc
index 8201b17..da62a3b 100644
--- a/src/cobalt/storage/savegame_starboard.cc
+++ b/src/cobalt/storage/savegame_starboard.cc
@@ -19,12 +19,82 @@
 #include "base/file_util.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
 #include "base/path_service.h"
 #include "starboard/storage.h"
 #include "starboard/user.h"
 
 namespace cobalt {
 namespace storage {
+namespace {
+
+// An arbitrary max size for the save game file so that, for example, a corrupt
+// filesystem cannot cause us to allocate a fatally large memory buffer.
+size_t kMaxSaveGameSizeBytes = 4 * 1024 * 1024;
+
+bool ReadRecord(Savegame::ByteVector* bytes_ptr, size_t max_to_read,
+                const scoped_ptr<starboard::StorageRecord>& record) {
+  if (!record->IsValid()) {
+    DLOG(WARNING) << __FUNCTION__ << ": Invalid StorageRecord";
+    return false;
+  }
+
+  int64_t size = record->GetSize();
+  if (size < 0) {
+    DLOG(WARNING) << "StorageRecord::GetSize failed";
+    return false;
+  }
+
+  if (static_cast<size_t>(size) > max_to_read) {
+    DLOG(WARNING) << "Savegame larger than max allowed size";
+    return false;
+  }
+
+  Savegame::ByteVector& bytes = *bytes_ptr;
+  bytes.resize(static_cast<size_t>(size));
+  if (bytes.empty()) {
+    return true;
+  }
+
+  int64_t bytes_read =
+      record->Read(reinterpret_cast<char*>(bytes.data()), size);
+  bytes.resize(
+      static_cast<size_t>(std::max(static_cast<int64_t>(0), bytes_read)));
+  return bytes_read == size;
+}
+
+bool WriteRecord(const scoped_ptr<starboard::StorageRecord>& record,
+                 const Savegame::ByteVector& bytes) {
+  int64_t byte_count = static_cast<int64_t>(bytes.size());
+  return record->Write(reinterpret_cast<const char*>(bytes.data()), byte_count);
+}
+
+scoped_ptr<starboard::StorageRecord> CreateRecord(
+    const base::optional<std::string>& id) {
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  if (id) {
+    return make_scoped_ptr(new starboard::StorageRecord(id->c_str()));
+  }
+#else  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  UNREFERENCED_PARAMETER(id);
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  return make_scoped_ptr(new starboard::StorageRecord());
+}
+
+bool EnsureRecord(scoped_ptr<starboard::StorageRecord>* record,
+                  const base::optional<std::string>& id) {
+  if (!(*record) || !(*record)->IsValid()) {
+    // Might have been deleted, so we'll create a new one.
+    (*record) = CreateRecord(id);
+  }
+
+  if (!(*record)->IsValid()) {
+    DLOG(WARNING) << __FUNCTION__ << ": Invalid StorageRecord: Signed in?";
+    return false;
+  }
+
+  return true;
+}
 
 // Savegame implementation that writes to the Starboard Storage API.
 class SavegameStarboard : public Savegame {
@@ -36,12 +106,14 @@
   bool PlatformDelete() OVERRIDE;
 
  private:
+  bool MigrateFromFallback();
+
   scoped_ptr<starboard::StorageRecord> record_;
 };
 
 SavegameStarboard::SavegameStarboard(const Options& options)
     : Savegame(options) {
-  record_.reset(new starboard::StorageRecord());
+  EnsureRecord(&record_, options_.id);
 }
 
 SavegameStarboard::~SavegameStarboard() {
@@ -52,47 +124,30 @@
 
 bool SavegameStarboard::PlatformRead(ByteVector* bytes_ptr,
                                      size_t max_to_read) {
-  if (!record_->IsValid()) {
-    DLOG(WARNING) << __FUNCTION__ << ": Invalid StorageRecord";
-    return false;
-  }
-
-  int64_t size = record_->GetSize();
-  if (size < 0) {
-    DLOG(WARNING) << "StorageRecord::GetSize failed";
-    return false;
-  }
-
-  if (static_cast<size_t>(size) > max_to_read) {
-    DLOG(WARNING) << "Savegame larger than max allowed size";
-    return false;
-  }
-
-  ByteVector& bytes = *bytes_ptr;
-  bytes.resize(static_cast<size_t>(size));
-  if (bytes.empty()) {
+  bool success = ReadRecord(bytes_ptr, max_to_read, record_);
+  if (success && !bytes_ptr->empty()) {
     return true;
   }
 
-  int64_t bytes_read = record_->Read(reinterpret_cast<char*>(&bytes[0]), size);
-  bytes.resize(
-      static_cast<size_t>(std::max(static_cast<int64_t>(0), bytes_read)));
-  return bytes_read == size;
-}
-
-bool SavegameStarboard::PlatformWrite(const ByteVector& bytes) {
-  if (!record_->IsValid()) {
-    // Might have been deleted, so we'll create a new one.
-    record_.reset(new starboard::StorageRecord());
-  }
-
-  if (!record_->IsValid()) {
-    DLOG(WARNING) << __FUNCTION__ << ": Invalid StorageRecord: Signed in?";
+  if (!options_.fallback_to_default_id) {
     return false;
   }
 
-  int64_t byte_count = static_cast<int64_t>(bytes.size());
-  return record_->Write(reinterpret_cast<const char*>(&bytes[0]), byte_count);
+  if (!MigrateFromFallback()) {
+    DLOG(WARNING) << __FUNCTION__ << ": Migration Failed";
+    return false;
+  }
+
+  // Now read the migrated data.
+  return ReadRecord(bytes_ptr, max_to_read, record_);
+}
+
+bool SavegameStarboard::PlatformWrite(const ByteVector& bytes) {
+  if (!EnsureRecord(&record_, options_.id)) {
+    return false;
+  }
+
+  return WriteRecord(record_, bytes);
 }
 
 bool SavegameStarboard::PlatformDelete() {
@@ -104,10 +159,54 @@
   return record_->Delete();
 }
 
+bool SavegameStarboard::MigrateFromFallback() {
+  ByteVector buffer;
+  if (!EnsureRecord(&record_, options_.id)) {
+    DLOG(WARNING) << __FUNCTION__ << ": "
+                  << "Failed to ensure record for ID: " << options_.id;
+    return false;
+  }
+
+  scoped_ptr<starboard::StorageRecord> fallback_record;
+  if (!EnsureRecord(&fallback_record, base::nullopt)) {
+    DLOG(WARNING) << __FUNCTION__ << ": "
+                  << "Failed to open default record.";
+    return false;
+  }
+
+  if (!ReadRecord(&buffer, kMaxSaveGameSizeBytes, fallback_record)) {
+    DLOG(WARNING) << __FUNCTION__ << ": "
+                  << "Failed to read default record.";
+    return false;
+  }
+
+  if (buffer.size() == 0) {
+    // We migrated nothing successfully.
+    return true;
+  }
+
+  if (!WriteRecord(record_, buffer)) {
+    DLOG(WARNING) << __FUNCTION__ << ": "
+                  << "Failed to write record for ID: " << options_.id;
+    return false;
+  }
+
+  // Flush the migrated record by closing and reopening it.
+  record_.reset();
+  if (!EnsureRecord(&record_, options_.id)) {
+    return false;
+  }
+
+  // Now cleanup the fallback record.
+  fallback_record->Delete();
+  return true;
+}
+
+}  // namespace
+
 // static
 scoped_ptr<Savegame> Savegame::Create(const Options& options) {
-  scoped_ptr<Savegame> savegame(new SavegameStarboard(options));
-  return savegame.Pass();
+  return make_scoped_ptr(new SavegameStarboard(options)).PassAs<Savegame>();
 }
 
 }  // namespace storage
diff --git a/src/glimp/stub/egl/display_impl.cc b/src/glimp/stub/egl/display_impl.cc
index 6b49981..447477c 100644
--- a/src/glimp/stub/egl/display_impl.cc
+++ b/src/glimp/stub/egl/display_impl.cc
@@ -15,6 +15,7 @@
  */
 
 #include "glimp/stub/egl/display_impl.h"
+
 #include "glimp/stub/egl/pbuffer_surface_impl.h"
 #include "glimp/stub/egl/window_surface_impl.h"
 #include "glimp/stub/gles/context_impl.h"
@@ -65,8 +66,8 @@
   (*config)[EGL_LUMINANCE_SIZE] = 0;
   (*config)[EGL_STENCIL_SIZE] = 0;
   (*config)[EGL_COLOR_BUFFER_TYPE] = EGL_RGB_BUFFER;
-  (*config)[EGL_CONFORMANT] = EGL_OPENGL_ES2_BIT;
-  (*config)[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT;
+  (*config)[EGL_CONFORMANT] = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
+  (*config)[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
   (*config)[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
   (*config)[EGL_BIND_TO_TEXTURE_RGBA] = EGL_TRUE;
 
diff --git a/src/starboard/BUILD.gn b/src/starboard/BUILD.gn
new file mode 100644
index 0000000..8adaf0c
--- /dev/null
+++ b/src/starboard/BUILD.gn
@@ -0,0 +1,97 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+group("all") {
+  testonly = true
+  deps = [
+    # TODO: uncomment as each of these are implemented
+    # "//starboard/client_porting/eztime:all",
+    # "//starboard/client_porting/icu_init:all",
+    # "//starboard/client_porting/poem:all",
+    "//starboard/examples",
+    # "//starboard/nplb/blitter_pixel_tests:all",
+    "//starboard/nplb:all",
+    ":starboard",
+  ]
+
+  has_platform_tests =
+      exec_script("//build/dir_exists.py",
+                  [ rebase_path("//$starboard_path/tests", root_build_dir) ],
+                  "string") == "True"
+  if (has_platform_tests) {
+    deps += [ "//$starboard_path/tests" ]
+  }
+}
+
+source_set("starboard") {
+  sources = [
+    "atomic.h",
+    "audio_sink.h",
+    "blitter.h",
+    "byte_swap.h",
+    "character.h",
+    "condition_variable.h",
+    "configuration.h",
+    "decode_target.h",
+    "directory.h",
+    "double.h",
+    "drm.h",
+    "event.h",
+    "export.h",
+    "file.h",
+    "input.h",
+    "key.h",
+    "log.h",
+    "media.h",
+    "memory.h",
+    "microphone.h",
+    "mutex.h",
+    "once.h",
+    "player.h",
+    "queue.h",
+    "socket.h",
+    "socket_waiter.h",
+    "spin_lock.h",
+    "storage.h",
+    "string.h",
+    "system.h",
+    "thread.h",
+    "thread_types.h",
+    "time.h",
+    "time_zone.h",
+    "types.h",
+    "user.h",
+    "window.h",
+    "//starboard/shared/media_session/playback_state.h",
+  ]
+
+  private_sources = exec_script("tools/find_private_files.py",
+                                [
+                                  rebase_path("//", root_build_dir),
+                                  "*.h",
+                                ],
+                                "list lines",
+                                [ "//starboard/private" ])
+  sources += rebase_path(private_sources, ".", root_build_dir)
+
+  deps = [
+    "//starboard/common",
+  ]
+
+  public_deps = [
+    "//$starboard_path:starboard_platform",
+  ]
+
+  # TODO: handle shared_main_adapter.cc
+}
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index acbe7b4..6bcc0d7 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -8,12 +8,18 @@
 
 ## Version 6
 
-### Introduce pointer (mouse) input support.
+### Named `SbStorageRecord`s
+
+This extends the `SbStorage` interface with the ability to open named
+`SbStorageRecord`s. Calling `SbStorageOpenRecord` and `SbStorageDeleteRecord`
+with a `NULL` `name` parameter provides access to the old "default" record.
+
+### Introduce pointer (mouse) input support
 
 This extends the `SbInput` interface with some enum values and data members to
 allow mouse, wheel, and more generic pointer input.
 
-### Flexible audio specific config.
+### Flexible audio specific config
 
 `SbMediaAudioHeader::audio_specific_config` will be a pointer instead of an
 array.
@@ -26,7 +32,7 @@
 Changes `SbTimeZoneGetName()` to be more flexible in what it is allowed to
 return.
 
-### SbDecodeTargetNumberOfPlanesForFormat
+### `SbDecodeTargetNumberOfPlanesForFormat`
 
 Adds the convenience inline function, SbDecodeTargetNumberOfPlanesForFormat() to
 `starboard/decode_target.h`.
@@ -40,7 +46,7 @@
 
 Removes `SbSystemPlatformErrorType` values specific to user status.
 
-### SbDecodeTarget support for the UYVY (i.e. YUV 422) format
+### `SbDecodeTarget` support for the UYVY (i.e. YUV 422) format
 
 Add support for UYVY decode targets (e.g. YUV 422) via the
 `kSbDecodeTargetFormat1PlaneUYVY` enum.
@@ -50,15 +56,19 @@
 This adds SbKey codes for the colored keys found on most contemporary TV
 remotes.
 
-### kSbEventTypeLowMemory
+### `kSbEventTypeLowMemory`
 
 Adds a new event type -- `kSbEventTypeLowMemory` -- to allow a platform to
 signal that the application may soon be terminated due to low memory
 availability.
 
-### Interface change to SbPlayerWriteSample()
+### Interface change to `SbPlayerWriteSample()`
 `const` is added to `sample_buffers` and `sample_buffer_sizes` parameters.
 
+### Support key status change
+Add `key_statuses_changed_callback` parameter to `SbDrmCreateSystem()` to
+support MediaKeySession::keyStatuses and MediaKeySession::onkeystatuseschange.
+
 ## Version 5
 
 ### Add Speech Recognizer API
diff --git a/src/starboard/README.md b/src/starboard/README.md
index 1487a1d..9f2d47f 100644
--- a/src/starboard/README.md
+++ b/src/starboard/README.md
@@ -5,6 +5,15 @@
 that it does not.
 
 
+## GN Migration Notice
+
+Cobalt and Starboard are currently migrating from the GYP build system to the GN
+build system. This readme contains instructions for both systems. As of now, GN
+is not ready for general use for Cobalt/Starboard. If you are not a core Cobalt
+developer, you should probably ignore the sections titled "GN Instructions" for
+now.
+
+
 ## Interesting Source Locations
 
 All source locations are specified relative to `src/starboard/` (this directory).
@@ -23,17 +32,6 @@
     platforms that share some facet of their OS API.
 
 
-## Building with Starboard
-
-Follow the Cobalt instructions, except when invoking gyp:
-
-    $ cobalt/build/gyp_cobalt -C debug linux-x64x11
-
-and when invoking ninja:
-
-    $ ninja -C out/linux-x64x11_debug cobalt
-
-
 ## Quick Guide to Starting a Port
 
 ### I. Enumerate and Name Your Platform Configurations
@@ -113,6 +111,31 @@
 
 And so on.
 
+#### GN Instructions
+
+Each `<binary-variant>/` directory must have at least `configuration_public.h`,
+`atomic_public.h`, `thread_types_public.h`, `BUILD.gn`, `configuration.gni`,
+and `buildconfig.gni`.
+
+In the BobCo's BobBox example, we would see a directory tree like:
+
+  * `src/third_party/starboard/bobbox/`
+      * `shared/`
+      * `mipseb/`
+          * `buildconfig.gni`
+          * `configuration.gni`
+          * `atomic_public.h`
+          * `BUILD.gn`
+          * `configuration_public.h`
+          * `thread_types_public.h`
+      * `mipsel/`
+          * `buildconfig.gni`
+          * `configuration.gni`
+          * `atomic_public.h`
+          * `BUILD.gn`
+          * `configuration_public.h`
+          * `thread_types_public.h`
+
 
 ### III. Base Your Port on a Reference Port
 
@@ -160,6 +183,26 @@
 `src/third_party/starboard/bobbox/mipseb/gyp_configuration.py`, it would choose
 the platform configuration name `bobbox-mipseb`.)
 
+#### GN Instructions
+
+Update `<binary-variant>/BUILD.gn` to point at all the source files that you
+want to build as your new Starboard implementation. The `//` expression in GN
+refers to the `src/` directory of your source tree. Otherwise, files are assumed
+to be relative to the directory the `BUILD.gn` or `.gni` file is in. The
+`BUILD.gn` file contains absolute paths, so the paths will still be valid if you
+copy it to a new directory. You can then incrementally replace files with new
+implementations as necessary.
+
+In order to use a new platform configuration in a build, you need to ensure that
+you have a `BUILD.gn`, `configuration.gni`, and `buildconfig.gni` in their
+own directory for each binary variant, plus the header files
+`configuration_public.h`, `atomic_public.h`, and `thread_types_public.h`. The GN
+build will scan your directories for these files, and then calculate a port name
+based on the directories between `src/third_party/starboard` and your
+`configuration.gni` files. (e.g. for
+`src/third_party/starboard/bobbox/mipseb/configuration.gni`, it would choose the
+platform configuration name `bobbox-mipseb`.)
+
 
 ### IV. A New Port, Step-by-Step
 
@@ -179,7 +222,7 @@
          toolchain analogs for the toolchain for your platform.
   1. In `gyp_configuration.gypi`
       1. Update the names of the configurations and the default_configuration to
-         be `<platform-configuation>_<build-type>` for your platform
+         be `<platform-configuration>_<build-type>` for your platform
          configuration name, where `<build-type>` is one of `debug`, `devel`,
          `qa`, `gold`.
       1. Update your platform variables.
@@ -213,6 +256,46 @@
 This will attempt to build the "No Platform Left Behind" test suite with your
 new Starboard implementation, and you are ready to start porting!
 
+#### GN Instructions
+
+Follow the above list, except:
+
+  1. Ignore the steps about `gyp_configuration.py` and `gyp_configuration.gypi`.
+  1. Update `BUILD.gn` instead of `starboard_platform.gyp`.
+  1. Also in `BUILD.gn`:
+      1. Update your toolchain command-line flags and libraries, for all
+         configurations as well as for each individual configuration.
+      1. Implement generic compiler configs such as `sb_pedantic_warnings`
+         and `rtti`.
+      1. If you're not using a predefined toolchain, define one.
+  1. In `buildconfig.gni`:
+      1. If your platform is Linux-based, set `target_os_ = "linux"`.
+      1. Set `target_cpu_` to your target architecture (e.g. `arm`, `ppc`,
+         `x64`, `x86`, `mips`).
+      1. Set the target and host toolchains. If you defined a target
+         toolchain in `BUILD.gn`, you'll want to set it to that.
+  1. In `configuration.gni`, set platform-specific defaults for any
+     variables that should be overriden for your Starboard platform.  You
+     can find a list of such variables at `//cobalt/build/config/base.gni`
+     and `//starboard/build/config/base.gni`.
+
+
+You should now be able to run GN with your new port. From your `src/` directory:
+
+    $ gn args out/bobbox-mipseb_debug
+
+An editor will open up. Type into the editor:
+
+    cobalt_config = "debug"
+    target_platform = "bobbox-mipseb"
+
+Save and close the editor. Then run
+
+    $ ninja -C out/bobbox-mipseb_debug nplb
+
+This will attempt to build the "No Platform Left Behind" test suite with your
+new Starboard implementation, and you are ready to start porting!
+
 
 ## Suggested Implementation Order
 
diff --git a/src/starboard/build/config/BUILD.gn b/src/starboard/build/config/BUILD.gn
new file mode 100644
index 0000000..73454f9
--- /dev/null
+++ b/src/starboard/build/config/BUILD.gn
@@ -0,0 +1,91 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO: change to //starboard eventually
+import("//cobalt/build/config/base.gni")
+import("//starboard/build/delegated_config.gni")
+
+# =============================================================================
+# COMPILER DEFAULTS
+# =============================================================================
+
+# TODO: only set these defines on the files that actually need them
+config("compiler_defaults") {
+  # The canonical way to detect Starboard is #if defined(STARBOARD). This
+  # should be defined both when building Starboard itself, and when building
+  # any other source file in a Starboard-based project.
+  defines = [ "STARBOARD" ]
+
+  if (gl_type == "none") {
+    defines += [ "SB_GYP_GL_TYPE_IS_NONE=1" ]
+  } else {
+    defines += [ "SB_GYP_GL_TYPE_IS_NONE=0" ]
+  }
+
+  if (abort_on_allocation_failure) {
+    defines += [ "SB_ABORT_ON_ALLOCATION_FAILURE" ]
+  }
+
+  if (sb_allow_memory_tracking) {
+    defines += [ "STARBOARD_ALLOWS_MEMORY_TRACKING" ]
+  }
+
+  if (sb_enable_lib) {
+    defines += [ "SB_IS_LIBRARY=1" ]
+  }
+
+  # There doesn't appear to be any way to use the C preprocessor to do
+  # string concatenation with the / character. This prevents us from using
+  # the preprocessor to assemble an include file path, so we have to do
+  # the concatenation here in GN.
+  # http://stackoverflow.com/questions/29601786/c-preprocessor-building-a-path-string
+  defines += [
+    "STARBOARD_ATOMIC_INCLUDE=\"$starboard_path/atomic_public.h\"",
+    "STARBOARD_CONFIGURATION_INCLUDE=\"$starboard_path/configuration_public.h\"",
+    "STARBOARD_THREAD_TYPES_INCLUDE=\"$starboard_path/thread_types_public.h\"",
+  ]
+
+  # TODO: find a way to remove this dependence on Cobalt
+  if (sb_media_platform == "starboard") {
+    defines += [ "SB_GYP_CAN_MEDIA_USE_STARBOARD_PIPELINE=1" ]
+  } else {
+    defines += [ "SB_GYP_CAN_MEDIA_USE_STARBOARD_PIPELINE=0" ]
+  }
+}
+
+# =============================================================================
+# DELEGATED CONFIGS
+# =============================================================================
+
+# Enables pedantic levels of warnings for the current toolchain.
+delegated_config("pedantic_warnings") {
+  path = "//$starboard_path"
+  generate_default = false
+}
+
+# Controls the optimization level
+delegated_config("optimizations") {
+  path = "//$starboard_path"
+  prefixes = [ "no", "debuggable", "full" ]
+}
+
+# Enables/disables rtti
+delegated_config("rtti") {
+  path = "//$starboard_path"
+}
+
+# Enables the exit-time-destructors warning.
+config("wexit_time_destructors") {
+  configs = [ "//$starboard_path:wexit_time_destructors" ]
+}
diff --git a/src/starboard/build/config/base.gni b/src/starboard/build/config/base.gni
new file mode 100644
index 0000000..7599e71
--- /dev/null
+++ b/src/starboard/build/config/base.gni
@@ -0,0 +1,81 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Import platform-specific defaults
+import("//$starboard_path/configuration.gni")
+
+# Starboard variables.
+
+# Enables embedding Starboard as a shared library within another app. This
+# requires a 'lib' starboard implementation for the corresponding platform.
+if (!defined(sb_enable_lib)) {
+  sb_enable_lib = false
+}
+
+# Directory path to static contents.
+if (!defined(sb_static_contents_output_base_dir)) {
+  sb_static_contents_output_base_dir = "$root_out_dir/content"
+}
+
+# Directory path to static contents' data.
+if (!defined(sb_static_contents_output_data_dir)) {
+  sb_static_contents_output_data_dir = "$root_out_dir/content/data"
+}
+
+# Halt execution on failure to allocate memory.
+if (!defined(abort_on_allocation_failure)) {
+  abort_on_allocation_failure = true
+}
+
+# The source of EGL and GLES headers and libraries.
+# Valid values (case and everything sensitive!):
+#   "none"   - No EGL + GLES implementation is available on this platform.
+#   "system_gles3" - Use the system implementation of EGL + GLES3. The
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "system_gles2" - Use the system implementation of EGL + GLES2. The
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "glimp"  - Cobalt's own EGL + GLES2 implementation. This requires a
+#              valid Glimp implementation for the platform.
+#   "angle"  - A DirectX-to-OpenGL adaptation layer. This requires a valid
+#              ANGLE implementation for the platform.
+# Choosing an unsupported value will result in a GN error:
+#   "Unresolved dependencies: //cobalt/renderer/egl_and_gles:<gl_type>"
+if (!defined(gl_type)) {
+  gl_type = "system_gles2"
+}
+
+# TODO: Temporary indicator for Tizen - should eventually move to feature defines.
+if (!defined(is_tizen_os)) {
+  is_tizen_os = false
+}
+
+# The event polling mechanism available on this platform to support libevent.
+# Platforms may redefine to "poll" if necessary.
+# Other mechanisms, e.g. devpoll, kqueue, select, are not yet supported.
+if (!defined(sb_libevent_method)) {
+  sb_libevent_method = "epoll"
+}
+
+# Whether to allow memory tracking
+if (!defined(sb_allow_memory_tracking)) {
+  sb_allow_memory_tracking = (cobalt_config != "gold" && !sb_enable_lib)
+}
+
+
+# The system root for cross-compiles. Default: none.
+if (!defined(sysroot)) {
+  sysroot = ""
+}
diff --git a/src/starboard/build/config/fastbuild.gni b/src/starboard/build/config/fastbuild.gni
new file mode 100644
index 0000000..b1fe040
--- /dev/null
+++ b/src/starboard/build/config/fastbuild.gni
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # If set to true, omits the generation of debugging symbols
+  cobalt_use_fastbuild = false
+}
diff --git a/src/starboard/build/config/sanitizers.gni b/src/starboard/build/config/sanitizers.gni
new file mode 100644
index 0000000..a1e9817
--- /dev/null
+++ b/src/starboard/build/config/sanitizers.gni
@@ -0,0 +1,40 @@
+# Copyright 2015 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Allow for platform-specific customization of use_asan_by_default
+import("//$starboard_path/configuration.gni")
+
+declare_args() {
+  # Compile for Thread Sanitizer to find threading bugs.
+  use_tsan = false
+}
+
+declare_args() {
+  # Compile for Address Sanitizer to find memory bugs.
+  use_asan =
+      !use_tsan && (cobalt_config == "debug" || cobalt_config == "devel") &&
+      (defined(use_asan_by_default) && use_asan_by_default)
+}
+
+assert(!(use_asan && use_tsan), "ASan and TSan are mutually exclusive")
+
+if (current_toolchain != default_toolchain) {
+  # Disable sanitizers for non-default toolchains.
+  use_asan = false
+  use_tsan = false
+}
diff --git a/src/starboard/build/delegated_config.gni b/src/starboard/build/delegated_config.gni
new file mode 100644
index 0000000..cc96a51
--- /dev/null
+++ b/src/starboard/build/delegated_config.gni
@@ -0,0 +1,111 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The delegated_config template defines a family of configs which delegate to
+# real implementations elsewhere. The most notable use of this template is to
+# define toolchain-independent compiler configs, such as
+# //starboard/build/config:rtti (and its sisters :no_rtti and :default_rtti).
+#
+# The simplest way to use delegated_config is to specify only a path:
+#
+#   delegated_config("rtti") {
+#     path = "//$starboard_path"
+#   }
+#
+# The sample code above will expand to:
+#
+#   config("rtti") {
+#     configs = [ "//$starboard_path:rtti" ]
+#   }
+#
+#   config("no_rtti") {
+#     configs = [ "//$starboard_path:no_rtti" ]
+#   }
+#
+#   config("default_rtti") {
+#     configs = [ "//$starboard_path:default_rtti" ]
+#   }
+#
+# The BUILD.gn file is then expected to define each of those three configs (or
+# further delegate them to e.g. a toolchain's implementation).
+#
+# To use a different list of prefixes, set the prefixes option:
+#
+#   delegated_config("optimizations") {
+#     path = "//$starboard_path"
+#     prefixes = [ "no", "debuggable", "full" ]
+#   }
+#
+# This will expand to:
+#
+#   config("no_optimizations") {
+#     configs = [ "//$starboard_path:no_optimizations" ]
+#   }
+#
+#   config("debuggable_optimizations") {
+#     configs = [ "//$starboard_path:debuggable_optimizations" ]
+#   }
+#
+#   config("full_optimizations") {
+#     configs = [ "//$starboard_path:full_optimizations" ]
+#   }
+#
+#   config("default_optimizations") {
+#     configs = [ "//$starboard_path:default_optimizations" ]
+#   }
+#
+# Note that the default_optimizations config is generated, even though it is
+# not in the list of prefixes. If you don't want the default_optimizations
+# config to be generated, set generate_default = false:
+#
+#   delegated_config("optimizations") {
+#     path = "//$starboard_path"
+#     prefixes = [ "no", "debuggable", "full" ]
+#     generate_default = false
+#   }
+#
+# Now only the no_, debuggable_, and full_ configs will be generated.
+
+template("delegated_config") {
+  assert(defined(invoker.path),
+         "You need to specify a path to the implementation")
+
+  if (defined(invoker.prefixes)) {
+    prefixes = invoker.prefixes
+  } else {
+    prefixes = [ "", "no" ]
+  }
+
+  foreach(prefix, prefixes) {
+    assert(prefix != "default",
+           "You shouldn't include \"default\" in the list of prefixes " +
+               "because it's separately generated.")
+
+    if (prefix == "") {
+      prefix_ = prefix
+    } else {
+      prefix_ = "${prefix}_"
+    }
+
+    config("$prefix_$target_name") {
+      configs = [ "${invoker.path}:$prefix_$target_name" ]
+    }
+  }
+
+  if (!(defined(invoker.generate_default) && !invoker.generate_default)) {
+    config("default_$target_name") {
+      configs = [ "${invoker.path}:default_$target_name" ]
+    }
+  }
+}
diff --git a/src/starboard/build/deploy.gni b/src/starboard/build/deploy.gni
new file mode 100644
index 0000000..0cd9369
--- /dev/null
+++ b/src/starboard/build/deploy.gni
@@ -0,0 +1,52 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This header file is meant to be included to provide a rule to deploy
+# a target on a target platform.
+#
+# Platforms needing such a deploy rule should define a template called
+# "deploy", which defines an action target which performs the necessary
+# actions (such as copying per-executable metadata files to the output
+# directory). This template should preferably be defined in
+# //$starboard_path/deploy.gni.
+#
+# To use this, at the top of the BUILD.gn file, import
+# //starboard/build/deploy.gni. Then, define a deploy target:
+#
+# deploy("deploy") {
+#   executable_name = "the_primary_targets_name"
+# }
+#
+# See //starboard/nplb/BUILD.gn for an example.
+
+import("//$starboard_path/configuration.gni")
+
+if (defined(include_path_platform_deploy_gni)) {
+  import(include_path_platform_deploy_gni)
+} else {
+  # The default deploy action is to do nothing.
+  template("deploy") {
+    not_needed(invoker, "*")
+
+    # Create an empty target so we can still depend on it. Honor the testonly
+    # and visibility settings.
+    group(target_name) {
+      forward_variables_from(invoker,
+                             [
+                               "testonly",
+                               "visibility",
+                             ])
+    }
+  }
+}
diff --git a/src/starboard/build/toolchain/BUILD.gn b/src/starboard/build/toolchain/BUILD.gn
new file mode 100644
index 0000000..daf2fb3
--- /dev/null
+++ b/src/starboard/build/toolchain/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2016 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.
+
+import("//starboard/build/toolchain/concurrent_links.gni")
+
+if (current_toolchain == default_toolchain) {
+  pool("link_pool") {
+    depth = concurrent_links
+  }
+}
diff --git a/src/starboard/build/toolchain/asan_symbolizer_path.gni b/src/starboard/build/toolchain/asan_symbolizer_path.gni
new file mode 100644
index 0000000..8ca146d
--- /dev/null
+++ b/src/starboard/build/toolchain/asan_symbolizer_path.gni
@@ -0,0 +1,23 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/sanitizers.gni")
+import("//starboard/build/toolchain/clang.gni")
+
+# TODO: is it really necessary to check whether ASAN is on here?
+if (use_asan) {
+  asan_symbolizer_path = "$clang_base_path/bin/llvm-symbolizer"
+} else {
+  asan_symbolizer_path = ""
+}
diff --git a/src/starboard/build/toolchain/clang.gni b/src/starboard/build/toolchain/clang.gni
new file mode 100644
index 0000000..6ef471a
--- /dev/null
+++ b/src/starboard/build/toolchain/clang.gni
@@ -0,0 +1,23 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  clang_base_path = ""
+}
+
+if (clang_base_path == "") {
+  # clang_base_path depends on gyp_utils.py for its _CLANG_VERSION variable
+  clang_base_path = exec_script("get_clang_base_path.py", [], "trim string",
+                                [ "//cobalt/build/gyp_utils.py" ])
+}
diff --git a/src/starboard/build/toolchain/concurrent_links.gni b/src/starboard/build/toolchain/concurrent_links.gni
new file mode 100644
index 0000000..22ac7e9
--- /dev/null
+++ b/src/starboard/build/toolchain/concurrent_links.gni
@@ -0,0 +1,45 @@
+# Copyright 2016 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file should only be imported from files that define toolchains.
+# There's no way to enforce this exactly, but all toolchains are processed
+# in the context of the default_toolchain, so we can at least check for that.
+assert(current_toolchain == default_toolchain)
+
+declare_args() {
+  # Limit the number of concurrent links; we often want to run fewer
+  # links at once than we do compiles, because linking is memory-intensive.
+  # The default to use varies by platform and by the amount of memory
+  # available, so we call out to a script to get the right value.
+  concurrent_links = -1
+}
+
+if (concurrent_links == -1) {
+  if (host_os == "win") {
+    _args = [ "--mem_per_link_gb=5" ]
+  } else if (host_os == "mac") {
+    _args = [ "--mem_per_link_gb=4" ]
+  }
+  else {
+    _args = []
+  }
+
+  # TODO(crbug.com/617429) Pass more build configuration info to the script
+  # so that we can compute better values.
+  concurrent_links = exec_script("get_concurrent_links.py", _args, "value")
+}
diff --git a/src/starboard/build/toolchain/gcc_ar_wrapper.py b/src/starboard/build/toolchain/gcc_ar_wrapper.py
new file mode 100755
index 0000000..4663f98
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_ar_wrapper.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+# Copyright 2015 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.
+
+"""Runs the 'ar' command after removing its output file first.
+
+This script is invoked like:
+  python gcc_ar_wrapper.py --ar=$AR --output=$OUT $OP $INPUTS
+to do the equivalent of:
+  rm -f $OUT && $AR $OP $OUT $INPUTS
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+import wrapper_utils
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--ar',
+                      required=True,
+                      help='The ar binary to run',
+                      metavar='PATH')
+  parser.add_argument('--output',
+                      required=True,
+                      help='Output archive file',
+                      metavar='ARCHIVE')
+  parser.add_argument('--plugin',
+                      help='Load plugin')
+  parser.add_argument('operation',
+                      help='Operation on the archive')
+  parser.add_argument('inputs', nargs='+',
+                      help='Input files')
+  args = parser.parse_args()
+
+  command = [args.ar, args.operation]
+  if args.plugin is not None:
+    command += ['--plugin', args.plugin]
+  command.append(args.output)
+  command += args.inputs
+
+  # Remove the output file first.
+  try:
+    os.remove(args.output)
+  except OSError as e:
+    if e.errno != os.errno.ENOENT:
+      raise
+
+  # Now just run the ar command.
+  return subprocess.call(wrapper_utils.CommandToRun(command))
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/gcc_link_wrapper.py b/src/starboard/build/toolchain/gcc_link_wrapper.py
new file mode 100755
index 0000000..b964347
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_link_wrapper.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+# Copyright 2015 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.
+
+"""Runs a linking command and optionally a strip command.
+
+This script exists to avoid using complex shell commands in
+gcc_toolchain.gni's tool("link"), in case the host running the compiler
+does not have a POSIX-like shell (e.g. Windows).
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+import wrapper_utils
+
+
+# When running on a Windows host and using a toolchain whose tools are
+# actually wrapper scripts (i.e. .bat files on Windows) rather than binary
+# executables, the "command" to run has to be prefixed with this magic.
+# The GN toolchain definitions take care of that for when GN/Ninja is
+# running the tool directly.  When that command is passed in to this
+# script, it appears as a unitary string but needs to be split up so that
+# just 'cmd' is the actual command given to Python's subprocess module.
+BAT_PREFIX = 'cmd /c call '
+
+
+def CommandToRun(command):
+  if command[0].startswith(BAT_PREFIX):
+    command = command[0].split(None, 3) + command[1:]
+  return command
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--strip',
+                      help='The strip binary to run',
+                      metavar='PATH')
+  parser.add_argument('--unstripped-file',
+                      help='Executable file produced by linking command',
+                      metavar='FILE')
+  parser.add_argument('--map-file',
+                      help=('Use --Wl,-Map to generate a map file. Will be '
+                            'gzipped if extension ends with .gz'),
+                      metavar='FILE')
+  parser.add_argument('--output',
+                      required=True,
+                      help='Final output executable file',
+                      metavar='FILE')
+  parser.add_argument('command', nargs='+',
+                      help='Linking command')
+  args = parser.parse_args()
+
+  # Work-around for gold being slow-by-default. http://crbug.com/632230
+  fast_env = dict(os.environ)
+  fast_env['LC_ALL'] = 'C'
+  result = wrapper_utils.RunLinkWithOptionalMapFile(args.command, env=fast_env,
+                                                    map_file=args.map_file)
+  if result != 0:
+    return result
+
+  # Finally, strip the linked executable (if desired).
+  if args.strip:
+    result = subprocess.call(CommandToRun([
+        args.strip, '--strip-unneeded', '-o', args.output, args.unstripped_file
+        ]))
+
+  return result
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/gcc_solink_wrapper.py b/src/starboard/build/toolchain/gcc_solink_wrapper.py
new file mode 100755
index 0000000..71af5e3
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_solink_wrapper.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+# Copyright 2015 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.
+
+"""Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged.
+
+This script exists to avoid using complex shell commands in
+gcc_toolchain.gni's tool("solink"), in case the host running the compiler
+does not have a POSIX-like shell (e.g. Windows).
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+import wrapper_utils
+
+
+def CollectSONAME(args):
+  """Replaces: readelf -d $sofile | grep SONAME.
+
+  Args:
+    args: the args to pass to the subprocess
+
+  Returns:
+    A tuple of (return code, output)
+  """
+  toc = ''
+  readelf = subprocess.Popen(wrapper_utils.CommandToRun(
+      [args.readelf, '-d', args.sofile]), stdout=subprocess.PIPE, bufsize=-1)
+  for line in readelf.stdout:
+    if 'SONAME' in line:
+      toc += line
+  return readelf.wait(), toc
+
+
+def CollectDynSym(args):
+  """Replaces: nm --format=posix -g -D $sofile | cut -f1-2 -d' '.
+
+  Args:
+    args: the args to pass to the subprocess
+
+  Returns:
+    A tuple of (return code, output)
+  """
+  toc = ''
+  nm = subprocess.Popen(wrapper_utils.CommandToRun([
+      args.nm, '--format=posix', '-g', '-D', args.sofile]),
+                        stdout=subprocess.PIPE, bufsize=-1)
+  for line in nm.stdout:
+    toc += ' '.join(line.split(' ', 2)[:2]) + '\n'
+  return nm.wait(), toc
+
+
+def CollectTOC(args):
+  result, toc = CollectSONAME(args)
+  if result == 0:
+    result, dynsym = CollectDynSym(args)
+    toc += dynsym
+  return result, toc
+
+
+def UpdateTOC(tocfile, toc):
+  if os.path.exists(tocfile):
+    old_toc = open(tocfile, 'r').read()
+  else:
+    old_toc = None
+  if toc != old_toc:
+    open(tocfile, 'w').write(toc)
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--readelf',
+                      required=True,
+                      help='The readelf binary to run',
+                      metavar='PATH')
+  parser.add_argument('--nm',
+                      required=True,
+                      help='The nm binary to run',
+                      metavar='PATH')
+  parser.add_argument('--strip',
+                      help='The strip binary to run',
+                      metavar='PATH')
+  parser.add_argument('--sofile',
+                      required=True,
+                      help='Shared object file produced by linking command',
+                      metavar='FILE')
+  parser.add_argument('--tocfile',
+                      required=True,
+                      help='Output table-of-contents file',
+                      metavar='FILE')
+  parser.add_argument('--map-file',
+                      help=('Use --Wl,-Map to generate a map file. Will be '
+                            'gzipped if extension ends with .gz'),
+                      metavar='FILE')
+  parser.add_argument('--output',
+                      required=True,
+                      help='Final output shared object file',
+                      metavar='FILE')
+  parser.add_argument('--resource-whitelist',
+                      help='Merge all resource whitelists into a single file.',
+                      metavar='PATH')
+  parser.add_argument('command', nargs='+',
+                      help='Linking command')
+  args = parser.parse_args()
+
+  # Work-around for gold being slow-by-default. http://crbug.com/632230
+  fast_env = dict(os.environ)
+  fast_env['LC_ALL'] = 'C'
+
+  if args.resource_whitelist:
+    whitelist_candidates = wrapper_utils.ResolveRspLinks(args.command)
+    wrapper_utils.CombineResourceWhitelists(
+        whitelist_candidates, args.resource_whitelist)
+
+  # First, run the actual link.
+  command = wrapper_utils.CommandToRun(args.command)
+  result = wrapper_utils.RunLinkWithOptionalMapFile(command, env=fast_env,
+                                                    map_file=args.map_file)
+
+  if result != 0:
+    return result
+
+  # Next, generate the contents of the TOC file.
+  result, toc = CollectTOC(args)
+  if result != 0:
+    return result
+
+  # If there is an existing TOC file with identical contents, leave it alone.
+  # Otherwise, write out the TOC file.
+  UpdateTOC(args.tocfile, toc)
+
+  # Finally, strip the linked shared object file (if desired).
+  if args.strip:
+    result = subprocess.call(wrapper_utils.CommandToRun(
+        [args.strip, '--strip-unneeded', '-o', args.output, args.sofile]))
+
+  return result
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/gcc_toolchain.gni b/src/starboard/build/toolchain/gcc_toolchain.gni
new file mode 100644
index 0000000..94c52f4
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_toolchain.gni
@@ -0,0 +1,487 @@
+# Copyright (c) 2013 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/toolchain/clang.gni")
+import("//starboard/build/toolchain/goma.gni")
+
+# This template defines a toolchain for something that works like gcc
+# (including clang).
+#
+# It requires the following variables specifying the executables to run:
+#  - ar
+#  - cc
+#  - cxx
+#  - ld
+#
+# Optional parameters that control the tools:
+#
+#  - extra_cflags
+#      Extra flags to be appended when compiling C files (but not C++ files).
+#  - extra_cppflags
+#      Extra flags to be appended when compiling both C and C++ files. "CPP"
+#      stands for "C PreProcessor" in this context, although it can be
+#      used for non-preprocessor flags as well. Not to be confused with
+#      "CXX" (which follows).
+#  - extra_cxxflags
+#      Extra flags to be appended when compiling C++ files (but not C files).
+#  - extra_ldflags
+#      Extra flags to be appended when linking
+#
+#  - libs_section_prefix
+#  - libs_section_postfix
+#      The contents of these strings, if specified, will be placed around
+#      the libs section of the linker line. It allows one to inject libraries
+#      at the beginning and end for all targets in a toolchain.
+#  - solink_libs_section_prefix
+#  - solink_libs_section_postfix
+#      Same as libs_section_{pre,post}fix except used for solink instead of link.
+#  - link_outputs
+#      The content of this array, if specified, will be added to the list of
+#      outputs from the link command. This can be useful in conjunction with
+#      the post_link parameter.
+#  - post_link
+#      The content of this string, if specified, will be run as a separate
+#      command following the the link command.
+#  - deps
+#      Just forwarded to the toolchain definition.
+#  - executable_extension
+#      If this string is specified it will be used for the file extension
+#      for an executable, rather than using no extension; targets will
+#      still be able to override the extension using the output_extension
+#      variable.
+#  - rebuild_define
+#      The contents of this string, if specified, will be passed as a #define
+#      to the toolchain. It can be used to force recompiles whenever a
+#      toolchain is updated.
+#  - shlib_extension
+#      If this string is specified it will be used for the file extension
+#      for a shared library, rather than default value specified in
+#      toolchain.gni
+#  - strip
+#      Location of the strip executable. When specified, strip will be run on
+#      all shared libraries and executables as they are built. The pre-stripped
+#      artifacts will be put in lib.unstripped/ and exe.unstripped/.
+template("gcc_toolchain") {
+  toolchain(target_name) {
+    assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value")
+    assert(defined(invoker.cc), "gcc_toolchain() must specify a \"cc\" value")
+    assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value")
+    assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value")
+    assert(defined(invoker.shlib_extension),
+           "gcc_toolchain() must specify a \"shlib_extension\" value")
+
+    # This define changes when the toolchain changes, forcing a rebuild.
+    # Nothing should ever use this define.
+    if (defined(invoker.rebuild_define)) {
+      rebuild_string = "-D" + invoker.rebuild_define + " "
+    } else {
+      rebuild_string = ""
+    }
+
+    # GN's syntax can't handle more than one scope dereference at once, like
+    # "invoker.toolchain_args.foo", so make a temporary to hold the toolchain
+    # args so we can do "invoker_toolchain_args.foo".
+    assert(defined(invoker.toolchain_args),
+           "Toolchains must specify toolchain_args")
+    invoker_toolchain_args = invoker.toolchain_args
+    assert(defined(invoker_toolchain_args.current_cpu),
+           "toolchain_args must specify a current_cpu")
+    assert(defined(invoker_toolchain_args.current_os),
+           "toolchain_args must specify a current_os")
+
+    # When invoking this toolchain not as the default one, these args will be
+    # passed to the build. They are ignored when this is the default toolchain.
+    toolchain_args = {
+      # Populate toolchain args from the invoker.
+      forward_variables_from(invoker_toolchain_args, "*")
+    }
+
+    # When the invoker has explicitly overridden use_goma or cc_wrapper in the
+    # toolchain args, use those values, otherwise default to the global one.
+    # This works because the only reasonable override that toolchains might
+    # supply for these values are to force-disable them.
+    if (defined(toolchain_args.use_goma)) {
+      toolchain_uses_goma = toolchain_args.use_goma
+    } else {
+      toolchain_uses_goma = use_goma
+    }
+
+    # When the invoker has explicitly overridden use_goma in the
+    # toolchain args, use those values, otherwise default to the global one.
+    # This works because the only reasonable override that toolchains might
+    # supply for these values are to force-disable them.
+    if (toolchain_uses_goma) {
+      goma_path = "$goma_dir/gomacc"
+      compiler_prefix = "${goma_path} "
+    } else {
+      compiler_prefix = ""
+    }
+
+    cc = compiler_prefix + invoker.cc
+    cxx = compiler_prefix + invoker.cxx
+    ar = invoker.ar
+    ld = invoker.ld
+    if (!defined(asm)) {
+      asm = cc
+    }
+    if (defined(invoker.readelf)) {
+      readelf = invoker.readelf
+    } else {
+      readelf = "readelf"
+    }
+    if (defined(invoker.nm)) {
+      nm = invoker.nm
+    } else {
+      nm = "nm"
+    }
+    if (defined(invoker.stamp_command)) {
+      stamp_command = invoker.stamp_command
+    } else {
+      stamp_command = "touch {{output}}"
+    }
+    if (defined(invoker.copy_command)) {
+      copy_command = invoker.copy_command
+    } else {
+      copy_command = "ln -f {{source}} {{output}} 2>/dev/null || (rm -rf {{output}} && cp -af {{source}} {{output}})"
+    }
+
+    default_shlib_extension = invoker.shlib_extension
+
+    if (defined(invoker.executable_extension)) {
+      default_executable_extension = invoker.executable_extension
+    } else {
+      default_executable_extension = ""
+    }
+
+    # Bring these into our scope for string interpolation with default values.
+    if (defined(invoker.libs_section_prefix)) {
+      libs_section_prefix = invoker.libs_section_prefix
+    } else {
+      libs_section_prefix = ""
+    }
+
+    if (defined(invoker.libs_section_postfix)) {
+      libs_section_postfix = invoker.libs_section_postfix
+    } else {
+      libs_section_postfix = ""
+    }
+
+    if (defined(invoker.solink_libs_section_prefix)) {
+      solink_libs_section_prefix = invoker.solink_libs_section_prefix
+    } else {
+      solink_libs_section_prefix = ""
+    }
+
+    if (defined(invoker.solink_libs_section_postfix)) {
+      solink_libs_section_postfix = invoker.solink_libs_section_postfix
+    } else {
+      solink_libs_section_postfix = ""
+    }
+
+    if (defined(invoker.extra_cflags) && invoker.extra_cflags != "") {
+      extra_cflags = " " + invoker.extra_cflags
+    } else {
+      extra_cflags = ""
+    }
+
+    if (defined(invoker.extra_cppflags) && invoker.extra_cppflags != "") {
+      extra_cppflags = " " + invoker.extra_cppflags
+    } else {
+      extra_cppflags = ""
+    }
+
+    if (defined(invoker.extra_cxxflags) && invoker.extra_cxxflags != "") {
+      extra_cxxflags = " " + invoker.extra_cxxflags
+    } else {
+      extra_cxxflags = ""
+    }
+
+    if (defined(invoker.extra_ldflags) && invoker.extra_ldflags != "") {
+      extra_ldflags = " " + invoker.extra_ldflags
+    } else {
+      extra_ldflags = ""
+    }
+
+    # These library switches can apply to all tools below.
+    lib_switch = "-l"
+    lib_dir_switch = "-L"
+
+    # Object files go in this directory.
+    object_subdir = "{{target_out_dir}}/{{label_name}}"
+
+    tool("cc") {
+      depfile = "{{output}}.d"
+      command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "CC {{output}}"
+      outputs = [
+        "$object_subdir/{{source_name_part}}.o",
+      ]
+    }
+
+    tool("cxx") {
+      depfile = "{{output}}.d"
+      command = "$cxx -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "CXX {{output}}"
+      outputs = [
+        "$object_subdir/{{source_name_part}}.o",
+      ]
+    }
+
+    tool("asm") {
+      # For GCC we can just use the C compiler to compile assembly.
+      depfile = "{{output}}.d"
+      command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "ASM {{output}}"
+      outputs = [
+        "$object_subdir/{{source_name_part}}.o",
+      ]
+    }
+
+    tool("alink") {
+      rspfile = "{{output}}.rsp"
+
+      # This needs a Python script to avoid using simple sh features in this
+      # command, in case the host does not use a POSIX shell (e.g. compiling
+      # POSIX-like toolchains such as NaCl on Windows).
+      ar_wrapper = rebase_path("//starboard/build/toolchain/gcc_ar_wrapper.py",
+                               root_build_dir)
+      extra_arflags = "rcsD"
+      command = "$python_path \"$ar_wrapper\" --output={{output}} --ar=\"$ar\" {{arflags}} $extra_arflags @\"$rspfile\""
+      description = "AR {{output}}"
+      rspfile_content = "{{inputs}}"
+      outputs = [
+        "{{output_dir}}/{{target_output_name}}{{output_extension}}",
+      ]
+
+      # Shared libraries go in the target out directory by default so we can
+      # generate different targets with the same name and not have them collide.
+      default_output_dir = "{{target_out_dir}}"
+      default_output_extension = ".a"
+      output_prefix = "lib"
+    }
+
+    tool("solink") {
+      soname = "{{target_output_name}}{{output_extension}}"  # e.g. "libfoo.so".
+      sofile = "{{output_dir}}/$soname"  # Possibly including toolchain dir.
+      rspfile = sofile + ".rsp"
+      pool = "//starboard/build/toolchain:link_pool($default_toolchain)"
+
+      if (defined(invoker.strip)) {
+        unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname"
+      } else {
+        unstripped_sofile = sofile
+      }
+
+      # These variables are not built into GN but are helpers that
+      # implement (1) linking to produce a .so, (2) extracting the symbols
+      # from that file (3) if the extracted list differs from the existing
+      # .TOC file, overwrite it, otherwise, don't change it.
+      tocfile = sofile + ".TOC"
+
+      link_command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\""
+
+      assert(defined(readelf), "to solink you must have a readelf")
+      assert(defined(nm), "to solink you must have an nm")
+      strip_switch = ""
+      if (defined(invoker.strip)) {
+        strip_switch = "--strip=${invoker.strip} "
+      }
+
+      # This needs a Python script to avoid using a complex shell command
+      # requiring sh control structures, pipelines, and POSIX utilities.
+      # The host might not have a POSIX shell and utilities (e.g. Windows).
+      solink_wrapper =
+          rebase_path("//starboard/build/toolchain/gcc_solink_wrapper.py",
+                      root_build_dir)
+      command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch--sofile=\"$unstripped_sofile\" --tocfile=\"$tocfile\" --output=\"$sofile\" -- $link_command"
+
+      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
+
+      description = "SOLINK $sofile"
+
+      # Use this for {{output_extension}} expansions unless a target manually
+      # overrides it (in which case {{output_extension}} will be what the target
+      # specifies).
+      default_output_extension = default_shlib_extension
+
+      default_output_dir = "{{root_out_dir}}"
+
+      output_prefix = "lib"
+
+      # Since the above commands only updates the .TOC file when it changes, ask
+      # Ninja to check if the timestamp actually changed to know if downstream
+      # dependencies should be recompiled.
+      restat = true
+
+      # Tell GN about the output files. It will link to the sofile but use the
+      # tocfile for dependency management.
+      outputs = [
+        sofile,
+        tocfile,
+      ]
+      if (sofile != unstripped_sofile) {
+        outputs += [ unstripped_sofile ]
+      }
+      link_output = sofile
+      depend_output = tocfile
+    }
+
+    tool("solink_module") {
+      soname = "{{target_output_name}}{{output_extension}}"  # e.g. "libfoo.so".
+      sofile = "{{output_dir}}/$soname"
+      rspfile = sofile + ".rsp"
+      pool = "//starboard/build/toolchain:link_pool($default_toolchain)"
+
+      if (defined(invoker.strip)) {
+        unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname"
+      } else {
+        unstripped_sofile = sofile
+      }
+
+      command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\""
+
+      if (defined(invoker.strip)) {
+        strip_command = "${invoker.strip} --strip-unneeded -o \"$sofile\" \"$unstripped_sofile\""
+        command += " && " + strip_command
+      }
+      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
+
+      description = "SOLINK_MODULE $sofile"
+
+      # Use this for {{output_extension}} expansions unless a target manually
+      # overrides it (in which case {{output_extension}} will be what the target
+      # specifies).
+      if (defined(invoker.loadable_module_extension)) {
+        default_output_extension = invoker.loadable_module_extension
+      } else {
+        default_output_extension = default_shlib_extension
+      }
+
+      default_output_dir = "{{root_out_dir}}"
+
+      output_prefix = "lib"
+
+      outputs = [
+        sofile,
+      ]
+      if (sofile != unstripped_sofile) {
+        outputs += [ unstripped_sofile ]
+      }
+    }
+
+    tool("link") {
+      exename = "{{target_output_name}}{{output_extension}}"
+      outfile = "{{output_dir}}/$exename"
+      rspfile = "$outfile.rsp"
+      unstripped_outfile = outfile
+      pool = "//starboard/build/toolchain:link_pool($default_toolchain)"
+
+      # Use this for {{output_extension}} expansions unless a target manually
+      # overrides it (in which case {{output_extension}} will be what the target
+      # specifies).
+      default_output_extension = default_executable_extension
+
+      default_output_dir = "{{root_out_dir}}"
+
+      if (defined(invoker.strip)) {
+        unstripped_outfile = "{{root_out_dir}}/exe.unstripped/$exename"
+      }
+
+      start_group_flag = "-Wl,--start-group"
+      end_group_flag = "-Wl,--end-group "
+      link_command = "$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag $libs_section_prefix {{libs}} $libs_section_postfix"
+
+      strip_switch = ""
+
+      if (defined(invoker.strip)) {
+        strip_switch = " --strip=\"${invoker.strip}\" --unstripped-file=\"$unstripped_outfile\""
+      }
+
+      link_wrapper =
+          rebase_path("//starboard/build/toolchain/gcc_link_wrapper.py",
+                      root_build_dir)
+      command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch -- $link_command"
+      description = "LINK $outfile"
+      rspfile_content = "{{inputs}}"
+      outputs = [
+        outfile,
+      ]
+      if (outfile != unstripped_outfile) {
+        outputs += [ unstripped_outfile ]
+      }
+      if (defined(invoker.link_outputs)) {
+        outputs += invoker.link_outputs
+      }
+    }
+
+    # These two are really entirely generic, but have to be repeated in
+    # each toolchain because GN doesn't allow a template to be used here.
+    # See //build/toolchain/toolchain.gni for details.
+    tool("stamp") {
+      command = stamp_command
+      description = "STAMP {{output}}"
+    }
+    tool("copy") {
+      command = copy_command
+      description = "COPY {{source}} {{output}}"
+    }
+
+    forward_variables_from(invoker, [ "deps" ])
+  }
+}
+
+# This is a shorthand for gcc_toolchain instances based on the Chromium-built
+# version of Clang. Only the toolchain_cpu and toolchain_os variables need to
+# be specified by the invoker, and optionally toolprefix if it's a
+# cross-compile case. Note that for a cross-compile case this toolchain
+# requires a config to pass the appropriate -target option, or else it will
+# actually just be doing a native compile. The invoker can optionally override
+# use_gold too.
+template("clang_toolchain") {
+  if (defined(invoker.toolprefix)) {
+    toolprefix = invoker.toolprefix
+  } else {
+    toolprefix = ""
+  }
+
+  gcc_toolchain(target_name) {
+    prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+    cc = "$prefix/clang"
+    cxx = "$prefix/clang++"
+    ld = cxx
+    readelf = "${toolprefix}readelf"
+    ar = "${prefix}/llvm-ar"
+    nm = "${toolprefix}nm"
+
+    forward_variables_from(invoker,
+                           [
+                             "strip",
+                             "is_clang_analysis_supported",
+                             "shlib_extension",
+                           ])
+
+    toolchain_args = {
+      if (defined(invoker.toolchain_args)) {
+        forward_variables_from(invoker.toolchain_args, "*")
+      }
+    }
+  }
+}
diff --git a/src/starboard/build/toolchain/get_clang_base_path.py b/src/starboard/build/toolchain/get_clang_base_path.py
new file mode 100755
index 0000000..d6fc75a
--- /dev/null
+++ b/src/starboard/build/toolchain/get_clang_base_path.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Finds and prints the Clang base path.
+
+If Clang is not present, it will be downloaded.
+"""
+
+import os.path
+import sys
+
+sys.path.append(
+    os.path.realpath(
+        os.path.join(
+            os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)))
+from cobalt.build.gyp_utils import EnsureClangAvailable  # pylint: disable=g-import-not-at-top
+from cobalt.build.gyp_utils import GetClangBasePath
+from cobalt.build.gyp_utils import GetClangBinPath
+
+
+def main():
+  base_dir = GetClangBasePath()
+  bin_path = GetClangBinPath()
+  EnsureClangAvailable(base_dir, bin_path)
+  print os.path.join(base_dir, 'llvm-build', 'Release+Asserts')
+
+if __name__ == '__main__':
+  main()
diff --git a/src/starboard/build/toolchain/get_concurrent_links.py b/src/starboard/build/toolchain/get_concurrent_links.py
new file mode 100644
index 0000000..be5640a
--- /dev/null
+++ b/src/starboard/build/toolchain/get_concurrent_links.py
@@ -0,0 +1,86 @@
+# Copyright 2014 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.
+
+"""Computes and prints the default number of concurrent links.
+
+Computes the number of concurrent links to be run in the build, as a function
+of machine spec. Based on GetDefaultConcurrentLinks in GYP.
+"""
+
+import ctypes
+import optparse
+import os
+import re
+import subprocess
+import sys
+
+
+def _GetTotalMemoryInBytes():
+  """Gets the total amount of virtual memory in bytes.
+
+  Returns:
+    See above
+  """
+  if sys.platform in ('win32', 'cygwin'):
+
+    class MEMORYSTATUSEX(ctypes.Structure):
+      _fields_ = [
+          ('dwLength', ctypes.c_ulong),
+          ('dwMemoryLoad', ctypes.c_ulong),
+          ('ullTotalPhys', ctypes.c_ulonglong),
+          ('ullAvailPhys', ctypes.c_ulonglong),
+          ('ullTotalPageFile', ctypes.c_ulonglong),
+          ('ullAvailPageFile', ctypes.c_ulonglong),
+          ('ullTotalVirtual', ctypes.c_ulonglong),
+          ('ullAvailVirtual', ctypes.c_ulonglong),
+          ('sullAvailExtendedVirtual', ctypes.c_ulonglong),
+      ]
+
+    stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX))
+    ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
+    return stat.ullTotalPhys
+  elif sys.platform.startswith('linux'):
+    if os.path.exists('/proc/meminfo'):
+      with open('/proc/meminfo') as meminfo:
+        memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
+        for line in meminfo:
+          match = memtotal_re.match(line)
+          if not match:
+            continue
+          return float(match.group(1)) * 2**10
+  elif sys.platform == 'darwin':
+    try:
+      return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
+    except subprocess.CalledProcessError:
+      return 0
+  # TODO: Implement this for other platforms.
+  return 0
+
+
+def _GetDefaultConcurrentLinks(mem_per_link_gb, reserve_mem_gb):
+  # Inherit the legacy environment variable for people that have set it in GYP.
+  pool_size = int(os.getenv('GYP_LINK_CONCURRENCY', 0))
+  if pool_size:
+    return pool_size
+
+  mem_total_bytes = _GetTotalMemoryInBytes()
+  mem_total_bytes = max(0, mem_total_bytes - reserve_mem_gb * 2**30)
+  num_concurrent_links = int(max(1, mem_total_bytes / mem_per_link_gb / 2**30))
+  hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
+  return min(num_concurrent_links, hard_cap)
+
+
+def main():
+  parser = optparse.OptionParser()
+  parser.add_option('--mem_per_link_gb', action='store', type='int', default=6)
+  parser.add_option('--reserve_mem_gb', action='store', type='int', default=0)
+  parser.disable_interspersed_args()
+  options, _ = parser.parse_args()
+
+  print _GetDefaultConcurrentLinks(options.mem_per_link_gb,
+                                   options.reserve_mem_gb)
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/goma.gni b/src/starboard/build/toolchain/goma.gni
new file mode 100644
index 0000000..dd72cefe
--- /dev/null
+++ b/src/starboard/build/toolchain/goma.gni
@@ -0,0 +1,40 @@
+# Copyright (c) 2013 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Defines the configuration of Goma.
+
+# Allow ports to set an alternate default for use_goma
+import("//$starboard_path/configuration.gni")
+
+declare_args() {
+  if (!defined(use_goma)) {
+    # Set to true to enable distributed compilation using Goma. By default we
+    # use Goma for stub and linux.
+    use_goma = false
+  }
+
+  # Set the default value based on the platform.
+  if (host_os == "win" || host_os == "winrt_81" ||
+      host_os == "winrt_81_phone" || host_os == "winrt_10") {
+    # Absolute directory containing the gomacc.exe binary.
+    goma_dir = "C:\goma\goma-win64"
+  } else {
+    # Absolute directory containing the gomacc binary.
+    goma_dir = getenv("HOME") + "/goma"
+  }
+}
diff --git a/src/starboard/build/toolchain/linux/BUILD.gn b/src/starboard/build/toolchain/linux/BUILD.gn
new file mode 100644
index 0000000..962109a
--- /dev/null
+++ b/src/starboard/build/toolchain/linux/BUILD.gn
@@ -0,0 +1,217 @@
+# Copyright (c) 2013 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/toolchain/gcc_toolchain.gni")
+
+clang_toolchain("clang_arm") {
+  toolprefix = "arm-linux-gnueabihf-"
+  shlib_extension = ".so"
+  toolchain_args = {
+    current_cpu = "arm"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_arm64") {
+  toolprefix = "aarch64-linux-gnu-"
+  shlib_extension = ".so"
+  toolchain_args = {
+    current_cpu = "arm64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("arm64") {
+  toolprefix = "aarch64-linux-gnu-"
+  shlib_extension = ".so"
+
+  cc = "${toolprefix}gcc"
+  cxx = "${toolprefix}g++"
+
+  ar = "${toolprefix}ar"
+  ld = cxx
+  readelf = "${toolprefix}readelf"
+  nm = "${toolprefix}nm"
+
+  toolchain_args = {
+    current_cpu = "arm64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("arm") {
+  toolprefix = "arm-linux-gnueabihf-"
+  shlib_extension = ".so"
+
+  cc = "${toolprefix}gcc"
+  cxx = "${toolprefix}g++"
+
+  ar = "${toolprefix}ar"
+  ld = cxx
+  readelf = "${toolprefix}readelf"
+  nm = "${toolprefix}nm"
+
+  toolchain_args = {
+    current_cpu = "arm"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_x86") {
+  shlib_extension = ".so"
+
+  toolchain_args = {
+    current_cpu = "x86"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("x86") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "x86"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_x64") {
+  shlib_extension = ".so"
+
+  toolchain_args = {
+    current_cpu = "x64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("x64") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "x64"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_mipsel") {
+  shlib_extension = ".so"
+  toolchain_args = {
+    current_cpu = "mipsel"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("mipsel") {
+  shlib_extension = ".so"
+
+  cc = "mipsel-linux-gnu-gcc"
+  cxx = "mipsel-linux-gnu-g++"
+  ar = "mipsel-linux-gnu-ar"
+  ld = cxx
+  readelf = "mipsel-linux-gnu-readelf"
+  nm = "mipsel-linux-gnu-nm"
+
+  toolchain_args = {
+    current_cpu = "mipsel"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("s390x") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "s390x"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("ppc64") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "ppc64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("mips") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "mips"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("mips64") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "mips64"
+    current_os = "linux"
+  }
+}
diff --git a/src/starboard/build/toolchain/linux/config/BUILD.gn b/src/starboard/build/toolchain/linux/config/BUILD.gn
new file mode 100644
index 0000000..63f8da6
--- /dev/null
+++ b/src/starboard/build/toolchain/linux/config/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Sets the optimization level
+config("no_optimizations") {
+  cflags = [ "-O0" ]
+}
+
+config("debuggable_optimizations") {
+  cflags = [ "-O2" ]
+}
+
+config("full_optimizations") {
+  cflags = [ "-O3" ]
+}
+
+
+# Enables/disables RTTI
+config("rtti") {
+  cflags_cc = [ "-frtti" ]
+}
+
+config("no_rtti") {
+  cflags_cc = [ "-fno-rtti" ]
+}
+
+
+# Enables -Wexit-time-destructors. Only usable for Clang.
+config("wexit_time_destructors") {
+  cflags = [ "-Wexit-time-destructors" ]
+}
diff --git a/src/starboard/build/toolchain/wrapper_utils.py b/src/starboard/build/toolchain/wrapper_utils.py
new file mode 100644
index 0000000..d01933d
--- /dev/null
+++ b/src/starboard/build/toolchain/wrapper_utils.py
@@ -0,0 +1,150 @@
+# Copyright (c) 2016 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.
+
+"""Helper functions for gcc_toolchain.gni wrappers."""
+
+import gzip
+import os
+import re
+import shlex
+import shutil
+import subprocess
+import threading
+
+_BAT_PREFIX = 'cmd /c call '
+_WHITELIST_RE = re.compile('whitelisted_resource_(?P<resource_id>[0-9]+)')
+
+
+def _GzipThenDelete(src_path, dest_path):
+  # Results for Android map file with GCC on a z620:
+  # Uncompressed: 207MB
+  # gzip -9: 16.4MB, takes 8.7 seconds.
+  # gzip -1: 21.8MB, takes 2.0 seconds.
+  # Piping directly from the linker via -print-map (or via -Map with a fifo)
+  # adds a whopping 30-45 seconds!
+  with open(src_path, 'rb') as f_in, gzip.GzipFile(dest_path, 'wb', 1) as f_out:
+    shutil.copyfileobj(f_in, f_out)
+  os.unlink(src_path)
+
+
+def CommandToRun(command):
+  """Generates commands compatible with Windows.
+
+  When running on a Windows host and using a toolchain whose tools are
+  actually wrapper scripts (i.e. .bat files on Windows) rather than binary
+  executables, the |command| to run has to be prefixed with this magic.
+  The GN toolchain definitions take care of that for when GN/Ninja is
+  running the tool directly.  When that command is passed in to this
+  script, it appears as a unitary string but needs to be split up so that
+  just 'cmd' is the actual command given to Python's subprocess module.
+
+  Args:
+    command: List containing the UNIX style |command|.
+
+  Returns:
+    A list containing the Windows version of the |command|.
+  """
+  if command[0].startswith(_BAT_PREFIX):
+    command = command[0].split(None, 3) + command[1:]
+  return command
+
+
+def RunLinkWithOptionalMapFile(command, env=None, map_file=None):
+  """Runs the given command, adding in -Wl,-Map when |map_file| is given.
+
+  Also takes care of gzipping when |map_file| ends with .gz.
+
+  Args:
+    command: List of arguments comprising the command.
+    env: Environment variables.
+    map_file: Path to output map_file.
+
+  Returns:
+    The exit code of running |command|.
+  """
+  tmp_map_path = None
+  if map_file and map_file.endswith('.gz'):
+    tmp_map_path = map_file + '.tmp'
+    command.append('-Wl,-Map,' + tmp_map_path)
+  elif map_file:
+    command.append('-Wl,-Map,' + map_file)
+
+  result = subprocess.call(command, env=env)
+
+  if tmp_map_path and result == 0:
+    threading.Thread(
+        target=lambda: _GzipThenDelete(tmp_map_path, map_file)).start()
+  elif tmp_map_path and os.path.exists(tmp_map_path):
+    os.unlink(tmp_map_path)
+
+  return result
+
+
+def ResolveRspLinks(inputs):
+  """Return a list of files contained in a response file.
+
+  Args:
+    inputs: A command containing rsp files.
+
+  Returns:
+    A set containing the rsp file content.
+  """
+  rspfiles = [a[1:] for a in inputs if a.startswith('@')]
+  resolved = set()
+  for rspfile in rspfiles:
+    with open(rspfile, 'r') as f:
+      resolved.update(shlex.split(f.read()))
+
+  return resolved
+
+
+def CombineResourceWhitelists(whitelist_candidates, outfile):
+  """Combines all whitelists for a resource file into a single whitelist.
+
+  Args:
+    whitelist_candidates: List of paths to rsp files containing all targets.
+    outfile: Path to save the combined whitelist.
+  """
+  whitelists = ('%s.whitelist' % candidate for candidate in whitelist_candidates
+                if os.path.exists('%s.whitelist' % candidate))
+
+  resources = set()
+  for whitelist in whitelists:
+    with open(whitelist, 'r') as f:
+      resources.update(f.readlines())
+
+  with open(outfile, 'w') as f:
+    f.writelines(resources)
+
+
+def ExtractResourceIdsFromPragmaWarnings(text):
+  """Returns set of resource IDs that are inside unknown pragma warnings.
+
+  Args:
+    text: The text that will be scanned for unknown pragma warnings.
+
+  Returns:
+    A set containing integers representing resource IDs.
+  """
+  used_resources = set()
+  lines = text.splitlines()
+  for ln in lines:
+    match = _WHITELIST_RE.search(ln)
+    if match:
+      resource_id = int(match.group('resource_id'))
+      used_resources.add(resource_id)
+
+  return used_resources
+
+
+def CaptureCommandStderr(command, env=None):
+  """Returns the stderr of a command.
+
+  Args:
+    command: A list containing the command and arguments.
+    env: Environment variables for the new process.
+  """
+  child = subprocess.Popen(command, stderr=subprocess.PIPE, env=env)
+  _, stderr = child.communicate()
+  return child.returncode, stderr
diff --git a/src/starboard/byte_swap.h b/src/starboard/byte_swap.h
index 9fbd1fa..2347bd2 100644
--- a/src/starboard/byte_swap.h
+++ b/src/starboard/byte_swap.h
@@ -82,4 +82,47 @@
 }  // extern "C"
 #endif
 
+#ifdef __cplusplus
+
+#include <algorithm>
+
+template <typename T>
+T SbByteSwap(T value) {
+  std::reverse(reinterpret_cast<uint8_t*>(&value),
+               reinterpret_cast<uint8_t*>(&value + 1));
+  return value;
+}
+
+template <>
+inline int16_t SbByteSwap(int16_t value) {
+  return SbByteSwapS16(value);
+}
+
+template <>
+inline uint16_t SbByteSwap(uint16_t value) {
+  return SbByteSwapU16(value);
+}
+
+template <>
+inline int32_t SbByteSwap(int32_t value) {
+  return SbByteSwapS32(value);
+}
+
+template <>
+inline uint32_t SbByteSwap(uint32_t value) {
+  return SbByteSwapU32(value);
+}
+
+template <>
+inline int64_t SbByteSwap(int64_t value) {
+  return SbByteSwapS64(value);
+}
+
+template <>
+inline uint64_t SbByteSwap(uint64_t value) {
+  return SbByteSwapU64(value);
+}
+
+#endif
+
 #endif  // STARBOARD_BYTE_SWAP_H_
diff --git a/src/starboard/common/BUILD.gn b/src/starboard/common/BUILD.gn
new file mode 100644
index 0000000..6d9fcbd
--- /dev/null
+++ b/src/starboard/common/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO: change to //starboard eventually
+import("//cobalt/build/config/base.gni")
+
+static_library("common") {
+  sources = [
+    "common.cc",
+    "flat_map.h",
+    "memory.cc",
+    "move.h",
+    "new.cc",
+    "optional.cc",
+    "ref_counted.cc",
+    "ref_counted.h",
+    "reset_and_return.h",
+    "rwlock.h",
+    "semaphore.h",
+    "scoped_ptr.h",
+    "state_machine.cc",
+    "state_machine.h",
+    "thread_collision_warner.cc",
+    "thread_collision_warner.h",
+  ]
+
+  # TODO: get rid of dependence on Cobalt
+  if (!enable_custom_media_session_client) {
+    sources += [ "//starboard/shared/media_session/stub_playback_state.cc" ]
+  }
+
+  # This must be defined when building Starboard, and must not when building
+  # Starboard client code.
+  defines = [ "STARBOARD_IMPLEMENTATION" ]
+}
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index bf285ef..f85fbb5 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -29,12 +29,15 @@
         'memory.cc',
         'move.h',
         'new.cc',
+        'optional.cc',
         'ref_counted.cc',
         'ref_counted.h',
         'reset_and_return.h',
         'rwlock.h',
         'semaphore.h',
         'scoped_ptr.h',
+        'state_machine.cc',
+        'state_machine.h',
         'thread_collision_warner.cc',
         'thread_collision_warner.h',
       ],
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/common/optional.cc
similarity index 69%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/common/optional.cc
index be4e805..f9618d1 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/common/optional.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
+// Copyright 2014 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#include "starboard/common/optional.h"
 
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+namespace starboard {
+const nullopt_t nullopt;
+const in_place_t in_place;
+}  // namespace starboard
diff --git a/src/starboard/common/optional.h b/src/starboard/common/optional.h
new file mode 100644
index 0000000..8e8fc00
--- /dev/null
+++ b/src/starboard/common/optional.h
@@ -0,0 +1,428 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_COMMON_OPTIONAL_H_
+#define STARBOARD_COMMON_OPTIONAL_H_
+
+#include <algorithm>
+#include <iosfwd>
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+
+// This class is based off of std::experimental::optional:
+//   http://en.cppreference.com/w/cpp/experimental/optional
+//
+// It is a template class where instances parameterized by type T contain
+// memory for an instance of type T, but it may or may not be constructed.
+// If it is not constructed, it cannot be accessed, and if it is, it can
+// be accessed.  This allows one to check if the inner object exists or not
+// before using it, and is useful for functions that may or may not return
+// a value.  Note that the memory for the object is stored internally, so
+// no heap allocations are made over the course of construction and destruction
+// of the internal object (unless the internal object allocates memory within
+// its constructor).
+//
+// Some functionality is left out.  For example, most C++11 functionality
+// is not implemented, since we would like this to be friendly to non-C++11
+// compilers.
+//
+// In the future, if C++11 functionality is needed, it can be implemented
+// and surrounded by preprocessor guards to maintain compatibility with non
+// C++11 compilers.
+//
+
+// The nullopt_t type is used as a signal for an empty optional.  If any
+// optional is assigned the value of nullopt, it will be disengaged.
+// For example,
+//   starboard::optional<int> my_int_optional(5);
+//   EXPECT_FALSE(!my_int_optional);
+//   my_int_optional = starboard::nullopt;
+//   EXPECT_TRUE(!my_int_optional);
+//
+struct nullopt_t {
+  nullopt_t() {}
+};
+extern const nullopt_t nullopt;
+
+// The in_place_t type is used to signal in-place construction of the internal
+// object.  This is used by the in place constructor of optional, which forwards
+// its parameters to the internal object's constructor.
+// For example,
+//   class Foo {
+//    public:
+//     Foo(int x, int y) { x_ = x; y_ = y; }
+//     int x() const { return x_; }
+//     int y() const { return y_; }
+//
+//    private:
+//     int x_;
+//     int y_;
+//   };
+//
+//   ...
+//
+//   base::optional<Foo> my_foo(base::in_place, 2, 3);
+//   EXPECT_FALSE(!my_foo);
+//   EXPECT_EQ(2, my_foo->x());
+//   EXPECT_EQ(3, my_foo->y());
+//
+struct in_place_t {
+  in_place_t() {}
+};
+extern const in_place_t in_place;
+
+template <typename T>
+class SB_EXPORT optional {
+ public:
+  // Construction via the default constructor results in an optional that is
+  // not engaged.
+  optional() { InitializeAsDisengaged(); }
+
+  optional(nullopt_t) { InitializeAsDisengaged(); }  // NOLINT(runtime/explicit)
+
+  // This non-explicit singleton constructor is provided so users can pass in a
+  // T wherever a optional<T> is expected.
+  optional(const T& value) { SetValue(value); }  // NOLINT(runtime/explicit)
+
+  optional(const optional<T>& other) {  // NOLINT(runtime/explicit)
+    if (other.engaged_) {
+      SetValue(other.value());
+    } else {
+      InitializeAsDisengaged();
+    }
+  }
+
+  // Move constructor.
+  // NOLINTNEXTLINE(runtime/explicit)
+  optional(optional&& other) {  // NOLINT(build/c++11)
+    if (other.engaged_) {
+      SetValue(std::move(other.value()));
+    } else {
+      InitializeAsDisengaged();
+    }
+  }
+
+  // Move value constructor.
+  // NOLINTNEXTLINE(runtime/explicit)
+  optional(T&& other) {  // NOLINT(build/c++11)
+    SetValue(std::move(other));
+  }
+
+  // Destruct contained object upon optional's destruction.
+  ~optional() { EnsureDisengaged(); }
+
+  // Disengages the optional, calling the destructor of the contained object
+  // if it is engaged.
+  optional<T>& operator=(nullopt_t) {
+    EnsureDisengaged();
+    return *this;
+  }
+
+  // Reassigns the underlying optional to value passed in on the right hand
+  // side.  This will destruct the lhs contained object first if it exists.
+  template <typename U>
+  optional<T>& operator=(const U& other) {
+    if (engaged_) {
+      value() = other;
+    } else {
+      SetValue(other);
+    }
+    return *this;
+  }
+
+  // Copy assignment.
+  optional<T>& operator=(const optional<T>& other) {
+    if (engaged_ && other.engaged_) {
+      value() = other.value();
+    } else if (!engaged_ && other.engaged_) {
+      SetValue(other.value());
+    } else if (engaged_ && !other.engaged_) {
+      EnsureDisengaged();
+    }
+    // Do nothing if lhs and rhs are both not engaged.
+    return *this;
+  }
+
+  // Move value assignment.
+  optional<T>& operator=(T&& other) {  // NOLINT(build/c++11)
+    if (engaged_) {
+      value() = std::move(other);
+    } else {
+      SetValue(std::move(other));
+    }
+    return *this;
+  }
+
+  // Move optional assignment.
+  optional<T>& operator=(optional&& other) {  // NOLINT(build/c++11)
+    if (engaged_ && other.engaged_) {
+      value() = std::move(other.value());
+    } else if (!engaged_ && other.engaged_) {
+      SetValue(std::move(other.value()));
+    } else if (engaged_ && !other.engaged_) {
+      EnsureDisengaged();
+    }
+    // Do nothing if lhs and rhs are both not engaged.
+    return *this;
+  }
+
+// Overloaded conversion to bool operator for determining whether the optional
+// is engaged or not.  It returns true if the optional is engaged, and false
+// otherwise.
+#if (defined(_MSC_VER) && (_MSC_VER < 1800)) || \
+    (defined(__GNUC__) &&                       \
+     (__GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 5))))
+  // MSVC 2012 does not support explicit cast operators.
+  // http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
+
+  // For any compiler that doesn't support explicit bool operators, we instead
+  // use the Safe Bool Idiom: http://www.artima.com/cppsource/safebool.html
+ private:
+  // The type of SafeBoolIdiomType (pointer to data member of a private type) is
+  // limited in functionality so much that the only thing a user can do with it
+  // is test for null, or apply to operator==/operator!=.  Since both operators
+  // == and != are already overloaded for optional, this leaves null tests,
+  // which we use for boolean testing.
+  class PrivateSafeBoolIdiomFakeMemberType;
+  typedef PrivateSafeBoolIdiomFakeMemberType optional::*SafeBoolIdiomType;
+
+ public:
+  operator const SafeBoolIdiomType() const {
+    // If we wish to return true, we cast engaged_ to our private type giving
+    // a non-null pointer to data member.  Otherwise, we return NULL.  The
+    // only thing the user can do with the return type is test for NULL.
+    return engaged_
+               ? reinterpret_cast<const SafeBoolIdiomType>(&optional::engaged_)
+               : NULL;
+  }
+#else
+  explicit operator bool() const { return engaged_; }
+#endif
+
+  // Dereferences the internal object.
+  const T* operator->() const { return &(value()); }
+
+  T* operator->() { return &(value()); }
+
+  const T& operator*() const { return value(); }
+
+  T& operator*() { return value(); }
+
+  // Dereferences and returns the internal object.
+  const T& value() const {
+    SB_DCHECK(engaged_)
+        << "Attempted to access object in a disengaged optional.";
+    return *static_cast<const T*>(void_value());
+  }
+
+  T& value() {
+    SB_DCHECK(engaged_)
+        << "Attempted to access object in a disengaged optional.";
+    return *static_cast<T*>(void_value());
+  }
+
+  template <typename U>
+  T value_or(const U& value) const {
+    if (engaged_) {
+      return this->value();
+    } else {
+      return value;
+    }
+  }
+
+  // Swaps the values of two optionals.
+  void swap(optional<T>& other) {
+    if (engaged_ && other.engaged_) {
+      // Swap the value contents with each other.
+      std::swap(value(), other.value());
+    } else if (engaged_) {
+      other.SetValue(std::move(value()));
+      EnsureDisengaged();
+    } else if (other.engaged_) {
+      SetValue(std::move(other.value()));
+      other.EnsureDisengaged();
+    }
+    // If both the lhs and rhs are not engaged, we do nothing.
+  }
+
+// Include the pump.py-generated declaration and implementation for the
+// forwarding constructor and emplace.
+#include "starboard/common/optional_internal.h"
+
+ private:
+  // Sets a non-engaged optional to a specified value, and marks it as engaged.
+  void SetValue(T&& value) {  // NOLINT(build/c++11)
+    new (void_value()) T(std::move(value));
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+  // Sets a non-engaged optional to a specified value, and marks it as engaged.
+  template <typename U>
+  void SetValue(const U& value) {
+    new (void_value()) T(value);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+  // If an optional is engaged, it destructs the wrapped value and marks the
+  // optional as disengaged.  Does nothing to a disengaged optional.
+  void EnsureDisengaged() {
+    if (engaged_) {
+      static_cast<T*>(void_value())->~T();
+      engaged_ = false;
+#if !defined(NDEBUG)
+      value_ptr_ = NULL;
+#endif
+    }
+  }
+
+  // Called upon object construction to initialize the object into a disengaged
+  // state.
+  void InitializeAsDisengaged() {
+    engaged_ = false;
+#if !defined(NDEBUG)
+    value_ptr_ = NULL;
+#endif
+  }
+
+  const void* void_value() const {
+    return reinterpret_cast<const void*>(value_memory_);
+  }
+  void* void_value() { return reinterpret_cast<void*>(value_memory_); }
+
+  // The actual memory reserved for the object that may or may not exist.
+  SB_ALIGNAS(SB_ALIGNOF(T)) uint8_t value_memory_[sizeof(T)];
+  // This boolean tracks whether or not the object is constructed yet or not.
+  bool engaged_;
+#if !defined(NDEBUG)
+  // In debug builds, this member makes it easy to inspect the value contained
+  // in the optional via a debugger.
+  const T* value_ptr_;
+#endif
+};
+
+// Comparison between 2 optionals
+template <typename T>
+inline bool operator==(const optional<T>& lhs, const optional<T>& rhs) {
+  if (!lhs) {
+    return !rhs;
+  }
+
+  return rhs == lhs.value();
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& lhs, const optional<T>& rhs) {
+  if (lhs && rhs) {
+    return lhs.value() < rhs.value();
+  } else {
+    // Handle all other cases simply in terms of whether the optionals are
+    // engaged or not.
+    return static_cast<bool>(lhs) < static_cast<bool>(rhs);
+  }
+}
+
+// Comparison with nullopt_t
+template <typename T>
+inline bool operator==(nullopt_t, const optional<T>& rhs) {
+  return !rhs;
+}
+
+template <typename T>
+inline bool operator==(const optional<T>& lhs, nullopt_t rhs) {
+  return rhs == lhs;
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& /* lhs */, nullopt_t) {
+  return false;
+}
+
+template <typename T>
+inline bool operator<(nullopt_t, const optional<T>& rhs) {
+  return static_cast<bool>(rhs);
+}
+
+// Comparison between an optional and a value
+template <typename T>
+inline bool operator==(const optional<T>& lhs, const T& rhs) {
+  return (!lhs ? false : lhs.value() == rhs);
+}
+
+template <typename T>
+inline bool operator==(const T& lhs, const optional<T>& rhs) {
+  return rhs == lhs;
+}
+
+template <typename T>
+inline bool operator<(const T& lhs, const optional<T>& rhs) {
+  return rhs && lhs < rhs.value();
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& lhs, const T& rhs) {
+  return !lhs || lhs.value() < rhs;
+}
+
+// This is a convenient but non-standard method, do not rely on it if you expect
+// the compatibility with upcoming C++ versions.
+template <typename T>
+inline std::ostream& operator<<(std::ostream& stream,
+                                const optional<T>& maybe_value) {
+  if (maybe_value) {
+    stream << *maybe_value;
+  } else {
+    stream << "nullopt";
+  }
+  return stream;
+}
+
+template <typename T>
+optional<T> make_optional(const T& value) {
+  return optional<T>(value);
+}
+
+}  // namespace starboard
+
+namespace std {
+
+template <typename T>
+struct hash<::starboard::optional<T> > {
+ public:
+  size_t operator()(const ::starboard::optional<T>& value) const {
+    return (value ? value_hash_(value.value()) : 0);
+  }
+
+ private:
+  hash<T> value_hash_;
+};
+
+template <typename T>
+void swap(::starboard::optional<T>& lhs, ::starboard::optional<T>& rhs) {
+  lhs.swap(rhs);
+}
+
+}  // namespace std
+
+#endif  // STARBOARD_COMMON_OPTIONAL_H_
diff --git a/src/starboard/common/optional_internal.h b/src/starboard/common/optional_internal.h
new file mode 100644
index 0000000..355e1db
--- /dev/null
+++ b/src/starboard/common/optional_internal.h
@@ -0,0 +1,180 @@
+// This file was GENERATED by command:
+//     pump.py optional_internal.h.pump
+// DO NOT EDIT BY HAND!!!
+
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// clang-format off
+// Begin forwarding constructor definitions ////////////////////////////////////
+  explicit optional(in_place_t) {
+    InitializeAsDisengaged();
+    new (void_value()) T();
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1>
+  explicit optional(in_place_t, const P1& p1) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2>
+  explicit optional(in_place_t, const P1& p1, const P2& p2) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4, const P5& p5) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4, const P5& p5, const P6& p6) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6, typename P7>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4, const P5& p5, const P6& p6, const P7& p7) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6, p7);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+// End forwarding constructor definitions //////////////////////////////////////
+
+// Begin emplace(...) definitions //////////////////////////////////////////////
+  void emplace() {
+    EnsureDisengaged();
+    new (void_value()) T();
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1>
+  void emplace(const P1& p1) {
+    EnsureDisengaged();
+    new (void_value()) T(p1);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2>
+  void emplace(const P1& p1, const P2& p2) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3>
+  void emplace(const P1& p1, const P2& p2, const P3& p3) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+      const P5& p5) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+      const P5& p5, const P6& p6) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6, typename P7>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+      const P5& p5, const P6& p6, const P7& p7) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6, p7);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+// End emplace(...) definitions ////////////////////////////////////////////////
+// clang-format on
diff --git a/src/starboard/common/optional_internal.h.pump b/src/starboard/common/optional_internal.h.pump
new file mode 100644
index 0000000..aaf152a
--- /dev/null
+++ b/src/starboard/common/optional_internal.h.pump
@@ -0,0 +1,74 @@
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+$$ This is a pump file for generating the interface and implementation of
+$$ optional's forwarding constructor and emplace() method.  Pump is a python
+$$ script that is part of the Google Test suite of utilities.  Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$var MAX_ARITY = 7
+
+$range ARITY 0..MAX_ARITY
+
+// clang-format off
+// Begin forwarding constructor definitions ////////////////////////////////////
+
+$for ARITY [[
+
+$range ARG 1..ARITY
+
+$if ARITY > 0 [[
+  template <$for ARG , [[typename P$(ARG)]]>
+
+]]
+  explicit optional(in_place_t$if ARITY > 0 [[, ]]
+                    $for ARG , [[const P$(ARG)& p$(ARG)]]) {
+    InitializeAsDisengaged();
+    new (void_value()) T($for ARG , [[p$(ARG)]]);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+]]
+// End forwarding constructor definitions //////////////////////////////////////
+
+// Begin emplace(...) definitions //////////////////////////////////////////////
+
+$for ARITY [[
+
+$range ARG 1..ARITY
+
+$if ARITY > 0 [[
+  template <$for ARG , [[typename P$(ARG)]]>
+
+]]
+  void emplace($for ARG , [[const P$(ARG)& p$(ARG)]]) {
+    EnsureDisengaged();
+    new (void_value()) T($for ARG , [[p$(ARG)]]);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+]]
+// End emplace(...) definitions ////////////////////////////////////////////////
+// clang-format on
\ No newline at end of file
diff --git a/src/base/state_machine_shell.cc b/src/starboard/common/state_machine.cc
similarity index 64%
rename from src/base/state_machine_shell.cc
rename to src/starboard/common/state_machine.cc
index 29a8786..a87e0c7 100644
--- a/src/base/state_machine_shell.cc
+++ b/src/starboard/common/state_machine.cc
@@ -2,30 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/state_machine_shell.h"
+#include "starboard/common/state_machine.h"
 
-#include "base/logging.h"
+#include <algorithm>
+
+#include "starboard/log.h"
 
 // --- Macros for common logging idioms ---
 
 #define SM_STATE(s) GetStateString(s) << "(" << s << ")"
 #define SM_STATEN(s) GetStateString(s) << "(" << s.value_or(0) << ")"
 #define SM_EVENT(e) GetEventString(e) << "(" << e << ")"
-#define SM_PREFIX \
-    name_ << "-" << SM_STATEN(state_) << "v" << version_ << ": "
-#define SM_DLOG(level) DLOG(level) << SM_PREFIX
+#define SM_PREFIX name_ << "-" << SM_STATEN(state_) << "v" << version_ << ": "
+#define SM_DLOG(level) SB_DLOG(level) << SM_PREFIX
 
-namespace base {
+namespace starboard {
 
-StateMachineBaseShell::StateMachineBaseShell(const std::string &name)
+StateMachineBase::StateMachineBase(const std::string& name)
     : name_(name),
       version_(0),
       is_handling_(false),
       is_initialized_(false),
-      should_log_(false) {
-}
+      should_log_(false) {}
 
-void StateMachineBaseShell::Initialize() {
+void StateMachineBase::Initialize() {
   if (is_initialized_) {
     return;
   }
@@ -33,14 +33,14 @@
   if (should_log_) {
     SM_DLOG(INFO) << "INITIALIZING";
   }
-  DCHECK(!state_) << SM_PREFIX;
-  DCHECK(!GetParentState(GetUserInitialState())) << SM_PREFIX;
+  SB_DCHECK(!state_) << SM_PREFIX;
+  SB_DCHECK(!GetParentState(GetUserInitialState())) << SM_PREFIX;
   is_initialized_ = true;
   FollowInitialSubstates();
 }
 
-bool StateMachineBaseShell::IsIn(State state) const {
-  StateN currentState = state_;
+bool StateMachineBase::IsIn(State state) const {
+  optional<State> currentState = state_;
   while (currentState) {
     if (state == currentState.value()) {
       return true;
@@ -52,12 +52,12 @@
   return false;
 }
 
-const char *StateMachineBaseShell::GetStateString(StateN state) const {
+const char* StateMachineBase::GetStateString(optional<State> state) const {
   if (!state) {
     return "<none>";
   }
 
-  const char *user = GetUserStateString(state.value());
+  const char* user = GetUserStateString(state.value());
   if (user) {
     return user;
   }
@@ -65,12 +65,12 @@
   return "UNKNOWN_STATE";
 }
 
-const char *StateMachineBaseShell::GetEventString(EventN event) const {
+const char* StateMachineBase::GetEventString(optional<Event> event) const {
   if (!event) {
     return "<none>";
   }
 
-  const char *user = GetUserEventString(event.value());
+  const char* user = GetUserEventString(event.value());
   if (user) {
     return user;
   }
@@ -78,7 +78,7 @@
   return "UNKNOWN_EVENT";
 }
 
-void StateMachineBaseShell::Handle(Event event, void *data) {
+void StateMachineBase::Handle(Event event, void* data) {
   Initialize();
 
   if (is_handling_) {
@@ -86,8 +86,7 @@
       SM_DLOG(INFO) << "QUEUEING " << SM_EVENT(event);
     }
 
-    event_queue_.push(Bind(&StateMachineBaseShell::HandleOneEvent, Unretained(this),
-                           event, Unretained(data)));
+    event_queue_.push(EventWithData(event, data));
     return;
   }
 
@@ -95,42 +94,43 @@
   HandleQueuedEvents();
 }
 
-StateMachineBaseShell::StateN
-StateMachineBaseShell::GetParentState(StateN state) const {
+optional<StateMachineBase::State> StateMachineBase::GetParentState(
+    optional<State> state) const {
   if (!state) {
-    return StateN();
+    return optional<State>();
   }
 
   return GetUserParentState(state.value());
 }
 
-StateMachineBaseShell::StateN
-StateMachineBaseShell::GetInitialSubstate(StateN state) const {
+optional<StateMachineBase::State> StateMachineBase::GetInitialSubstate(
+    optional<State> state) const {
   if (!state) {
-    return StateN();
+    return optional<State>();
   }
 
   return GetUserInitialSubstate(state.value());
 }
 
-void StateMachineBaseShell::HandleQueuedEvents() {
+void StateMachineBase::HandleQueuedEvents() {
   while (!event_queue_.empty()) {
-    event_queue_.front().Run();
+    EventWithData& event = event_queue_.front();
+    HandleOneEvent(event.event, event.data);
     event_queue_.pop();
   }
 }
 
-void StateMachineBaseShell::HandleOneEvent(Event event, void *data) {
+void StateMachineBase::HandleOneEvent(Event event, void* data) {
   if (should_log_) {
     SM_DLOG(INFO) << "HANDLING " << SM_EVENT(event);
   }
 
-  DCHECK(!is_handling_) << SM_PREFIX;
+  SB_DCHECK(!is_handling_) << SM_PREFIX;
   is_handling_ = true;
 
   // Walk up the hierarchy from the simplest current state, looking for event
   // handlers, stopping at the first state that decides to handle the event.
-  StateN source = state_;
+  optional<State> source = state_;
   Result result;
   while (source) {
     result = HandleUserStateEvent(source.value(), event, data);
@@ -161,8 +161,8 @@
 
 // Returns the index of |state| in |path|, or -1 if not found in |path_length|
 // elements. If |state| is null, then it will always return -1.
-static int IndexOf(StateMachineBaseShell::StateN state,
-                   const StateMachineBaseShell::State *path,
+static int IndexOf(optional<StateMachineBase::State> state,
+                   const StateMachineBase::State* path,
                    size_t path_length) {
   if (!state) {
     return -1;
@@ -176,19 +176,22 @@
 
 // Finds the least common ancestor between the two state paths. If there is no
 // common ancestor, returns null.
-static StateMachineBaseShell::StateN FindLeastCommonAncestor(
-    const StateMachineBaseShell::State *path_a,
+static optional<StateMachineBase::State> FindLeastCommonAncestor(
+    const StateMachineBase::State* path_a,
     size_t length_a,
-    const StateMachineBaseShell::State *path_b,
+    const StateMachineBase::State* path_b,
     size_t length_b) {
   size_t max = std::min(length_a, length_b);
   size_t i;
-  for (i = 0; path_a[i] == path_b[i] && i < max; ++i) { }
-  return (i == 0 ? StateMachineBaseShell::StateN() : path_a[i - 1]);
+  for (i = 0; path_a[i] == path_b[i] && i < max; ++i) {
+  }
+  return (i == 0 ? optional<StateMachineBase::State>() : path_a[i - 1]);
 }
 
-void StateMachineBaseShell::GetPath(State state, size_t max_depth, State *out_path,
-                                    size_t *out_depth) const {
+void StateMachineBase::GetPath(State state,
+                               size_t max_depth,
+                               State* out_path,
+                               size_t* out_depth) const {
   if (max_depth == 0) {
     SM_DLOG(FATAL) << "max_depth must be > 0";
     if (out_depth) {
@@ -198,7 +201,7 @@
   }
 
   size_t depth = 0;
-  StateN currentState = state;
+  optional<State> currentState = state;
   while (currentState && depth < max_depth) {
     out_path[depth] = currentState.value();
     ++depth;
@@ -212,8 +215,10 @@
   }
 }
 
-void StateMachineBaseShell::Transition(Event event, State source, State target,
-                                       bool is_external) {
+void StateMachineBase::Transition(Event event,
+                                  State source,
+                                  State target,
+                                  bool is_external) {
   if (should_log_) {
     SM_DLOG(INFO) << SM_EVENT(event) << " caused " << SM_STATE(source) << " -> "
                   << SM_STATE(target) << (is_external ? " (external)" : "");
@@ -229,14 +234,13 @@
   State target_path[kMaxDepth];
   size_t target_depth;
   GetPath(target, kMaxDepth, target_path, &target_depth);
-  StateN least_common_ancestor =
-      FindLeastCommonAncestor(source_path, source_depth, target_path,
-                              target_depth);
+  optional<State> least_common_ancestor = FindLeastCommonAncestor(
+      source_path, source_depth, target_path, target_depth);
 
   // External transitions must exit the source state and enter the target
   // state, so if the LCA is one of the two, we need to go one level up.
-  if (is_external && (least_common_ancestor == source ||
-                      least_common_ancestor == target)) {
+  if (is_external &&
+      (least_common_ancestor == source || least_common_ancestor == target)) {
     least_common_ancestor = GetParentState(least_common_ancestor);
   }
 
@@ -250,7 +254,7 @@
 
   // Wind (enter) states down to the target state.
   size_t next_depth = IndexOf(state_, target_path, target_depth) + 1;
-  DCHECK_LE(next_depth, target_depth);
+  SB_DCHECK(next_depth <= target_depth);
   while (next_depth < target_depth) {
     EnterState(target_path[next_depth]);
     ++next_depth;
@@ -259,9 +263,9 @@
   FollowInitialSubstates();
 }
 
-void StateMachineBaseShell::FollowInitialSubstates() {
+void StateMachineBase::FollowInitialSubstates() {
   while (true) {
-    StateN substate =
+    optional<State> substate =
         (state_ ? GetInitialSubstate(state_) : GetUserInitialState());
     if (!substate) {
       break;
@@ -270,7 +274,7 @@
   }
 }
 
-void StateMachineBaseShell::EnterState(State state) {
+void StateMachineBase::EnterState(State state) {
   state_ = state;
 
   if (should_log_) {
@@ -280,7 +284,7 @@
   HandleUserStateEnter(state);
 }
 
-void StateMachineBaseShell::ExitCurrentState() {
+void StateMachineBase::ExitCurrentState() {
   if (should_log_) {
     SM_DLOG(INFO) << "EXIT";
   }
@@ -289,4 +293,4 @@
   state_ = GetParentState(state_);
 }
 
-}  // namespace base
+}  // namespace starboard
diff --git a/src/starboard/common/state_machine.h b/src/starboard/common/state_machine.h
new file mode 100644
index 0000000..017c06d
--- /dev/null
+++ b/src/starboard/common/state_machine.h
@@ -0,0 +1,517 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef STARBOARD_COMMON_STATE_MACHINE_H_
+#define STARBOARD_COMMON_STATE_MACHINE_H_
+
+#include <queue>
+#include <string>
+
+#include "starboard/common/optional.h"
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+
+#ifdef __cplusplus
+namespace starboard {
+
+// An approximate implementation of a run-to-completion (RTC) Hierarchical State
+// Machine (HSM), supporting most UML Statechart semantics.
+//
+// Some good background information on UML Statecharts, mostly written by Miro
+// Samek:
+// http://en.wikipedia.org/wiki/UML_state_machine
+//
+// And Miro Samek's 3-part article on practical UML Statecharts:
+// http://www.embedded.com/design/prototyping-and-development/4008247/A-crash-course-in-UML-state-machines-Part-1
+//
+// This class does not provide any intrinsic support for "orthogonal regions,"
+// "extended state," or "deferred events." "Guard conditions" are easily
+// implemented by the client directly in the event handler. It also minorly
+// deviates from the UML Statechart specification by calling transition handlers
+// before exiting the current states. This is because this implementation uses a
+// simplification which combines the transition handlers and the
+// state-event-transition map into a single function (HandleUserStateEvent).
+//
+// This class is not thread-safe. It is the user's responsibility to synchronize
+// calls into the state machine, either with locks or by simply using from a
+// single thread.
+//
+// Terse suggestions for using this class:
+//   * Use the templated StateMachine wrapper class instead of this class
+//     directly.
+//   * Define two enums, one for your states, and one for your events.
+//   * Subclass to define your state machine and event handlers.
+//   * Avoid directly exposing or passing around state machines (wrap instead).
+//     Handle() is not a great public interface.
+//   * Include your state machine in another class as a private by-value member.
+//   * Synchronize access to the state machine.
+//   * Prefer writing state machine event handlers over checking if the machine
+//     IsIn() a particular state.
+//   * Convert public methods into events, get into the state machine as quickly
+//     as possible.
+//   * You only need to define a state when you are going to leave the state
+//     machine in a particular condition where events can occur.
+//   * Create a superstate when you have an event you want to handle the same
+//     way for a collection of states.
+//   * When managing resources, create a state or superstate that represents the
+//     acquisition of that resource, and release the resource in the Exit
+//     handler.
+//
+// Some Definitions:
+//   Simple State      - A State with no substates. The state machine is always
+//                       left in exactly one Simple State.
+//   Composite State   - A State with at least one substate.
+//   Guard Condition   - An external condition on which an event handler
+//                       branches.
+//   Run-To-Completion - A property specifying that the state machine handles
+//                       one event at a time, and no events are handled until
+//                       the previous event is done being handled.
+//
+// See the unittests for this class for a contrived example state machine
+// implementation.
+class StateMachineBase {
+ public:
+  // --- Nested Types and Constants ---
+
+  typedef uint32_t State;
+  typedef uint32_t Event;
+
+  // --- Public Methods ---
+
+  // Constructor with name. The name is helpful for debugging.
+  explicit StateMachineBase(const std::string& name);
+  virtual ~StateMachineBase() {}
+
+  // Enters the initial state, as specified by |GetUserInitialState()| and
+  // follows the initial substates down to the first simple (childless) state
+  // found. Idempotent. Will happen automatically on the first |Handle()| call,
+  // if not done explicitly.
+  void Initialize();
+
+  // Gets the name of this state machine, for logging purposes.
+  const char* name() const { return name_.c_str(); }
+
+  // Gets a version number that increases monotonically with each state
+  // transition (unless it overflows |uint64_t|). This can be useful for timers
+  // and asynchronous events that only want to do their action if the state has
+  // not changed since they were queued.
+  //
+  // The state version changes exactly once per transition. In other words, it
+  // doesn't change for every Enter and Exit for a transition.
+  uint64_t version() const { return version_; }
+
+  // Gets the simplest current state that this state machine is in. To check if
+  // the state machine is in a particular composite state, use |IsIn()|. Returns
+  // null if uninitialized.
+  optional<State> state() const { return state_; }
+
+  // Reports whether the state machine is currently in the given state. A state
+  // machine is considered to be "in" the current simple state, but also "in"
+  // all superstates of the current simple state.
+  bool IsIn(State state) const;
+
+  // Gets a printable string for the given state.
+  const char* GetStateString(optional<State> state) const;
+
+  // Gets a printable string for the given event.
+  const char* GetEventString(optional<Event> event) const;
+
+  // Handles the given event in the context of the state machine's current
+  // state, executing the appropriate event handlers and performing any
+  // precipitate state transitions. If called reentrantly, the event will be
+  // queued until the current event has run to completion.
+  //
+  // |data| is a way to pass auxiliary data to the event handler. No retention
+  // or ref-counting is done on that data, it is simply passed through to the
+  // event handler. Since |Handle()| will queue the event if (and only if)
+  // called from an event handler, it is up to the caller to ensure the lifetime
+  // of whatever is passed in here.
+  void Handle(Event event, void* data = NULL);
+
+ protected:
+  // --- Protected Nested Types ---
+
+  // A type that can be returned from a state-event handler, which will be
+  // coerced into the appropriate Result structure.
+  enum HandledState {
+    // The event handler returns this to say that the handler consume the event,
+    // but caused no state transition.
+    kHandled,
+
+    // The event handler returns this to say that the handler did not consume
+    // the event, and it should bubble up to the parent state, if any.
+    kNotHandled,
+  };
+
+  // Structure that handlers return, allowing them to cause state transitions or
+  // prevent the event from bubbling. State-event handlers may just return a
+  // HandledState or a state to transition to, and it will be coerced into this
+  // structure.
+  struct Result {
+    // Default constructor is unhandled.
+    Result() : is_transition(false), is_external(false), is_handled(false) {}
+
+    // The no-transition constructor. Non-explicit so that the implementor of
+    // |HandleUserStateEvent()| just needs to return a |HandledState| and it
+    // does the right thing.
+    Result(HandledState handled)  // NOLINT(runtime/explicit)
+        : is_transition(false),
+          is_external(false),
+          is_handled(handled == kHandled) {}
+
+    // The state transition constructor. This implies that the event was
+    // handled. Non-explicit so that the implementor of |HandleUserStateEvent()|
+    // just needs to return a State and it does a non-external transition.
+    Result(State transition_target,
+           bool external = false)  // NOLINT(runtime/explicit)
+        : target(transition_target),
+          is_transition(true),
+          is_external(external),
+          is_handled(true) {}
+
+    Result& operator=(HandledState rhs) {
+      target = nullopt;
+      is_transition = false;
+      is_external = false;
+      is_handled = (rhs == kHandled);
+      return *this;
+    }
+
+    Result& operator=(State transition_target) {
+      target = transition_target;
+      is_transition = true;
+      is_external = false;
+      is_handled = true;
+      return *this;
+    }
+
+    // State to transition to. Only valid if is_transition is true.
+    optional<State> target;
+
+    // Whether this result indicates a state transition.
+    bool is_transition;
+
+    // Whether the specified transition is external. Only meaningful if
+    // is_transition is true.
+    //
+    // For more on state transitions, see:
+    // http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
+    bool is_external;
+
+    // Whether the event was handled by the handler. If true, consumes the
+    // event, and prevents bubbling up to superstates. False propagates the
+    // event to superstates.
+    bool is_handled;
+  };
+
+  struct EventWithData {
+    EventWithData(Event event, void* data) : event(event), data(data) {}
+    Event event;
+    void* data;
+  };
+
+  // --- Implementation Interface for Subclasses ---
+
+  // Abstract method for subclasses to define the state hierarchy. It is
+  // recommended that all user-defined states be descendents of a single "top"
+  // state.
+  virtual optional<State> GetUserParentState(State state) const = 0;
+
+  // Abstract method for subclasses to define the initial substate of their
+  // states, which is required for all complex states. If a state is a simple
+  // state (no substates), returns null.
+  virtual optional<State> GetUserInitialSubstate(State state) const = 0;
+
+  // Abstract method for subclasses to define the initial (top) state of their
+  // state machine. This must be a state that has no parent. This state will be
+  // entered upon calling |Initialize()|.
+  virtual State GetUserInitialState() const = 0;
+
+  // Optional method for subclasses to define strings for their states, solely
+  // used for debugging purposes.
+  virtual const char* GetUserStateString(State state) const { return NULL; }
+
+  // Optional method for subclasses to define strings for their events, solely
+  // used for debugging purposes.
+  virtual const char* GetUserEventString(Event event) const { return NULL; }
+
+  // Abstract method for subclasses to define handlers for events in given
+  // states. This method must return either:
+  //   a) a state to transition to, meaning the event was handled and caused
+  //      a transition
+  //   b) |kHandled| meaning the event was handled but no transition occurred
+  //   c) |kNotHandled|, meaning the event should bubble up to the parent state
+  //   d) an explicit Result structure, mainly for external transitions.
+  //
+  // Implementations wishing to catch all unhandled events may do so in their
+  // top state.
+  //
+  // This method is generally implemented as a nested switch statement, with the
+  // outer switch on |state| and the inner switch on |event|. You may want to
+  // break this apart into per-state or per-state-event functions for
+  // readability and maintainability.
+  virtual Result HandleUserStateEvent(State state, Event event, void* data) = 0;
+
+  // Abstract method for subclasses to define state entry behaviors.
+  virtual void HandleUserStateEnter(State state) = 0;
+
+  // Abstract method for subclasses to define state exit behaviors.
+  virtual void HandleUserStateExit(State state) = 0;
+
+  // --- Helper Methods for Subclasses ---
+
+  // Subclasses can call this method to turn on logging. Logging is opt-in,
+  // because it can be very verbose, and is likely only useful during
+  // development of a particular state machine.
+  void EnableLogging() { should_log_ = true; }
+
+ private:
+  // --- Private Helper Methods ---
+
+  // Gets the parent state of the given state.
+  optional<State> GetParentState(optional<State> state) const;
+
+  // Gets the initial substate of given state.
+  optional<State> GetInitialSubstate(optional<State> state) const;
+
+  // Handles all queued events until there are no more to run. Event handlers
+  // may queue more events by calling |Handle()|, and this method will also
+  // process all precipitate events.
+  void HandleQueuedEvents();
+
+  // Workhorse method for handling a single event.
+  void HandleOneEvent(Event event, void* data);
+
+  // Gets the path from the Top state to the given |state|, storing it in the
+  // given |out_path| array, up to |max_depth| entries. If specified,
+  // |out_depth| will be set to the number of valid states that fit in the given
+  // array.
+  void GetPath(State state,
+               size_t max_depth,
+               State* out_path,
+               size_t* out_depth) const;
+
+  // Transitions between the given source and target states, assuming that the
+  // source state is in the current state path to the Top state. The source
+  // state is the state whose handler generated the transition.
+  //
+  // See:
+  // http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
+  void Transition(Event event, State source, State target, bool is_external);
+
+  // Follows the initial substates from the current state until it reaches a
+  // simple state.
+  void FollowInitialSubstates();
+
+  // Enters the given state.
+  void EnterState(State state);
+
+  // Exits the current state to its parent.
+  void ExitCurrentState();
+
+  // --- Members ---
+
+  // The name of this state machine, for debugging purposes.
+  const std::string name_;
+
+  // The current state of this state machine. Null until initialized.
+  optional<State> state_;
+
+  // The unique version of this state machine's state, updated on every
+  // transition.
+  uint64_t version_;
+
+  // Queue of events to handle once the current event is done being
+  // handled. Should always be empty unless |is_handling_| is true.
+  std::queue<EventWithData> event_queue_;
+
+  // Whether this state machine is actively handling an event. Used to detect
+  // reentrant calls to |Handle()|.
+  bool is_handling_;
+
+  // Whether the state machine has been initialized into its initial state
+  // yet. Used to make |Initialize()| idempotent.
+  bool is_initialized_;
+
+  // Whether the state machine should log information about state transitions.
+  bool should_log_;
+};
+
+// A convenience template wrapper for StateMachineBase. See the above class
+// for complete documentation. Basically, you define your states and events as
+// two enums, and then pass them as template args to this template class. Your
+// state machine should then subclass this template class. It then does the work
+// of casting and converting back and forth from your enums to
+// StateMachineBase's numeric State and Event definitions.
+//
+// All the methods in this class, protected and public, match the description
+// and behavioral contracts of the equivalently named method in
+// StateMachineBase.
+template <typename StateEnum, typename EventEnum>
+class StateMachine {
+ public:
+  // --- Nested Types and Constants ---
+
+  explicit StateMachine(const std::string& name) : machine_(this, name) {}
+  virtual ~StateMachine() {}
+
+  void Initialize() { machine_.Initialize(); }
+
+  const char* name() const { return machine_.name(); }
+
+  uint64_t version() const { return machine_.version(); }
+
+  optional<StateEnum> state() const {
+    optional<BaseState> wrappedState = machine_.state();
+    return (wrappedState ? static_cast<StateEnum>(*wrappedState)
+                         : optional<StateEnum>());
+  }
+
+  bool IsIn(StateEnum state) const {
+    return machine_.IsIn(static_cast<BaseState>(state));
+  }
+
+  const char* GetStateString(optional<StateEnum> state) const {
+    return machine_.GetStateString(state ? static_cast<BaseState>(*state)
+                                         : optional<BaseState>());
+  }
+
+  const char* GetEventString(optional<EventEnum> event) const {
+    return machine_.GetEventString(event ? static_cast<BaseEvent>(*event)
+                                         : optional<BaseEvent>());
+  }
+
+  void Handle(EventEnum event, void* data = NULL) {
+    machine_.Handle(static_cast<BaseEvent>(event), data);
+  }
+
+ protected:
+  // See the other HandledState in StateMachineBase.
+  enum HandledState {
+    kHandled,
+    kNotHandled,
+  };
+
+  // See the other Result in StateMachineBase.
+  struct Result {
+    Result(HandledState handled)  // NOLINT(runtime/explicit)
+        : target(),
+          is_transition(false),
+          is_external(false),
+          is_handled(handled == kHandled) {}
+
+    Result(StateEnum transition_target,
+           bool external = false)  // NOLINT(runtime/explicit)
+        : target(transition_target),
+          is_transition(true),
+          is_external(external),
+          is_handled(true) {}
+
+    Result& operator=(HandledState rhs) {
+      target = nullopt;
+      is_transition = false;
+      is_external = false;
+      is_handled = (rhs == kHandled);
+      return *this;
+    }
+
+    Result& operator=(StateEnum transition_target) {
+      target = transition_target;
+      is_transition = true;
+      is_external = false;
+      is_handled = true;
+      return *this;
+    }
+
+    optional<StateEnum> target;
+    bool is_transition;
+    bool is_external;
+    bool is_handled;
+  };
+
+  virtual optional<StateEnum> GetUserParentState(StateEnum state) const = 0;
+  virtual optional<StateEnum> GetUserInitialSubstate(StateEnum state) const = 0;
+  virtual StateEnum GetUserInitialState() const = 0;
+  virtual const char* GetUserStateString(StateEnum state) const { return NULL; }
+  virtual const char* GetUserEventString(EventEnum event) const { return NULL; }
+  virtual Result HandleUserStateEvent(StateEnum state,
+                                      EventEnum event,
+                                      void* data) = 0;
+  virtual void HandleUserStateEnter(StateEnum state) = 0;
+  virtual void HandleUserStateExit(StateEnum state) = 0;
+
+  void EnableLogging() { machine_.EnableLoggingPublic(); }
+
+ private:
+  typedef StateMachineBase::State BaseState;
+  typedef StateMachineBase::Event BaseEvent;
+
+  // Private contained subclass that forwards and adapts all virtual methods
+  // into this class's equivalent virtual methods.
+  class WrappedMachine : public StateMachineBase {
+   public:
+    WrappedMachine(StateMachine<StateEnum, EventEnum>* wrapper,
+                   const std::string& name)
+        : StateMachineBase(name), wrapper_(wrapper) {}
+
+    optional<State> GetUserParentState(State state) const SB_OVERRIDE {
+      optional<StateEnum> result =
+          wrapper_->GetUserParentState(static_cast<StateEnum>(state));
+      return (result ? static_cast<State>(*result) : optional<State>());
+    }
+
+    optional<State> GetUserInitialSubstate(State state) const SB_OVERRIDE {
+      optional<StateEnum> result =
+          wrapper_->GetUserInitialSubstate(static_cast<StateEnum>(state));
+      return (result ? static_cast<State>(*result) : optional<State>());
+    }
+
+    State GetUserInitialState() const SB_OVERRIDE {
+      return static_cast<State>(wrapper_->GetUserInitialState());
+    }
+
+    const char* GetUserStateString(State state) const SB_OVERRIDE {
+      return wrapper_->GetUserStateString(static_cast<StateEnum>(state));
+    }
+
+    const char* GetUserEventString(Event event) const SB_OVERRIDE {
+      return wrapper_->GetUserEventString(static_cast<EventEnum>(event));
+    }
+
+    Result HandleUserStateEvent(State state,
+                                Event event,
+                                void* data) SB_OVERRIDE {
+      typename StateMachine<StateEnum, EventEnum>::Result result =
+          wrapper_->HandleUserStateEvent(static_cast<StateEnum>(state),
+                                         static_cast<EventEnum>(event), data);
+      if (result.is_transition) {
+        return Result(static_cast<State>(*result.target), result.is_external);
+      }
+
+      return result.is_handled ? kHandled : kNotHandled;
+    }
+
+    void HandleUserStateEnter(State state) SB_OVERRIDE {
+      wrapper_->HandleUserStateEnter(static_cast<StateEnum>(state));
+    }
+
+    void HandleUserStateExit(State state) SB_OVERRIDE {
+      wrapper_->HandleUserStateExit(static_cast<StateEnum>(state));
+    }
+
+    // A public exposure of EnableLogging so the wrapper can access it. Since
+    // this class is private to the wrapper, it is the only one who can see it.
+    void EnableLoggingPublic() { EnableLogging(); }
+
+   private:
+    StateMachine<StateEnum, EventEnum>* wrapper_;
+  };
+
+  WrappedMachine machine_;
+};
+
+}  // namespace starboard
+#endif  // __cplusplus
+
+#endif  // STARBOARD_COMMON_STATE_MACHINE_H_
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index d6007c9..cb8d84a 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -64,6 +64,15 @@
 //   //   exposes functionality for my new feature.
 //   #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// SbDecodeTargetInfoPlane's now can specify color plane information.
+// Previously: Planes of type kSbDecodeTargetFormat2PlaneYUVNV12
+// were assumed to have the luma mapped to the alpha channel (GL_ALPHA)
+// and the chroma mapped to blue and alpha (GL_LUMINANCE_ALPHA). However,
+// some graphics systems require that luma is on GL_RED_EXT and the chroma
+// is on GL_RG_EXT.
+
+#define SB_DECODE_TARGET_PLANE_FORMAT_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // --- Release Candidate Feature Defines -------------------------------------
 
 #define SB_POINTER_INPUT_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
@@ -78,6 +87,9 @@
 #define SB_LOW_MEMORY_EVENT_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
 #define SB_PLAYER_WRITE_SAMPLE_EXTRA_CONST_API_VERSION \
   SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION \
+  SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_STORAGE_NAMES_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
 
 // --- Common Detected Features ----------------------------------------------
 
@@ -312,6 +324,32 @@
 #endif  // SB_NORETURN
 #endif  // SB_API_VERSION >= 4
 
+// Specifies the alignment for a class, struct, union, enum, class/struct field,
+// or stack variable.
+#if !defined(SB_ALIGNAS)
+#if SB_IS(COMPILER_GCC)
+#define SB_ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#elif SB_IS(COMPILER_MSVC)
+#define SB_ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#else
+// Fallback to the C++11 form.
+#define SB_ALIGNAS(byte_alignment) alignas(byte_alignment)
+#endif
+#endif  // !defined(SB_ALIGNAS)
+
+// Returns the alignment reqiured for any instance of the type indicated by
+// |type|.
+#if !defined(SB_ALIGNOF)
+#if SB_IS(COMPILER_GCC)
+#define SB_ALIGNOF(type) __alignof__(type)
+#elif SB_IS(COMPILER_MSVC)
+#define SB_ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#else
+// Fallback to the C++11 form.
+#define SB_ALIGNOF(type) alignof(type)
+#endif
+#endif  // !defined(SB_ALIGNOF)
+
 // --- Configuration Audits --------------------------------------------------
 
 #if !defined(SB_API_VERSION)
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index e109b6a..c437c96 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -287,6 +287,16 @@
   // Typically this would be GL_TEXTURE_2D, but some platforms may require
   // that it be set to something else like GL_TEXTURE_EXTERNAL_OES.
   uint32_t gl_texture_target;
+
+#if SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
+  // For kSbDecodeTargetFormat2PlaneYUVNV12 planes: the format of the
+  // texture. Usually, for the luma plane, this is either GL_ALPHA or
+  // GL_RED_EXT. For the chroma plane, this is usually GL_LUMINANCE_ALPHA
+  // or GL_RG_EXT.
+  // Ignored for other plane types.
+  uint32_t gl_texture_format;
+#endif  // SB_API_VERSION >= SB_DECODE_TARGET_NV12_R_RG_API_VERSION
+
 #endif               // SB_HAS(BLITTER)
 
   // The width of the texture/surface for this particular plane.
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index dad7d24..cae076d 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -52,6 +52,15 @@
   int32_t encrypted_byte_count;
 } SbDrmSubSampleMapping;
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+typedef struct SbDrmKeyId {
+  // The ID of the license (or key) required to decrypt this sample. For
+  // PlayReady, this is the license GUID in packed little-endian binary form.
+  uint8_t identifier[16];
+  int identifier_size;
+} SbDrmKeyId;
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 // All the optional information needed per sample for encrypted samples.
 typedef struct SbDrmSampleInfo {
   // The Initialization Vector needed to decrypt this sample.
@@ -113,6 +122,20 @@
                                         int session_id_size,
                                         bool succeeded);
 
+// A callback for notifications that the status of one or more keys in a session
+// has been changed.  All keys of the session and their new status will be
+// passed along.  Any keys not in the list is considered as deleted.
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+typedef void (*SbDrmSessionKeyStatusesChangedFunc)(
+    SbDrmSystem drm_system,
+    void* context,
+    const void* session_id,
+    int session_id_size,
+    int number_of_keys,
+    const SbDrmKeyId* key_ids,
+    const SbDrmKeyStatus* key_statuses);
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 // --- Constants -------------------------------------------------------------
 
 // An invalid SbDrmSystem.
@@ -156,12 +179,25 @@
 // SbDrmGenerateSessionUpdateRequest() is called.
 // |session_updated_callback|: A function that is called every time after
 // SbDrmUpdateSession() is called.
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
+SB_EXPORT SbDrmSystem SbDrmCreateSystem(
+    const char* key_system,
+    void* context,
+    SbDrmSessionUpdateRequestFunc update_request_callback,
+    SbDrmSessionUpdatedFunc session_updated_callback,
+    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback);
+
+#else  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 SB_EXPORT SbDrmSystem
 SbDrmCreateSystem(const char* key_system,
                   void* context,
                   SbDrmSessionUpdateRequestFunc update_request_callback,
                   SbDrmSessionUpdatedFunc session_updated_callback);
 
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUS_UPDATE_SUPPORT_API_VERSION
+
 // Asynchronously generates a session update request payload for
 // |initialization_data|, of |initialization_data_size|, in case sensitive
 // |type|, extracted from the media stream, in |drm_system|'s key system.
diff --git a/src/starboard/egl_and_gles/egl_and_gles_angle.gyp b/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
index 8812c25..52a9d96 100644
--- a/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
+++ b/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
@@ -13,6 +13,9 @@
 # limitations under the License.
 
 {
+  'variables': {
+    'enable_d3d11_feature_level_11%': 0,
+  },
   'targets': [
     {
       'target_name': 'egl_and_gles_implementation',
@@ -27,6 +30,18 @@
           '<(DEPTH)/third_party/angle/include',
         ],
       },
+      'conditions': [
+        # ANGLE supports GLES3 on Windows only if DirectX11 feauture level 11 is
+        # supported.
+        ['target_os=="win" and enable_d3d11_feature_level_11==1', {
+          'direct_dependent_settings': {
+            'defines': [
+              'GLES3_SUPPORTED',
+              'GL_GLEXT_PROTOTYPES',
+            ],
+          },
+        }]
+      ]
     },
   ],
 }
diff --git a/src/starboard/examples/BUILD.gn b/src/starboard/examples/BUILD.gn
new file mode 100644
index 0000000..0012617
--- /dev/null
+++ b/src/starboard/examples/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/base.gni")
+
+group("examples") {
+  deps = [
+    "//starboard/examples/blitter",
+    "//starboard/examples/window",
+  ]
+
+  if (gl_type != "none") {
+    deps += [
+      "//starboard/examples/glclear",
+    ]
+  }
+}
diff --git a/src/starboard/examples/blitter/BUILD.gn b/src/starboard/examples/blitter/BUILD.gn
new file mode 100644
index 0000000..403815f
--- /dev/null
+++ b/src/starboard/examples/blitter/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+final_executable("blitter") {
+  output_name = "starboard_blitter_example"
+
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//starboard",
+  ]
+}
+
+# TODO: add the deploy target if it's desirable
diff --git a/src/starboard/examples/glclear/BUILD.gn b/src/starboard/examples/glclear/BUILD.gn
new file mode 100644
index 0000000..d90f020
--- /dev/null
+++ b/src/starboard/examples/glclear/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+final_executable("glclear") {
+  output_name = "starboard_glclear_example"
+
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//starboard",
+    # TODO: convert this target over
+    # "//starboard/egl_and_gles",
+  ]
+}
+
+# TODO: add the deploy target if it's desirable
diff --git a/src/starboard/examples/window/BUILD.gn b/src/starboard/examples/window/BUILD.gn
new file mode 100644
index 0000000..b8a295a
--- /dev/null
+++ b/src/starboard/examples/window/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+final_executable("window") {
+  output_name = "starboard_window_example"
+
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//starboard",
+  ]
+}
+
+# TODO: add the deploy target if it's desirable
diff --git a/src/starboard/input.h b/src/starboard/input.h
index 0959a00..9c1e2cc 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -173,7 +173,10 @@
   unsigned int key_modifiers;
 
   // The (x, y) coordinates of the persistent cursor controlled by this device.
-  // The value is |0| if this data is not applicable.
+  // The value is |0| if this data is not applicable. For events with type
+  // kSbInputEventTypeMove and device_type kSbInputDeviceTypeGamepad, this field
+  // is interpreted as a stick position with the range [-1, 1], with positive
+  // values for the up and left direction.
   SbInputVector position;
 
   // The relative motion vector of this input. The value is |0| if this data is
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
new file mode 100644
index 0000000..66579d8
--- /dev/null
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -0,0 +1,608 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/base.gni")
+import("//starboard/build/config/fastbuild.gni")
+import("//starboard/build/config/sanitizers.gni")
+import("//starboard/build/toolchain/asan_symbolizer_path.gni")
+import("//starboard/build/delegated_config.gni")
+import("//starboard/linux/shared/clang.gni")
+import("//starboard/linux/shared/dlmalloc.gni")
+
+# =============================================================================
+# DEFAULT COMPILER CONFIGS
+# =============================================================================
+
+config("compiler_defaults") {
+  cflags = [
+     # We'll pretend not to be Linux, but Starboard instead.
+    "-U__linux__",
+  ]
+
+  cflags_c = [
+    # Limit to C99. This allows Linux to be a canary build for any
+    # C11 features that are not supported on some platforms' compilers.
+    "-std=c99",
+  ]
+
+  cflags_cc = [
+    "-std=gnu++11",
+  ]
+
+  libs = [
+    "asound",
+    "avcodec",
+    "avformat",
+    "avresample",
+    "avutil",
+    "pthread",
+    "rt",
+  ]
+
+  ldflags = []
+
+  if (use_dlmalloc_allocator && !use_asan) {
+    ldflags += [
+      "-Wl,--wrap=malloc",
+      "-Wl,--wrap=calloc",
+      "-Wl,--wrap=realloc",
+      "-Wl,--wrap=memalign",
+      "-Wl,--wrap=reallocalign",
+      "-Wl,--wrap=free",
+      "-Wl,--wrap=strdup",
+      "-Wl,--wrap=malloc_usable_size",
+      "-Wl,--wrap=malloc_stats_fast",
+      "-Wl,--wrap=__cxa_demangle",
+    ]
+  }
+
+  defines = [
+    # Cobalt on Linux flag
+    "COBALT_LINUX",
+    "__STDC_FORMAT_MACROS", # so that we get PRI*
+    # Enable GNU extensions to get prototypes like ffsl.
+    "_GNU_SOURCE=1",
+
+    # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
+    # nasty macros (|Status|, for example) that conflict with Chromium base.
+    "MESA_EGL_NO_X11_HEADERS",
+  ]
+
+  if (use_clang) {
+    cflags += [
+      "-Werror",
+      "-fcolor-diagnostics",
+      # Default visibility to hidden, to enable dead stripping.
+      "-fvisibility=hidden",
+      # Warn for implicit type conversions that may change a value.
+      "-Wconversion",
+      "-Wno-c++11-compat",
+      # This complains about "override", which we use heavily.
+      "-Wno-c++11-extensions",
+      # Warns on switches on enums that cover all enum values but
+      # also contain a default: branch. Chrome is full of that.
+      "-Wno-covered-switch-default",
+      # protobuf uses hash_map.
+      "-Wno-deprecated",
+      "-fno-exceptions",
+      # Don't warn about the "struct foo f = {0};" initialization pattern.
+      "-Wno-missing-field-initializers",
+      # Do not warn for implicit sign conversions.
+      "-Wno-sign-conversion",
+      "-fno-strict-aliasing",  # See http://crbug.com/32204
+      "-Wno-unnamed-type-template-args",
+      # Triggered by the COMPILE_ASSERT macro.
+      "-Wno-unused-local-typedef",
+      # Do not warn if a function or variable cannot be implicitly
+      # instantiated.
+      "-Wno-undefined-var-template",
+      # Do not warn about an implicit exception spec mismatch.
+      "-Wno-implicit-exception-spec-mismatch",
+    ]
+
+    ldflags += [ "-fuse-ld=lld" ]
+
+    if (use_asan) {
+      cflags += [
+        "-fsanitize=address",
+        "-fno-omit-frame-pointer",
+      ]
+
+      ldflags += [
+        "-fsanitize=address",
+        # Force linking of the helpers in sanitizer_options.cc
+        "-Wl,-u_sanitizer_options_link_helper",
+      ]
+
+      defines += [
+        "ADDRESS_SANITIZER",
+      ]
+
+      if (asan_symbolizer_path != "") {
+        defines += [
+          "ASAN_SYMBOLIZER_PATH=\"$asan_symbolizer_path\"",
+        ]
+      }
+    }
+
+    if (use_tsan) {
+      cflags += [
+        "-fsanitize=thread",
+        "-fno-omit-frame-pointer",
+      ]
+
+      ldflags += [
+        "-fsanitize-thread",
+      ]
+
+      defines += [
+        "THREAD_SANITIZER",
+      ]
+    }
+  }
+}
+
+config("compiler_defaults_debug") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_devel") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_qa") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+config("compiler_defaults_gold") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+config("compiler_defaults_host") {
+  cflags = [
+    "-O2",
+  ]
+}
+
+# ==============================================================================
+# DELEGATED CONFIGS
+# ==============================================================================
+
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+  ]
+}
+
+config("no_pedantic_warnings") {
+  cflags = [
+    # "this" pointer cannot be NULL...pointer may be assumed
+    # to always convert to true.
+    "-Wno-undefined-bool-conversion",
+    # Skia doesn't use overrides.
+    "-Wno-inconsistent-missing-override",
+    # Do not warn about unused function params.
+    "-Wno-unused-parameter",
+    # Do not warn for implicit type conversions that may change a value.
+    "-Wno-conversion",
+    # shifting a negative signed value is undefined
+    "-Wno-shift-negative-value",
+    # Width of bit-field exceeds width of its type- value will be truncated
+    "-Wno-bitfield-width",
+    "-Wno-undefined-var-template",
+  ]
+}
+
+
+delegated_config("optimizations") {
+  path = "//starboard/build/toolchain/linux/config"
+  prefixes = [ "no", "debuggable", "full" ]
+  generate_default = false
+}
+
+config("default_optimizations") {
+  if (cobalt_config == "debug") {
+    configs = [ ":no_optimizations" ]
+  } else {
+    configs = [ ":debuggable_optimizations" ]
+  }
+}
+
+
+delegated_config("rtti") {
+  path = "//starboard/build/toolchain/linux/config"
+  generate_default = false
+}
+
+config("default_rtti") {
+  if (cobalt_config == "debug" || cobalt_config == "devel") {
+    configs = [ ":rtti" ]
+  } else {
+    configs = [ ":no_rtti" ]
+  }
+}
+
+
+config("wexit_time_destructors") {
+  if (use_clang) {
+    configs =
+        [ "//starboard/build/toolchain/linux/config:wexit_time_destructors" ]
+  }
+}
+
+# =============================================================================
+# starboard_platform TARGET
+# =============================================================================
+
+source_set("starboard_platform") {
+  visibility = [ "//starboard/linux/*" ]
+
+  sources = [
+    "//starboard/linux/shared/atomic_public.h",
+    "//starboard/linux/shared/configuration_public.h",
+    "//starboard/linux/shared/decode_target_get_info.cc",
+    "//starboard/linux/shared/decode_target_internal.cc",
+    "//starboard/linux/shared/decode_target_internal.h",
+    "//starboard/linux/shared/decode_target_release.cc",
+    "//starboard/linux/shared/media_is_video_supported.cc",
+    "//starboard/linux/shared/player_components_impl.cc",
+    "//starboard/linux/shared/system_get_connection_type.cc",
+    "//starboard/linux/shared/system_get_device_type.cc",
+    "//starboard/linux/shared/system_get_path.cc",
+    "//starboard/linux/shared/system_has_capability.cc",
+    "//starboard/shared/alsa/alsa_audio_sink_type.cc",
+    "//starboard/shared/alsa/alsa_audio_sink_type.h",
+    "//starboard/shared/alsa/alsa_util.cc",
+    "//starboard/shared/alsa/alsa_util.h",
+    "//starboard/shared/alsa/audio_sink_get_max_channels.cc",
+    "//starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc",
+    "//starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc",
+    "//starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc",
+    "//starboard/shared/dlmalloc/memory_map.cc",
+    "//starboard/shared/dlmalloc/memory_unmap.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_decoder.h",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_resampler.h",
+    "//starboard/shared/ffmpeg/ffmpeg_common.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_common.h",
+    "//starboard/shared/ffmpeg/ffmpeg_video_decoder.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_video_decoder.h",
+    "//starboard/shared/gcc/atomic_gcc_public.h",
+    "//starboard/shared/iso/character_is_alphanumeric.cc",
+    "//starboard/shared/iso/character_is_digit.cc",
+    "//starboard/shared/iso/character_is_hex_digit.cc",
+    "//starboard/shared/iso/character_is_space.cc",
+    "//starboard/shared/iso/character_is_upper.cc",
+    "//starboard/shared/iso/character_to_lower.cc",
+    "//starboard/shared/iso/character_to_upper.cc",
+    "//starboard/shared/iso/directory_close.cc",
+    "//starboard/shared/iso/directory_get_next.cc",
+    "//starboard/shared/iso/directory_open.cc",
+    "//starboard/shared/iso/double_absolute.cc",
+    "//starboard/shared/iso/double_exponent.cc",
+    "//starboard/shared/iso/double_floor.cc",
+    "//starboard/shared/iso/double_is_finite.cc",
+    "//starboard/shared/iso/double_is_nan.cc",
+    "//starboard/shared/iso/memory_compare.cc",
+    "//starboard/shared/iso/memory_copy.cc",
+    "//starboard/shared/iso/memory_find_byte.cc",
+    "//starboard/shared/iso/memory_move.cc",
+    "//starboard/shared/iso/memory_set.cc",
+    "//starboard/shared/iso/string_compare.cc",
+    "//starboard/shared/iso/string_compare_all.cc",
+    "//starboard/shared/iso/string_find_character.cc",
+    "//starboard/shared/iso/string_find_last_character.cc",
+    "//starboard/shared/iso/string_find_string.cc",
+    "//starboard/shared/iso/string_get_length.cc",
+    "//starboard/shared/iso/string_get_length_wide.cc",
+    "//starboard/shared/iso/string_parse_double.cc",
+    "//starboard/shared/iso/string_parse_signed_integer.cc",
+    "//starboard/shared/iso/string_parse_uint64.cc",
+    "//starboard/shared/iso/string_parse_unsigned_integer.cc",
+    "//starboard/shared/iso/string_scan.cc",
+    "//starboard/shared/iso/system_binary_search.cc",
+    "//starboard/shared/iso/system_sort.cc",
+    "//starboard/shared/libevent/socket_waiter_add.cc",
+    "//starboard/shared/libevent/socket_waiter_create.cc",
+    "//starboard/shared/libevent/socket_waiter_destroy.cc",
+    "//starboard/shared/libevent/socket_waiter_internal.cc",
+    "//starboard/shared/libevent/socket_waiter_remove.cc",
+    "//starboard/shared/libevent/socket_waiter_wait.cc",
+    "//starboard/shared/libevent/socket_waiter_wait_timed.cc",
+    "//starboard/shared/libevent/socket_waiter_wake_up.cc",
+    "//starboard/shared/libvpx/vpx_video_decoder.cc",
+    "//starboard/shared/libvpx/vpx_video_decoder.h",
+    "//starboard/shared/linux/byte_swap.cc",
+    "//starboard/shared/linux/get_home_directory.cc",
+    "//starboard/shared/linux/memory_get_stack_bounds.cc",
+    "//starboard/shared/linux/page_internal.cc",
+    "//starboard/shared/linux/socket_get_interface_address.cc",
+    "//starboard/shared/linux/socket_get_local_interface_address.cc",
+    "//starboard/shared/linux/system_get_random_data.cc",
+    "//starboard/shared/linux/system_get_stack.cc",
+    "//starboard/shared/linux/system_get_total_cpu_memory.cc",
+    "//starboard/shared/linux/system_is_debugger_attached.cc",
+    "//starboard/shared/linux/system_symbolize.cc",
+    "//starboard/shared/linux/thread_get_id.cc",
+    "//starboard/shared/linux/thread_get_name.cc",
+    "//starboard/shared/linux/thread_set_name.cc",
+    "//starboard/shared/nouser/user_get_current.cc",
+    "//starboard/shared/nouser/user_get_property.cc",
+    "//starboard/shared/nouser/user_get_signed_in.cc",
+    "//starboard/shared/nouser/user_internal.cc",
+    "//starboard/shared/posix/directory_create.cc",
+    "//starboard/shared/posix/file_can_open.cc",
+    "//starboard/shared/posix/file_close.cc",
+    "//starboard/shared/posix/file_delete.cc",
+    "//starboard/shared/posix/file_exists.cc",
+    "//starboard/shared/posix/file_flush.cc",
+    "//starboard/shared/posix/file_get_info.cc",
+    "//starboard/shared/posix/file_get_path_info.cc",
+    "//starboard/shared/posix/file_open.cc",
+    "//starboard/shared/posix/file_read.cc",
+    "//starboard/shared/posix/file_seek.cc",
+    "//starboard/shared/posix/file_truncate.cc",
+    "//starboard/shared/posix/file_write.cc",
+    "//starboard/shared/posix/log.cc",
+    "//starboard/shared/posix/log_flush.cc",
+    "//starboard/shared/posix/log_format.cc",
+    "//starboard/shared/posix/log_is_tty.cc",
+    "//starboard/shared/posix/log_raw.cc",
+    "//starboard/shared/posix/memory_flush.cc",
+    "//starboard/shared/posix/set_non_blocking_internal.cc",
+    "//starboard/shared/posix/socket_accept.cc",
+    "//starboard/shared/posix/socket_bind.cc",
+    "//starboard/shared/posix/socket_clear_last_error.cc",
+    "//starboard/shared/posix/socket_connect.cc",
+    "//starboard/shared/posix/socket_create.cc",
+    "//starboard/shared/posix/socket_destroy.cc",
+    "//starboard/shared/posix/socket_free_resolution.cc",
+    "//starboard/shared/posix/socket_get_last_error.cc",
+    "//starboard/shared/posix/socket_get_local_address.cc",
+    "//starboard/shared/posix/socket_internal.cc",
+    "//starboard/shared/posix/socket_is_connected.cc",
+    "//starboard/shared/posix/socket_is_connected_and_idle.cc",
+    "//starboard/shared/posix/socket_join_multicast_group.cc",
+    "//starboard/shared/posix/socket_listen.cc",
+    "//starboard/shared/posix/socket_receive_from.cc",
+    "//starboard/shared/posix/socket_resolve.cc",
+    "//starboard/shared/posix/socket_send_to.cc",
+    "//starboard/shared/posix/socket_set_broadcast.cc",
+    "//starboard/shared/posix/socket_set_receive_buffer_size.cc",
+    "//starboard/shared/posix/socket_set_reuse_address.cc",
+    "//starboard/shared/posix/socket_set_send_buffer_size.cc",
+    "//starboard/shared/posix/socket_set_tcp_keep_alive.cc",
+    "//starboard/shared/posix/socket_set_tcp_no_delay.cc",
+    "//starboard/shared/posix/socket_set_tcp_window_scaling.cc",
+    "//starboard/shared/posix/string_compare_no_case.cc",
+    "//starboard/shared/posix/string_compare_no_case_n.cc",
+    "//starboard/shared/posix/string_compare_wide.cc",
+    "//starboard/shared/posix/string_format.cc",
+    "//starboard/shared/posix/string_format_wide.cc",
+    "//starboard/shared/posix/system_break_into_debugger.cc",
+    "//starboard/shared/posix/system_clear_last_error.cc",
+    "//starboard/shared/posix/system_get_error_string.cc",
+    "//starboard/shared/posix/system_get_last_error.cc",
+    "//starboard/shared/posix/system_get_locale_id.cc",
+    "//starboard/shared/posix/system_get_number_of_processors.cc",
+    "//starboard/shared/posix/thread_sleep.cc",
+    "//starboard/shared/posix/time_get_monotonic_now.cc",
+    "//starboard/shared/posix/time_get_monotonic_thread_now.cc",
+    "//starboard/shared/posix/time_get_now.cc",
+    "//starboard/shared/posix/time_zone_get_current.cc",
+    "//starboard/shared/posix/time_zone_get_dst_name.cc",
+    "//starboard/shared/posix/time_zone_get_name.cc",
+    "//starboard/shared/pthread/condition_variable_broadcast.cc",
+    "//starboard/shared/pthread/condition_variable_create.cc",
+    "//starboard/shared/pthread/condition_variable_destroy.cc",
+    "//starboard/shared/pthread/condition_variable_signal.cc",
+    "//starboard/shared/pthread/condition_variable_wait.cc",
+    "//starboard/shared/pthread/condition_variable_wait_timed.cc",
+    "//starboard/shared/pthread/mutex_acquire.cc",
+    "//starboard/shared/pthread/mutex_acquire_try.cc",
+    "//starboard/shared/pthread/mutex_create.cc",
+    "//starboard/shared/pthread/mutex_destroy.cc",
+    "//starboard/shared/pthread/mutex_release.cc",
+    "//starboard/shared/pthread/once.cc",
+    "//starboard/shared/pthread/thread_create.cc",
+    "//starboard/shared/pthread/thread_create_local_key.cc",
+    "//starboard/shared/pthread/thread_create_priority.h",
+    "//starboard/shared/pthread/thread_destroy_local_key.cc",
+    "//starboard/shared/pthread/thread_detach.cc",
+    "//starboard/shared/pthread/thread_get_current.cc",
+    "//starboard/shared/pthread/thread_get_local_value.cc",
+    "//starboard/shared/pthread/thread_is_equal.cc",
+    "//starboard/shared/pthread/thread_join.cc",
+    "//starboard/shared/pthread/thread_set_local_value.cc",
+    "//starboard/shared/pthread/thread_yield.cc",
+    "//starboard/shared/signal/crash_signals.h",
+    "//starboard/shared/signal/crash_signals_sigaction.cc",
+    "//starboard/shared/signal/suspend_signals.cc",
+    "//starboard/shared/signal/suspend_signals.h",
+    "//starboard/shared/starboard/application.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_create.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_destroy.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_internal.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_internal.h",
+    "//starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc",
+    "//starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc",
+    "//starboard/shared/starboard/audio_sink/stub_audio_sink_type.h",
+    "//starboard/shared/starboard/command_line.cc",
+    "//starboard/shared/starboard/command_line.h",
+    "//starboard/shared/starboard/directory_can_open.cc",
+    "//starboard/shared/starboard/event_cancel.cc",
+    "//starboard/shared/starboard/event_schedule.cc",
+    "//starboard/shared/starboard/file_mode_string_to_flags.cc",
+    "//starboard/shared/starboard/file_storage/storage_close_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_delete_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_get_record_size.cc",
+    "//starboard/shared/starboard/file_storage/storage_open_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_read_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_write_record.cc",
+    "//starboard/shared/starboard/log_message.cc",
+    "//starboard/shared/starboard/log_raw_dump_stack.cc",
+    "//starboard/shared/starboard/log_raw_format.cc",
+    "//starboard/shared/starboard/media/codec_util.cc",
+    "//starboard/shared/starboard/media/codec_util.h",
+    "//starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc",
+    "//starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc",
+    "//starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc",
+    "//starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc",
+    "//starboard/shared/starboard/media/media_is_output_protected.cc",
+    "//starboard/shared/starboard/media/media_set_output_protection.cc",
+    "//starboard/shared/starboard/media/media_util.cc",
+    "//starboard/shared/starboard/media/media_util.h",
+    "//starboard/shared/starboard/media/mime_type.cc",
+    "//starboard/shared/starboard/media/mime_type.h",
+    "//starboard/shared/starboard/new.cc",
+    "//starboard/shared/starboard/player/decoded_audio_internal.cc",
+    "//starboard/shared/starboard/player/decoded_audio_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_decoder_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_frame_tracker.h",
+    "//starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc",
+    "//starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_renderer_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_time_stretcher.cc",
+    "//starboard/shared/starboard/player/filter/audio_time_stretcher.h",
+    "//starboard/shared/starboard/player/filter/decoded_audio_queue.cc",
+    "//starboard/shared/starboard/player/filter/decoded_audio_queue.h",
+    "//starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc",
+    "//starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h",
+    "//starboard/shared/starboard/player/filter/player_components.h",
+    "//starboard/shared/starboard/player/filter/video_decoder_internal.h",
+    "//starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc",
+    "//starboard/shared/starboard/player/filter/video_renderer_impl_internal.h",
+    "//starboard/shared/starboard/player/filter/video_renderer_internal.h",
+    "//starboard/shared/starboard/player/filter/wsola_internal.cc",
+    "//starboard/shared/starboard/player/filter/wsola_internal.h",
+    "//starboard/shared/starboard/player/input_buffer_internal.cc",
+    "//starboard/shared/starboard/player/input_buffer_internal.h",
+    "//starboard/shared/starboard/player/job_queue.cc",
+    "//starboard/shared/starboard/player/job_queue.h",
+    "//starboard/shared/starboard/player/player_create.cc",
+    "//starboard/shared/starboard/player/player_destroy.cc",
+    "//starboard/shared/starboard/player/player_get_current_frame.cc",
+    "//starboard/shared/starboard/player/player_get_info.cc",
+    "//starboard/shared/starboard/player/player_internal.cc",
+    "//starboard/shared/starboard/player/player_internal.h",
+    "//starboard/shared/starboard/player/player_output_mode_supported.cc",
+    "//starboard/shared/starboard/player/player_seek.cc",
+    "//starboard/shared/starboard/player/player_set_bounds.cc",
+    "//starboard/shared/starboard/player/player_set_pause.cc",
+    "//starboard/shared/starboard/player/player_set_playback_rate.cc",
+    "//starboard/shared/starboard/player/player_set_volume.cc",
+    "//starboard/shared/starboard/player/player_worker.cc",
+    "//starboard/shared/starboard/player/player_worker.h",
+    "//starboard/shared/starboard/player/player_write_end_of_stream.cc",
+    "//starboard/shared/starboard/player/player_write_sample.cc",
+    "//starboard/shared/starboard/player/video_frame_internal.cc",
+    "//starboard/shared/starboard/player/video_frame_internal.h",
+    "//starboard/shared/starboard/queue_application.cc",
+    "//starboard/shared/starboard/string_concat.cc",
+    "//starboard/shared/starboard/string_concat_wide.cc",
+    "//starboard/shared/starboard/string_copy.cc",
+    "//starboard/shared/starboard/string_copy_wide.cc",
+    "//starboard/shared/starboard/string_duplicate.cc",
+    "//starboard/shared/starboard/system_get_random_uint64.cc",
+    "//starboard/shared/starboard/system_request_pause.cc",
+    "//starboard/shared/starboard/system_request_stop.cc",
+    "//starboard/shared/starboard/system_request_suspend.cc",
+    "//starboard/shared/starboard/system_request_unpause.cc",
+    "//starboard/shared/starboard/window_set_default_options.cc",
+    "//starboard/shared/stub/accessibility_get_display_settings.cc",
+    "//starboard/shared/stub/accessibility_get_text_to_speech_settings.cc",
+    "//starboard/shared/stub/cryptography_create_transformer.cc",
+    "//starboard/shared/stub/cryptography_destroy_transformer.cc",
+    "//starboard/shared/stub/cryptography_get_tag.cc",
+    "//starboard/shared/stub/cryptography_set_authenticated_data.cc",
+    "//starboard/shared/stub/cryptography_set_initialization_vector.cc",
+    "//starboard/shared/stub/cryptography_transform.cc",
+    "//starboard/shared/stub/drm_close_session.cc",
+    "//starboard/shared/stub/drm_create_system.cc",
+    "//starboard/shared/stub/drm_destroy_system.cc",
+    "//starboard/shared/stub/drm_generate_session_update_request.cc",
+    "//starboard/shared/stub/drm_system_internal.h",
+    "//starboard/shared/stub/drm_update_session.cc",
+    "//starboard/shared/stub/image_decode.cc",
+    "//starboard/shared/stub/image_is_decode_supported.cc",
+    "//starboard/shared/stub/media_is_supported.cc",
+    "//starboard/shared/stub/media_is_transfer_characteristics_supported.cc",
+    "//starboard/shared/stub/microphone_close.cc",
+    "//starboard/shared/stub/microphone_create.cc",
+    "//starboard/shared/stub/microphone_destroy.cc",
+    "//starboard/shared/stub/microphone_get_available.cc",
+    "//starboard/shared/stub/microphone_is_sample_rate_supported.cc",
+    "//starboard/shared/stub/microphone_open.cc",
+    "//starboard/shared/stub/microphone_read.cc",
+    "//starboard/shared/stub/system_clear_platform_error.cc",
+    "//starboard/shared/stub/system_get_total_gpu_memory.cc",
+    "//starboard/shared/stub/system_get_used_gpu_memory.cc",
+    "//starboard/shared/stub/system_hide_splash_screen.cc",
+    "//starboard/shared/stub/system_raise_platform_error.cc",
+  ]
+
+  if (use_dlmalloc_allocator) {
+    sources += [
+      "//starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc",
+      "//starboard/shared/dlmalloc/memory_allocate_unchecked.cc",
+      "//starboard/shared/dlmalloc/memory_free.cc",
+      "//starboard/shared/dlmalloc/memory_free_aligned.cc",
+      "//starboard/shared/dlmalloc/memory_reallocate_unchecked.cc",
+      "//starboard/shared/dlmalloc/system_get_used_cpu_memory.cc",
+    ]
+  } else {
+    sources += [
+      "//starboard/shared/iso/memory_allocate_unchecked.cc",
+      "//starboard/shared/iso/memory_free.cc",
+      "//starboard/shared/iso/memory_reallocate_unchecked.cc",
+      "//starboard/shared/linux/system_get_used_cpu_memory.cc",
+      "//starboard/shared/posix/memory_allocate_aligned_unchecked.cc",
+      "//starboard/shared/posix/memory_free_aligned.cc",
+    ]
+  }
+
+  defines = [
+    # This must be defined when building Starboard, and must not when
+    # building Starboard client code.
+    "STARBOARD_IMPLEMENTATION",
+  ]
+
+  deps = [
+    "//starboard/common",
+    ":starboard_base_symbolize",
+    "//third_party/dlmalloc",
+    "//third_party/libevent",
+    "//third_party/libvpx",
+  ]
+}
+
+source_set("starboard_base_symbolize") {
+  sources = [
+    "//base/third_party/symbolize/demangle.cc",
+    "//base/third_party/symbolize/symbolize.cc",
+  ]
+}
diff --git a/src/starboard/linux/shared/buildconfig.gni b/src/starboard/linux/shared/buildconfig.gni
new file mode 100644
index 0000000..9cbc874
--- /dev/null
+++ b/src/starboard/linux/shared/buildconfig.gni
@@ -0,0 +1,43 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/linux/shared/clang.gni")
+
+###############################################################################
+# This file is imported into BUILDCONFIG.gn. As a side effect, any variable
+# defined here becomes a global variable in all GN files.
+#
+# Adding new variables to this file could result in variable name conflicts, so
+# keep the number of variables in this file to a minimum!
+#
+# If you need to define a temporary variable, prefix it with an underscore like
+# _this. Prefixing a variable with an underscore makes it a private variable
+# that won't bleed into other files.
+###############################################################################
+
+# Target OS and CPU.
+# BUILDCONFIG.gn sets target_os and target_cpu to the values of these two
+# variables below. Unfortunately, due to GN's restrictions on how variables can
+# be changed, we can't directly set target_os and target_cpu here.
+target_os_ = "linux"
+target_cpu_ = "x64"
+
+# The target and host toolchain
+if (use_clang) {
+  target_toolchain = "//starboard/build/toolchain/linux:clang_x64"
+  host_toolchain = "//starboard/build/toolchain/linux:clang_$host_cpu"
+} else {
+  target_toolchain = "//starboard/build/toolchain/linux:x64"
+  host_toolchain = "//starboard/build/toolchain/linux:$host_cpu"
+}
diff --git a/src/starboard/linux/shared/clang.gni b/src/starboard/linux/shared/clang.gni
new file mode 100644
index 0000000..a9aa3f4
--- /dev/null
+++ b/src/starboard/linux/shared/clang.gni
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # Whether to use Clang.
+  use_clang = true
+}
diff --git a/src/starboard/linux/shared/configuration.gni b/src/starboard/linux/shared/configuration.gni
new file mode 100644
index 0000000..99cb78e
--- /dev/null
+++ b/src/starboard/linux/shared/configuration.gni
@@ -0,0 +1,46 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set to true to build with DIAL support.
+enable_in_app_dial = true
+
+# The source of EGL and GLES headers and libraries.
+# Valid values (case and everything sensitive!):
+#   "none"   - No EGL + GLES implementation is available on this platform.
+#   "system_gles3" - Use the system implementation of EGL + GLES3. The
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "system_gles2" - Use the system implementation of EGL + GLES2. The
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "glimp"  - Cobalt's own EGL + GLES2 implementation. This requires a
+#              valid Glimp implementation for the platform.
+#   "angle"  - A DirectX-to-OpenGL adaptation layer. This requires a valid
+#              ANGLE implementation for the platform.
+# Choosing an unsupported value will result in a GYP error:
+#   "cobalt/renderer/egl_and_gles/egl_and_gles_<gl_type>.gyp not found"
+gl_type = "system_gles3"
+
+# Use media source extension implementation that is conformed to the
+# Candidate Recommandation of July 5th 2016.
+cobalt_use_media_source_2016 = true
+
+# Use ASAN by default when building with Clang.
+use_asan_by_default = true
+
+declare_args() {
+  # Set to true to enable distributed compilation using Goma. By default we
+  # use Goma for stub and linux.
+  use_goma = true
+}
diff --git a/src/starboard/linux/shared/dlmalloc.gni b/src/starboard/linux/shared/dlmalloc.gni
new file mode 100644
index 0000000..73c1137
--- /dev/null
+++ b/src/starboard/linux/shared/dlmalloc.gni
@@ -0,0 +1,19 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # Whether to use the dlmalloc allocator instead of the system allocator
+  use_dlmalloc_allocator = false
+}
+
diff --git a/src/starboard/linux/shared/launcher.py b/src/starboard/linux/shared/launcher.py
index 73e708a..3b476d1 100644
--- a/src/starboard/linux/shared/launcher.py
+++ b/src/starboard/linux/shared/launcher.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
 #
 # Copyright 2017 Google Inc. All Rights Reserved.
 #
@@ -15,17 +14,25 @@
 # limitations under the License.
 """Linux implementation of Starboard launcher abstraction."""
 
-import imp
+import importlib
 import os
+import sys
+
+if "environment" in sys.modules:
+  environment = sys.modules["environment"]
+else:
+  env_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir,
+                                          os.pardir, "tools"))
+  if env_path not in sys.path:
+    sys.path.append(env_path)
+  environment = importlib.import_module("environment")
+
+
 import signal
 import socket
 import subprocess
 
-module_path = os.path.abspath(
-    os.path.join(os.path.dirname(__file__),
-                 os.pardir, os.pardir, "tools", "abstract_launcher.py"))
-
-abstract_launcher = imp.load_source("abstract_launcher", module_path)
+import starboard.tools.abstract_launcher as abstract_launcher
 
 
 class Launcher(abstract_launcher.AbstractLauncher):
@@ -57,6 +64,7 @@
     proc = subprocess.Popen([self.executable] + self.target_command_line_params)
     self.pid = proc.pid
     proc.wait()
+    return proc.returncode
 
   def Kill(self):
     print "\n***Killing Launcher***\n"
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index f896d17..311618c 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -90,6 +90,8 @@
       '<(DEPTH)/starboard/shared/libvpx/vpx_video_decoder.cc',
       '<(DEPTH)/starboard/shared/libvpx/vpx_video_decoder.h',
       '<(DEPTH)/starboard/shared/linux/byte_swap.cc',
+      '<(DEPTH)/starboard/shared/linux/dev_input/dev_input.cc',
+      '<(DEPTH)/starboard/shared/linux/dev_input/dev_input.h',
       '<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
       '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
       '<(DEPTH)/starboard/shared/linux/page_internal.cc',
@@ -98,7 +100,6 @@
       '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
       '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
       '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
-      '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
       '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
       '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
       '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
@@ -326,12 +327,14 @@
           '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
           '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
           '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
+          '<(DEPTH)/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc',
         ],
       }, {
         'starboard_platform_sources': [
           '<(DEPTH)/starboard/shared/iso/memory_allocate_unchecked.cc',
           '<(DEPTH)/starboard/shared/iso/memory_free.cc',
           '<(DEPTH)/starboard/shared/iso/memory_reallocate_unchecked.cc',
+          '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
           '<(DEPTH)/starboard/shared/posix/memory_allocate_aligned_unchecked.cc',
           '<(DEPTH)/starboard/shared/posix/memory_free_aligned.cc',
         ],
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc
index 4ae443b..315903be 100644
--- a/src/starboard/linux/shared/system_get_path.cc
+++ b/src/starboard/linux/shared/system_get_path.cc
@@ -36,14 +36,12 @@
                          home_path, kMaxPathSize)) {
     return false;
   }
-  int result =
-      SbStringFormatF(out_path, path_size, "%s/.cache/cobalt", home_path);
+  int result = SbStringFormatF(out_path, path_size, "%s/.cache", home_path);
   if (result < 0 || result >= path_size) {
     out_path[0] = '\0';
     return false;
   }
-
-  return true;
+  return SbDirectoryCreate(out_path);
 }
 
 // Places up to |path_size| - 1 characters of the path to the current
@@ -144,7 +142,12 @@
       if (!GetCacheDirectory(path, kPathSize)) {
         return false;
       }
-      SbDirectoryCreate(path);
+      if (SbStringConcat(path, "/cobalt", kPathSize) >= kPathSize) {
+        return false;
+      }
+      if (!SbDirectoryCreate(path)) {
+        return false;
+      }
       break;
     case kSbSystemPathDebugOutputDirectory:
       if (!SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize)) {
diff --git a/src/starboard/linux/x64directfb/future/gyp_configuration.py b/src/starboard/linux/x64directfb/future/gyp_configuration.py
index b31713a..1074bc3 100644
--- a/src/starboard/linux/x64directfb/future/gyp_configuration.py
+++ b/src/starboard/linux/x64directfb/future/gyp_configuration.py
@@ -14,15 +14,9 @@
 """Starboard Linux X64 DirectFB future platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64directfb/gyp_configuration.py b/src/starboard/linux/x64directfb/gyp_configuration.py
index 9933052..7e60294 100644
--- a/src/starboard/linux/x64directfb/gyp_configuration.py
+++ b/src/starboard/linux/x64directfb/gyp_configuration.py
@@ -14,13 +14,9 @@
 """Starboard Linux X64 DirectFB platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(os.path.realpath(os.path.join(
-    os.path.dirname(__file__), os.pardir, 'shared')))
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64x11/BUILD.gn b/src/starboard/linux/x64x11/BUILD.gn
new file mode 100644
index 0000000..259502b
--- /dev/null
+++ b/src/starboard/linux/x64x11/BUILD.gn
@@ -0,0 +1,98 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/delegated_config.gni")
+
+# =============================================================================
+# DEFAULT COMPILER CONFIGS
+# =============================================================================
+
+config("compiler_defaults") {
+  libs = [
+    "EGL",
+    "GLESv2",
+    "X11",
+    "Xcomposite",
+    "Xrender",
+  ]
+
+  configs = [ "//starboard/linux/shared:compiler_defaults" ]
+}
+
+config("compiler_defaults_debug") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_debug" ]
+}
+
+config("compiler_defaults_devel") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_devel" ]
+}
+
+config("compiler_defaults_qa") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_qa" ]
+}
+
+config("compiler_defaults_gold") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_gold" ]
+}
+
+# =============================================================================
+# DELEGATED CONFIGS
+# =============================================================================
+
+delegated_config("pedantic_warnings") {
+  path = "//starboard/linux/shared"
+  generate_default = false
+}
+
+delegated_config("optimizations") {
+  path = "//starboard/linux/shared"
+  prefixes = [ "no", "debuggable", "full" ]
+}
+
+delegated_config("rtti") {
+  path = "//starboard/linux/shared"
+}
+
+config("wexit_time_destructors") {
+  configs = [ "//starboard/linux/shared:wexit_time_destructors" ]
+}
+
+# =============================================================================
+# starboard_platform TARGET
+# =============================================================================
+
+static_library("starboard_platform") {
+  sources = [
+    "//starboard/linux/x64x11/main.cc",
+    "//starboard/linux/x64x11/sanitizer_options.cc",
+    "//starboard/linux/x64x11/system_get_property.cc",
+    "//starboard/shared/starboard/link_receiver.cc",
+    "//starboard/shared/x11/application_x11.cc",
+    "//starboard/shared/x11/window_create.cc",
+    "//starboard/shared/x11/window_destroy.cc",
+    "//starboard/shared/x11/window_get_platform_handle.cc",
+    "//starboard/shared/x11/window_get_size.cc",
+    "//starboard/shared/x11/window_internal.cc",
+  ]
+  
+  defines = [
+    # This must be defined when building Starboard, and must not when
+    # building Starboard client code.
+    "STARBOARD_IMPLEMENTATION",
+  ]
+
+  deps = [
+    "//starboard/linux/shared:starboard_platform",
+  ]
+}
diff --git a/src/starboard/linux/x64x11/buildconfig.gni b/src/starboard/linux/x64x11/buildconfig.gni
new file mode 100644
index 0000000..4335652
--- /dev/null
+++ b/src/starboard/linux/x64x11/buildconfig.gni
@@ -0,0 +1,15 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/linux/shared/buildconfig.gni")
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
index 67a704c..58d9f75 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -14,20 +14,12 @@
 """Starboard Linux X64 X11 Clang 3.6 platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
-            'shared')))
-# pylint: disable=import-self,g-import-not-at-top
-import gyp_configuration as shared_configuration
+from starboard.linux.shared import gyp_configuration
 
 
-class PlatformConfig(shared_configuration.PlatformConfig):
+class PlatformConfig(gyp_configuration.PlatformConfig):
   """Starboard Linux platform configuration."""
 
   def __init__(self, platform, asan_enabled_by_default=True):
diff --git a/src/starboard/linux/x64x11/configuration.gni b/src/starboard/linux/x64x11/configuration.gni
new file mode 100644
index 0000000..bb3a238
--- /dev/null
+++ b/src/starboard/linux/x64x11/configuration.gni
@@ -0,0 +1,19 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/linux/shared/configuration.gni")
+
+# Enable support for the map to mesh filter, which is primarily used to
+# implement spherical video playback.
+enable_map_to_mesh = true
diff --git a/src/starboard/linux/x64x11/directgles/gyp_configuration.py b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
index 4fc899f..ca2ca64 100644
--- a/src/starboard/linux/x64x11/directgles/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
@@ -14,16 +14,9 @@
 """Starboard Linux X64 X11 future platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
-# pylint: disable=import-self,g-import-not-at-top
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.py b/src/starboard/linux/x64x11/future/gyp_configuration.py
index 5f82ad7..3a3e34f 100644
--- a/src/starboard/linux/x64x11/future/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/future/gyp_configuration.py
@@ -14,15 +14,9 @@
 """Starboard Linux X64 X11 future platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
index bac3a98..3c1702d 100644
--- a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
@@ -16,20 +16,15 @@
 import logging
 import os
 import subprocess
-import sys
 
-# Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
-            'shared')))
+
 # pylint: disable=import-self,g-import-not-at-top
-import gyp_configuration as shared_configuration
 import gyp_utils
+# Import the shared Linux platform configuration.
+from starboard.linux.shared import gyp_configuration
 
 
-class PlatformConfig(shared_configuration.PlatformConfig):
+class PlatformConfig(gyp_configuration.PlatformConfig):
   """Starboard Linux platform configuration."""
 
   def __init__(self, platform, asan_enabled_by_default=False):
diff --git a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
index 659c47d..f76e9ea 100644
--- a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
@@ -29,6 +29,7 @@
     'cobalt_enable_jit': 0,
 
     'cobalt_media_source_2016': 1,
+    'cobalt_encrypted_media_extension_enable_key_statuses_update': 0,
 
     'platform_libraries': [
       '-lpthread',
diff --git a/src/starboard/log.h b/src/starboard/log.h
index 596bc88..bd0edef 100644
--- a/src/starboard/log.h
+++ b/src/starboard/log.h
@@ -145,7 +145,7 @@
 }
 
 #if defined(__cplusplus_winrt)
-inline std::ostream& operator<<(std::ostream& out, ::Platform::String ^ str) {
+inline std::ostream& operator<<(std::ostream& out, ::Platform::String^ str) {
   return out << std::wstring(str->Begin(), str->End());
 }
 #endif
@@ -156,7 +156,10 @@
 const SbLogPriority SB_LOG_FATAL = kSbLogPriorityFatal;
 const SbLogPriority SB_LOG_0 = SB_LOG_ERROR;
 
-class SB_EXPORT LogMessage {
+// TODO: Export this, e.g. by wrapping in straight-C functions which can then be
+// SB_EXPORT'd. Using SB_EXPORT here directly causes issues on Windows because
+// it uses std classes which also need to be exported.
+class LogMessage {
  public:
   LogMessage(const char* file, int line, SbLogPriority priority);
   ~LogMessage();
diff --git a/src/starboard/nplb/BUILD.gn b/src/starboard/nplb/BUILD.gn
new file mode 100644
index 0000000..29d0b35
--- /dev/null
+++ b/src/starboard/nplb/BUILD.gn
@@ -0,0 +1,297 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/deploy.gni")
+
+group("all") {
+  testonly = true
+  deps = [
+    ":nplb",
+    ":deploy",
+  ]
+}
+
+test("nplb") {
+  testonly = true
+  sources = [
+    "//starboard/common/test_main.cc",
+    "accessibility_get_setting_test.cc",
+    "align_test.cc",
+    "atomic_test.cc",
+    "audio_sink_create_test.cc",
+    "audio_sink_destroy_test.cc",
+    "audio_sink_get_max_channels_test.cc",
+    "audio_sink_get_nearest_supported_sample_frequency_test.cc",
+    "audio_sink_helpers.cc",
+    "audio_sink_helpers.h",
+    "audio_sink_is_audio_frame_storage_type_supported_test.cc",
+    "audio_sink_is_audio_sample_type_supported_test.cc",
+    "audio_sink_test.cc",
+    "blitter_blit_rect_to_rect_test.cc",
+    "blitter_blit_rect_to_rect_tiled_test.cc",
+    "blitter_blit_rects_to_rects_test.cc",
+    "blitter_create_context_test.cc",
+    "blitter_create_default_device_test.cc",
+    "blitter_create_pixel_data_test.cc",
+    "blitter_create_render_target_surface_test.cc",
+    "blitter_create_surface_from_pixel_data_test.cc",
+    "blitter_create_swap_chain_from_window_test.cc",
+    "blitter_destroy_context_test.cc",
+    "blitter_destroy_device_test.cc",
+    "blitter_destroy_pixel_data_test.cc",
+    "blitter_destroy_surface_test.cc",
+    "blitter_destroy_swap_chain_test.cc",
+    "blitter_download_surface_pixels_test.cc",
+    "blitter_fill_rect_test.cc",
+    "blitter_flip_swap_chain_test.cc",
+    "blitter_flush_context_test.cc",
+    "blitter_get_max_contexts_test.cc",
+    "blitter_get_pixel_data_pitch_in_bytes_test.cc",
+    "blitter_get_pixel_data_pointer_test.cc",
+    "blitter_get_render_target_from_surface_test.cc",
+    "blitter_get_render_target_from_swap_chain_test.cc",
+    "blitter_get_surface_info_test.cc",
+    "blitter_helpers.cc",
+    "blitter_is_pixel_format_supported_by_download_surface_pixels_test.cc",
+    "blitter_is_pixel_format_supported_by_pixel_data_test.cc",
+    "blitter_is_pixel_format_supported_by_render_target_surface_test.cc",
+    "blitter_set_blending_test.cc",
+    "blitter_set_modulate_blits_with_color_test.cc",
+    "blitter_set_render_target_test.cc",
+    "blitter_set_scissor_test.cc",
+    "byte_swap_test.cc",
+    "char_is_signed_test.cc",
+    "character_is_alphanumeric_test.cc",
+    "character_is_digit_test.cc",
+    "character_is_hex_digit_test.cc",
+    "character_is_space_test.cc",
+    "character_is_upper_test.cc",
+    "character_to_lower_test.cc",
+    "character_to_upper_test.cc",
+    "condition_variable_broadcast_test.cc",
+    "condition_variable_create_test.cc",
+    "condition_variable_destroy_test.cc",
+    "condition_variable_signal_test.cc",
+    "condition_variable_wait_test.cc",
+    "condition_variable_wait_timed_test.cc",
+    "configuration_test.cc",
+    "cryptography_create_transformer_test.cc",
+    "cryptography_helpers.cc",
+    "cryptography_helpers.h",
+    "cryptography_transform_gcm_test.cc",
+    "cryptography_transform_test.cc",
+    "decode_target_create_test.cc",
+    "decode_target_provider_test.cc",
+    "directory_can_open_test.cc",
+    "directory_close_test.cc",
+    "directory_create_test.cc",
+    "directory_get_next_test.cc",
+    "directory_open_test.cc",
+    "double_absolute_test.cc",
+    "double_exponent_test.cc",
+    "double_floor_test.cc",
+    "double_is_finite_test.cc",
+    "double_is_nan_test.cc",
+    "file_can_open_test.cc",
+    "file_close_test.cc",
+    "file_get_info_test.cc",
+    "file_get_path_info_test.cc",
+    "file_helpers.cc",
+    "file_mode_string_to_flags_test.cc",
+    "file_open_test.cc",
+    "file_read_test.cc",
+    "file_seek_test.cc",
+    "file_truncate_test.cc",
+    "file_write_test.cc",
+    "flat_map_test.cc",
+    "include_all.c",
+    "include_all_too.c",
+    "log_flush_test.cc",
+    "log_format_test.cc",
+    "log_is_tty_test.cc",
+    "log_raw_dump_stack_test.cc",
+    "log_raw_test.cc",
+    "log_test.cc",
+    "memory_align_to_page_size_test.cc",
+    "memory_allocate_aligned_test.cc",
+    "memory_allocate_test.cc",
+    "memory_compare_test.cc",
+    "memory_copy_test.cc",
+    "memory_deallocate_aligned_test.cc",
+    "memory_deallocate_test.cc",
+    "memory_find_byte_test.cc",
+    "memory_get_stack_bounds_test.cc",
+    "memory_is_zero_test.cc",
+    "memory_map_test.cc",
+    "memory_move_test.cc",
+    "memory_reallocate_test.cc",
+    "memory_reporter_test.cc",
+    "memory_set_test.cc",
+    "microphone_close_test.cc",
+    "microphone_create_test.cc",
+    "microphone_destroy_test.cc",
+    "microphone_get_available_test.cc",
+    "microphone_is_sample_rate_supported_test.cc",
+    "microphone_open_test.cc",
+    "microphone_read_test.cc",
+    "mutex_acquire_test.cc",
+    "mutex_acquire_try_test.cc",
+    "mutex_create_test.cc",
+    "mutex_destroy_test.cc",
+    "once_test.cc",
+    "optional_test.cc",
+    "player_create_test.cc",
+    "random_helpers.cc",
+    "rwlock_test.cc",
+    "semaphore_test.cc",
+    "socket_accept_test.cc",
+    "socket_bind_test.cc",
+    "socket_clear_last_error_test.cc",
+    "socket_connect_test.cc",
+    "socket_create_test.cc",
+    "socket_destroy_test.cc",
+    "socket_get_interface_address_test.cc",
+    "socket_get_last_error_test.cc",
+    "socket_get_local_address_test.cc",
+    "socket_get_local_interface_address_test.cc",
+    "socket_helpers.cc",
+    "socket_is_connected_and_idle_test.cc",
+    "socket_is_connected_test.cc",
+    "socket_join_multicast_group_test.cc",
+    "socket_listen_test.cc",
+    "socket_receive_from_test.cc",
+    "socket_resolve_test.cc",
+    "socket_send_to_test.cc",
+    "socket_set_options_test.cc",
+    "socket_waiter_add_test.cc",
+    "socket_waiter_create_test.cc",
+    "socket_waiter_destroy_test.cc",
+    "socket_waiter_remove_test.cc",
+    "socket_waiter_wait_test.cc",
+    "socket_waiter_wait_timed_test.cc",
+    "socket_waiter_wake_up_test.cc",
+    "socket_wrapper_test.cc",
+    "speech_recognizer_cancel_test.cc",
+    "speech_recognizer_create_test.cc",
+    "speech_recognizer_destroy_test.cc",
+    "speech_recognizer_helper.h",
+    "speech_recognizer_start_test.cc",
+    "speech_recognizer_stop_test.cc",
+    "speech_synthesis_basic_test.cc",
+    "storage_close_record_test.cc",
+    "storage_delete_record_test.cc",
+    "storage_get_record_size_test.cc",
+    "storage_open_record_test.cc",
+    "storage_read_record_test.cc",
+    "storage_write_record_test.cc",
+    "state_machine_test.cc",
+    "string_compare_all_test.cc",
+    "string_compare_no_case_n_test.cc",
+    "string_compare_no_case_test.cc",
+    "string_compare_test.cc",
+    "string_compare_wide_test.cc",
+    "string_concat_test.cc",
+    "string_concat_wide_test.cc",
+    "string_copy_test.cc",
+    "string_copy_wide_test.cc",
+    "string_duplicate_test.cc",
+    "string_find_character_test.cc",
+    "string_find_last_character_test.cc",
+    "string_find_string_test.cc",
+    "string_format_test.cc",
+    "string_format_wide_test.cc",
+    "string_parse_double_test.cc",
+    "string_parse_signed_integer_test.cc",
+    "string_parse_uint64_test.cc",
+    "string_parse_unsigned_integer_test.cc",
+    "string_scan_test.cc",
+    "system_binary_search_test.cc",
+    "system_clear_last_error_test.cc",
+    "system_get_error_string_test.cc",
+    "system_get_last_error_test.cc",
+    "system_get_locale_id_test.cc",
+    "system_get_number_of_processors_test.cc",
+    "system_get_path_test.cc",
+    "system_get_property_test.cc",
+    "system_get_random_data_test.cc",
+    "system_get_random_uint64_test.cc",
+    "system_get_stack_test.cc",
+    "system_get_total_cpu_memory_test.cc",
+    "system_get_total_gpu_memory_test.cc",
+    "system_get_used_cpu_memory_test.cc",
+    "system_get_used_gpu_memory_test.cc",
+    "system_has_capability_test.cc",
+    "system_hide_splash_screen_test.cc",
+    "system_is_debugger_attached_test.cc",
+    "system_sort_test.cc",
+    "system_symbolize_test.cc",
+    "thread_create_test.cc",
+    "thread_detach_test.cc",
+    "thread_get_current_test.cc",
+    "thread_get_id_test.cc",
+    "thread_get_name_test.cc",
+    "thread_helpers.cc",
+    "thread_is_equal_test.cc",
+    "thread_join_test.cc",
+    "thread_local_value_test.cc",
+    "thread_set_name_test.cc",
+    "thread_sleep_test.cc",
+    "thread_yield_test.cc",
+    "time_get_monotonic_now_test.cc",
+    "time_get_now_test.cc",
+    "time_narrow_test.cc",
+    "time_zone_get_current_test.cc",
+    "time_zone_get_dst_name_test.cc",
+    "time_zone_get_name_test.cc",
+    "user_get_current_test.cc",
+    "user_get_property_test.cc",
+    "user_get_signed_in_test.cc",
+    "window_create_test.cc",
+    "window_destroy_test.cc",
+    "window_get_platform_handle_test.cc",
+    "window_get_size_test.cc",
+  ]
+  # Include private C headers, if present.
+  private_sources = exec_script("//starboard/tools/find_private_files.py",
+                                [
+                                  rebase_path("//", root_build_dir),
+                                  "nplb/include_all_private.c",
+                                ],
+                                "list lines",
+                                [ "." ])
+  # Include private tests, if present.
+  private_sources += exec_script("//starboard/tools/find_private_files.py",
+                                 [
+                                   rebase_path("//", root_build_dir),
+                                   "nplb/*_test.cc",
+                                 ],
+                                 "list lines",
+                                 [ "." ])
+  sources += rebase_path(private_sources, ".", root_build_dir)
+
+  deps = [
+    "//starboard",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+deploy("deploy") {
+  testonly = true
+  executable_name = "nplb"
+
+  deps = [
+    ":nplb",
+  ]
+}
diff --git a/src/starboard/nplb/align_test.cc b/src/starboard/nplb/align_test.cc
new file mode 100644
index 0000000..ce53310
--- /dev/null
+++ b/src/starboard/nplb/align_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/configuration.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+bool IsAligned(void *pointer, size_t alignment) {
+  return (reinterpret_cast<uintptr_t>(pointer) % alignment) == 0;
+}
+
+size_t GetAlignment(void *pointer) {
+  for (size_t alignment = static_cast<size_t>(1) << ((sizeof(size_t) * 8) - 1);
+       alignment != 0; alignment /= 2) {
+    if (IsAligned(pointer, alignment)) {
+      return alignment;
+    }
+  }
+
+  return 1;
+}
+
+struct AlignedFields {
+  char unaligned1;
+  SB_ALIGNAS(2) char by_2;
+  char unaligned2;
+  SB_ALIGNAS(4) char by_4;
+  char unaligned3;
+  SB_ALIGNAS(8) char by_8;
+  char unaligned4;
+  SB_ALIGNAS(16) char by_16;
+  char unaligned5;
+  SB_ALIGNAS(32) char by_32;
+  char unaligned6;
+  SB_ALIGNAS(64) char by_64;
+  char unaligned7;
+  SB_ALIGNAS(128) char by_128;
+  char unaligned8;
+  SB_ALIGNAS(256) char by_256;
+  char unaligned9;
+};
+
+struct AlignedFieldsOf {
+  char unaligned1;
+  SB_ALIGNAS(SB_ALIGNOF(void*)) char by_void_star;
+  char unaligned2;
+  SB_ALIGNAS(SB_ALIGNOF(int)) char by_int;
+  char unaligned3;
+  SB_ALIGNAS(SB_ALIGNOF(uint64_t)) char by_uint64_t;
+  char unaligned4;
+};
+
+TEST(SbAlignTest, AlignAsStructFieldOnStack) {
+  char unaligned = 0;
+  EXPECT_NE(1, unaligned);
+  AlignedFields aligned;
+  EXPECT_LE(2, GetAlignment(&(aligned.by_2)));
+  EXPECT_LE(4, GetAlignment(&(aligned.by_4)));
+  EXPECT_LE(8, GetAlignment(&(aligned.by_8)));
+  EXPECT_LE(16, GetAlignment(&(aligned.by_16)));
+  EXPECT_LE(32, GetAlignment(&(aligned.by_32)));
+  EXPECT_LE(64, GetAlignment(&(aligned.by_64)));
+  EXPECT_LE(128, GetAlignment(&(aligned.by_128)));
+  EXPECT_LE(256, GetAlignment(&(aligned.by_256)));
+}
+
+TEST(SbAlignTest, AlignAsStackVariable) {
+  char unaligned1 = 1;
+  SB_ALIGNAS(2) char by_2;
+  char unaligned2 = 2;
+  EXPECT_NE(unaligned2, unaligned1);  // These are to try to keep the stack
+                                      // variables around.
+  SB_ALIGNAS(4) char by_4;
+  char unaligned3 = 3;
+  EXPECT_NE(unaligned3, unaligned1);
+  SB_ALIGNAS(8) char by_8;
+  char unaligned4 = 4;
+  EXPECT_NE(unaligned4, unaligned1);
+  SB_ALIGNAS(16) char by_16;
+  char unaligned5 = 5;
+  EXPECT_NE(unaligned5, unaligned1);
+
+  EXPECT_LE(2, GetAlignment(&by_2));
+  EXPECT_LE(4, GetAlignment(&by_4));
+  EXPECT_LE(8, GetAlignment(&by_8));
+  EXPECT_LE(16, GetAlignment(&by_16));
+
+#if !SB_HAS_QUIRK(DOES_NOT_STACK_ALIGN_OVER_16_BYTES)
+  SB_ALIGNAS(32) char by_32;
+  char unaligned6 = 6;
+  EXPECT_NE(unaligned6, unaligned1);
+  SB_ALIGNAS(64) char by_64;
+  char unaligned7 = 7;
+  EXPECT_NE(unaligned7, unaligned1);
+  SB_ALIGNAS(128) char by_128;
+  char unaligned8 = 8;
+  EXPECT_NE(unaligned8, unaligned1);
+  SB_ALIGNAS(256) char by_256;
+  char unaligned9 = 9;
+  EXPECT_NE(unaligned9, unaligned1);
+
+  EXPECT_LE(32, GetAlignment(&by_32));
+  EXPECT_LE(64, GetAlignment(&by_64));
+  EXPECT_LE(128, GetAlignment(&by_128));
+  EXPECT_LE(256, GetAlignment(&by_256));
+#endif  // !SB_HAS_QUIRK(DOES_NOT_STACK_ALIGN_OVER_16_BYTES)
+}
+
+TEST(SbAlignTest, AlignOf) {
+  EXPECT_LE(sizeof(uint8_t), SB_ALIGNOF(uint8_t));
+  EXPECT_LE(sizeof(uint16_t), SB_ALIGNOF(uint16_t));
+  EXPECT_LE(sizeof(uint32_t), SB_ALIGNOF(uint32_t));
+  EXPECT_LE(sizeof(uint64_t), SB_ALIGNOF(uint64_t));
+  EXPECT_LE(sizeof(uintptr_t), SB_ALIGNOF(uintptr_t));
+  EXPECT_LE(sizeof(void*), SB_ALIGNOF(void*));
+}
+
+TEST(SbAlignTest, AlignAsAlignOfStructFieldOnStack) {
+  char unaligned = 0;
+  EXPECT_NE(1, unaligned);
+
+  AlignedFieldsOf aligned;
+  EXPECT_LE(SB_ALIGNOF(void*), GetAlignment(&(aligned.by_void_star)));
+  EXPECT_LE(SB_ALIGNOF(int), GetAlignment(&(aligned.by_int)));
+  EXPECT_LE(SB_ALIGNOF(uint64_t), GetAlignment(&(aligned.by_uint64_t)));
+}
+
+TEST(SbAlignTest, AlignAsAlignOfStackVariable) {
+  char unaligned = 0;
+  EXPECT_NE(1, unaligned);
+  SB_ALIGNAS(SB_ALIGNOF(void*)) char by_void_star;
+  SB_ALIGNAS(SB_ALIGNOF(int)) char by_int;
+  SB_ALIGNAS(SB_ALIGNOF(uint64_t)) char by_uint64_t;
+
+  EXPECT_LE(SB_ALIGNOF(void*), GetAlignment(&by_void_star));
+  EXPECT_LE(SB_ALIGNOF(int), GetAlignment(&by_int));
+  EXPECT_LE(SB_ALIGNOF(uint64_t), GetAlignment(&by_uint64_t));
+}
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/file_helpers.cc b/src/starboard/nplb/file_helpers.cc
index f6a9f43..07421a9 100644
--- a/src/starboard/nplb/file_helpers.cc
+++ b/src/starboard/nplb/file_helpers.cc
@@ -41,6 +41,13 @@
 }
 
 // static
+std::string ScopedRandomFile::MakeRandomFilename() {
+  std::ostringstream filename_stream;
+  filename_stream << "ScopedRandomFile.File_" << SbSystemGetRandomUInt64();
+  return filename_stream.str();
+}
+
+// static
 void ScopedRandomFile::ExpectPattern(int pattern_offset,
                                      void* buffer,
                                      int size,
@@ -53,22 +60,19 @@
 }
 
 // static
-std::string ScopedRandomFile::MakeRandomFilename() {
-  char path[kPathSize] = {0};
-  bool result = SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize);
-  EXPECT_TRUE(result);
-  if (!result) {
+std::string ScopedRandomFile::MakeRandomFilePath() {
+  std::ostringstream filename_stream;
+  filename_stream << GetTempDir();
+  if (!filename_stream.tellp()) {
     return "";
   }
 
-  std::ostringstream filename_stream;
-  filename_stream << path << SB_FILE_SEP_CHAR << "ScopedRandomFile.File_"
-                  << SbSystemGetRandomUInt64();
+  filename_stream << SB_FILE_SEP_CHAR << MakeRandomFilename();
   return filename_stream.str();
 }
 
 std::string ScopedRandomFile::MakeRandomFile(int length) {
-  std::string filename = MakeRandomFilename();
+  std::string filename = MakeRandomFilePath();
   if (filename.empty()) {
     return filename;
   }
diff --git a/src/starboard/nplb/file_helpers.h b/src/starboard/nplb/file_helpers.h
index ad0c946..0a8ba5b 100644
--- a/src/starboard/nplb/file_helpers.h
+++ b/src/starboard/nplb/file_helpers.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_NBLP_FILE_HELPERS_H_
-#define STARBOARD_NBLP_FILE_HELPERS_H_
+#ifndef STARBOARD_NPLB_FILE_HELPERS_H_
+#define STARBOARD_NPLB_FILE_HELPERS_H_
 
 #include <string>
 
@@ -44,7 +44,7 @@
   }
 
   // Will create a file |length| bytes long.
-  ScopedRandomFile(int length) : size_(length) {
+  explicit ScopedRandomFile(int length) : size_(length) {
     filename_ = MakeRandomFile(size_);
   }
 
@@ -52,18 +52,22 @@
   // filename.  |create| is whether to create the file or not.
   ScopedRandomFile(int length, Create create) : size_(length) {
     filename_ =
-        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilename());
+        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilePath());
   }
 
   // Will either create a file of |kDefaultLength| bytes long, or will just
   // generate a filename.  |create| is whether to create the file or not.
-  ScopedRandomFile(Create create) : size_(kDefaultLength) {
+  explicit ScopedRandomFile(Create create) : size_(kDefaultLength) {
     filename_ =
-        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilename());
+        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilePath());
   }
 
   ~ScopedRandomFile() { SbFileDelete(filename_.c_str()); }
 
+  // Creates and returns a random filename (no path), but does not create the
+  // file.
+  static std::string MakeRandomFilename();
+
   // Returns the filename generated for this file.
   const std::string& filename() const { return filename_; }
 
@@ -83,8 +87,8 @@
   // the new file.
   static std::string MakeRandomFile(int length);
 
-  // Creates and returns a random filename, but does not create the file.
-  static std::string MakeRandomFilename();
+  // Creates and returns a path to a random file, but does not create the file.
+  static std::string MakeRandomFilePath();
 
   std::string filename_;
   int size_;
@@ -93,4 +97,4 @@
 }  // namespace nplb
 }  // namespace starboard
 
-#endif
+#endif  // STARBOARD_NPLB_FILE_HELPERS_H_
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 6c0c76f..274a67f 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -24,6 +24,7 @@
       'sources': [
         '<(DEPTH)/starboard/common/test_main.cc',
         'accessibility_get_setting_test.cc',
+        'align_test.cc',
         'atomic_test.cc',
         'audio_sink_create_test.cc',
         'audio_sink_destroy_test.cc',
@@ -146,6 +147,7 @@
         'mutex_create_test.cc',
         'mutex_destroy_test.cc',
         'once_test.cc',
+        'optional_test.cc',
         'player_create_test.cc',
         'random_helpers.cc',
         'rwlock_test.cc',
@@ -184,6 +186,7 @@
         'speech_recognizer_start_test.cc',
         'speech_recognizer_stop_test.cc',
         'speech_synthesis_basic_test.cc',
+        'state_machine_test.cc',
         'storage_close_record_test.cc',
         'storage_delete_record_test.cc',
         'storage_get_record_size_test.cc',
diff --git a/src/starboard/nplb/optional_test.cc b/src/starboard/nplb/optional_test.cc
new file mode 100644
index 0000000..c467d2a
--- /dev/null
+++ b/src/starboard/nplb/optional_test.cc
@@ -0,0 +1,830 @@
+/*
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "starboard/common/optional.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InSequence;
+
+namespace starboard {
+namespace {
+
+TEST(OptionalTest, EnsureDefaultConstructorGivesDisengagedOptional) {
+  optional<int> test;
+  EXPECT_TRUE(!test);
+}
+
+TEST(OptionalTest, EnsureNullOptConstructorGivesDisengagedOptional) {
+  optional<int> test(nullopt);
+  EXPECT_TRUE(!test);
+}
+
+TEST(OptionalTest, InitializeConstructor) {
+  optional<int> test(2);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(2, test.value());
+}
+
+TEST(OptionalTest, BoolCastOperator) {
+  optional<int> test;
+  EXPECT_FALSE(static_cast<bool>(test));
+
+  test = 5;
+  EXPECT_TRUE(static_cast<bool>(test));
+}
+
+TEST(OptionalTest, InitializeAssign) {
+  optional<int> test = 2;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(2, test.value());
+}
+
+TEST(OptionalTest, ReassignValue) {
+  optional<int> test(2);
+  test = 5;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, CopyAssignment) {
+  optional<int> a;
+  optional<int> b = 2;
+  optional<int> c = 3;
+  a = b;
+  EXPECT_FALSE(!a);
+  EXPECT_EQ(2, a.value());
+
+  a = c;
+  EXPECT_FALSE(!a);
+  EXPECT_EQ(3, a.value());
+}
+
+TEST(OptionalTest, ClearAssignment) {
+  optional<int> test(2);
+  test = optional<int>();
+  EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, EnsureAssignmentOfNullOptResultsInDisengagement) {
+  optional<int> test(2);
+  test = nullopt;
+  EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, EnsureAssignmentOfDefaultOptionalResultsInDisengagement) {
+  optional<int> test(2);
+  test = optional<int>();
+  EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, CopyConstruction) {
+  optional<int> test1(2);
+  optional<int> test2(test1);
+
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(2, test2.value());
+}
+
+TEST(OptionalTest, Swap) {
+  optional<int> test1(1);
+  optional<int> test2(2);
+
+  // Swap two engaged optionals.
+  test1.swap(test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(2, test1.value());
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two optionals where only one is engaged.
+  test1 = nullopt;
+  test1.swap(test2);
+  EXPECT_FALSE(test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(1, test1.value());
+
+  // Swap two optionals where only one is engaged, except the other way around.
+  test1.swap(test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two disengaged optionals.
+  test2 = nullopt;
+  test1.swap(test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(test2);
+}
+
+TEST(OptionalTest, StdSwap) {
+  optional<int> test1(1);
+  optional<int> test2(2);
+
+  // Swap two engaged optionals.
+  std::swap(test1, test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(2, test1.value());
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two optionals where only one is engaged.
+  test1 = nullopt;
+  std::swap(test1, test2);
+  EXPECT_FALSE(test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(1, test1.value());
+
+  // Swap two optionals where only one is engaged, except the other way around.
+  std::swap(test1, test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two disengaged optionals.
+  test2 = nullopt;
+  std::swap(test1, test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(test2);
+}
+
+TEST(OptionalTest, SwapWithSelf) {
+  optional<int> test(1);
+
+  test.swap(test);
+  EXPECT_EQ(1, test.value());
+
+  std::swap(test, test);
+  EXPECT_EQ(1, test.value());
+}
+
+TEST(OptionalTest, AsteriskDereference) {
+  optional<int> test(2);
+  EXPECT_EQ(2, *test);
+
+  *test = 5;
+  EXPECT_EQ(5, test.value());
+}
+
+struct TestStruct {
+  int foobar;
+};
+
+TEST(OptionalTest, ArrowDereference) {
+  TestStruct a;
+  a.foobar = 2;
+
+  optional<TestStruct> test(a);
+  EXPECT_EQ(2, test->foobar);
+
+  test->foobar = 5;
+  EXPECT_EQ(5, test.value().foobar);
+
+  // Test const arrow dereference.
+  const optional<TestStruct> test_const(a);
+  EXPECT_EQ(2, test_const->foobar);
+}
+
+class NoDefaultTest {
+ public:
+  explicit NoDefaultTest(int i) { foobar_ = i; }
+  int foobar() const { return foobar_; }
+
+ private:
+  NoDefaultTest();
+
+  int foobar_;
+};
+
+TEST(OptionalTest, NoDefaultConstructorIsSupported) {
+  // First test with an object passed in upon construction
+  optional<NoDefaultTest> test1(NoDefaultTest(2));
+  EXPECT_EQ(2, test1.value().foobar());
+
+  // Now test with an object assignment after construction
+  optional<NoDefaultTest> test2;
+  test2 = NoDefaultTest(5);
+  EXPECT_EQ(5, test2.value().foobar());
+}
+
+TEST(OptionalTest, TestEquivalenceComparisons) {
+  optional<int> test1 = 1;
+  optional<int> test2 = 2;
+  optional<int> test3;
+  optional<int> test4 = 2;
+
+  EXPECT_FALSE(test1 == test2);  // NOLINT(readability/check)
+  EXPECT_FALSE(test2 == test1);  // NOLINT(readability/check)
+  EXPECT_FALSE(test1 == test3);  // NOLINT(readability/check)
+  EXPECT_FALSE(test3 == test1);  // NOLINT(readability/check)
+  EXPECT_FALSE(2 == test1);      // NOLINT(readability/check)
+  EXPECT_FALSE(test1 == 2);      // NOLINT(readability/check)
+  EXPECT_EQ(1, test1);
+  EXPECT_EQ(test1, 1);
+  EXPECT_EQ(test4, test2);
+  EXPECT_EQ(test2, test4);
+
+  // Test nullopt comparisons
+  EXPECT_TRUE(test3 == nullopt);
+  EXPECT_TRUE(nullopt == test3);
+  EXPECT_FALSE(test1 == nullopt);
+  EXPECT_FALSE(nullopt == test1);
+}
+
+TEST(OptionalTest, TestLessThanComparisons) {
+  optional<int> test1 = 1;
+  optional<int> test2 = 2;
+  optional<int> test3;
+  optional<int> test4 = 2;
+
+  EXPECT_TRUE(test1 < test2);
+  EXPECT_FALSE(test2 < test1);
+  EXPECT_FALSE(test1 < test1);
+  EXPECT_TRUE(test3 < test1);
+  EXPECT_FALSE(test1 < test3);
+  EXPECT_FALSE(test3 < test3);
+
+  EXPECT_TRUE(nullopt < test1);
+  EXPECT_FALSE(test1 < nullopt);
+
+  EXPECT_TRUE(test1 < 2);   // NOLINT(readability/check)
+  EXPECT_FALSE(2 < test1);  // NOLINT(readability/check)
+}
+
+TEST(OptionalTest, EnsureOptionalWorksWithFunnyAlignments) {
+  struct {
+    char c;
+    optional<uint64_t> number;
+  } foo;
+
+  EXPECT_FALSE(!!foo.number);
+  foo.number = 1;
+  EXPECT_TRUE(!!foo.number);
+  EXPECT_EQ(1, foo.number.value());
+}
+
+class DestructorCallTester {
+ public:
+  DestructorCallTester() {}
+  DestructorCallTester(const DestructorCallTester&) {}
+  ~DestructorCallTester() { Die(); }
+  MOCK_METHOD0(Die, void());
+};
+
+TEST(OptionalTest, EnsureDestructorIsCalled) {
+  {
+    // Ensure destructor is called upon optional destruction
+    optional<DestructorCallTester> test(in_place);
+    EXPECT_CALL(test.value(), Die());
+  }
+
+  {
+    // Ensure destructor is called upon assignment to null
+    optional<DestructorCallTester> test1(in_place);
+    optional<DestructorCallTester> test2(in_place);
+    {
+      InSequence s;
+      EXPECT_CALL(test1.value(), Die());
+      EXPECT_CALL(test2.value(), Die());
+    }
+    test1 = nullopt;
+  }
+}
+
+// This class counts all calls to the set of methods declared in the class.
+// It can be used to verify that certain methods are indeed called.
+class MethodCallCounter {
+ public:
+  MethodCallCounter() {
+    ResetCounts();
+    ++default_constructor_calls_;
+  }
+
+  MethodCallCounter(const MethodCallCounter& other) {
+    // A very non-standard copy constructor, since this is a test object
+    // intended to count method calls on this object and only this object.
+    ResetCounts();
+    ++copy_constructor_calls_;
+  }
+
+  MethodCallCounter(MethodCallCounter&& other) {  // NOLINT(build/c++11)
+    // A very non-standard move constructor, since this is a test object
+    // intended to count method calls on this object and only this object.
+    ResetCounts();
+    ++move_constructor_calls_;
+  }
+
+  MethodCallCounter& operator=(const MethodCallCounter& other) {
+    ++assignment_calls_;
+    return *this;
+  }
+
+  // NOLINTNEXTLINE(build/c++11)
+  MethodCallCounter& operator=(MethodCallCounter&& other) {
+    ++move_assignment_calls_;
+    return *this;
+  }
+
+  int assignment_calls() const { return assignment_calls_; }
+  int copy_constructor_calls() const { return copy_constructor_calls_; }
+  int default_constructor_calls() const { return default_constructor_calls_; }
+  int move_assignment_calls() const { return move_assignment_calls_; }
+  int move_constructor_calls() const { return move_constructor_calls_; }
+
+ private:
+  void ResetCounts() {
+    assignment_calls_ = 0;
+    copy_constructor_calls_ = 0;
+    default_constructor_calls_ = 0;
+    move_assignment_calls_ = 0;
+    move_constructor_calls_ = 0;
+  }
+
+  int assignment_calls_;
+  int copy_constructor_calls_;
+  int default_constructor_calls_;
+  int move_assignment_calls_;
+  int move_constructor_calls_;
+};
+
+TEST(OptionalTest, CopyConstructorIsCalledByValueCopyConstructor) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(original_counter);
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(1, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, CopyConstructorIsCalledByOptionalCopyConstructor) {
+  optional<MethodCallCounter> test1(in_place);
+  optional<MethodCallCounter> test2(test1);
+
+  EXPECT_EQ(0, test2->assignment_calls());
+  EXPECT_EQ(1, test2->copy_constructor_calls());
+  EXPECT_EQ(0, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+TEST(OptionalTest, CopyConstructorIsCalledByOptionalAssignment) {
+  optional<MethodCallCounter> test1(in_place);
+  optional<MethodCallCounter> test2;
+  test2 = test1;
+
+  EXPECT_EQ(0, test2->assignment_calls());
+  EXPECT_EQ(1, test2->copy_constructor_calls());
+  EXPECT_EQ(0, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+TEST(OptionalTest, AssignmentIsCalledByValueAssignment) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(in_place);
+  test = original_counter;
+
+  EXPECT_EQ(1, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, AssignmentIsCalledByOptionalAssignment) {
+  optional<MethodCallCounter> test1(in_place);
+  optional<MethodCallCounter> test2(in_place);
+  test2 = test1;
+
+  EXPECT_EQ(1, test2->assignment_calls());
+  EXPECT_EQ(0, test2->copy_constructor_calls());
+  EXPECT_EQ(1, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnOptionalMoveConstructor) {
+  optional<MethodCallCounter> test(
+      std::move(optional<MethodCallCounter>(in_place)));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnUnengagedOptionalMoveAssignment) {
+  optional<MethodCallCounter> test;
+  test = std::move(optional<MethodCallCounter>(in_place));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveAssignmentIsCalledOnEngagedMoveOptionalAssignment) {
+  optional<MethodCallCounter> test(in_place);
+  test = std::move(optional<MethodCallCounter>(in_place));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(1, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnUnengagedMoveValueConstructor) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(std::move(original_counter));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnUnengagedMoveValueAssignment) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test;
+  test = std::move(original_counter);
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveAssignmentIsCalledOnEngagedMoveValueAssignment) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(in_place);
+  test = std::move(original_counter);
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(1, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, EnsureSelfAssignmentIsOkay) {
+  // Ensure that values are as we expect them to be.
+  optional<int> test1(5);
+
+  test1 = test1;
+
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(5, test1.value());
+
+  // Ensure that the methods we expect to be called are actually called.
+  optional<MethodCallCounter> test2(in_place);
+
+  test2 = test2;
+
+  EXPECT_EQ(1, test2->assignment_calls());
+  EXPECT_EQ(0, test2->copy_constructor_calls());
+  EXPECT_EQ(1, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+// Helper classes to ensure that we can assign different values to optionals
+// so long as the wrapped value can be assigned and constructed from the other.
+struct XType {
+  explicit XType(int number) { number_ = number; }
+
+  int number_;
+};
+
+struct YType {
+  explicit YType(int number) { number_ = number; }
+
+  explicit YType(const XType& x_type) { number_ = x_type.number_; }
+
+  YType& operator=(const XType& x_type) {
+    number_ = x_type.number_;
+    return *this;
+  }
+
+  int number_;
+};
+
+TEST(OptionalTest, CopyConstructorIsCalledByValueAssignmentFromOtherType) {
+  XType x_type(1);
+  optional<YType> test;
+  test = x_type;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->number_);
+}
+
+TEST(OptionalTest, AssignmentIsCalledByValueAssignmentFromOtherType) {
+  XType x_type(1);
+  optional<YType> test(in_place, 2);
+  test = x_type;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->number_);
+}
+
+TEST(OptionalTest, TestMakeOptional) {
+  optional<int> test = make_optional(5);
+
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, ValueOrTest) {
+  optional<int> test;
+
+  EXPECT_EQ(4, test.value_or(4));
+
+  test = 2;
+
+  EXPECT_EQ(2, test.value_or(4));
+}
+
+TEST(OptionalTest, ConstOptionalsTest) {
+  const optional<int> test1(5);
+
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(5, test1.value());
+  EXPECT_EQ(5, *test1);
+
+  const optional<int> test2(test1);
+
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(5, test2.value());
+  EXPECT_EQ(5, *test2);
+
+  optional<int> test3;
+  test3 = test2;
+
+  EXPECT_FALSE(!test3);
+  EXPECT_EQ(5, test3.value());
+  EXPECT_EQ(5, *test3);
+}
+
+TEST(OptionalTest, EmplaceInt) {
+  optional<int> test;
+
+  test.emplace(5);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, EmplaceWithDefaultConstructor) {
+  optional<MethodCallCounter> test;
+  test.emplace();
+
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+class NoDefaultOrCopyConstructor {
+ public:
+  NoDefaultOrCopyConstructor(int x, int y) {
+    x_ = x;
+    y_ = y;
+  }
+
+  int x() const { return x_; }
+  int y() const { return y_; }
+
+ private:
+  NoDefaultOrCopyConstructor();
+  NoDefaultOrCopyConstructor(const NoDefaultOrCopyConstructor&);
+
+  int x_;
+  int y_;
+};
+
+TEST(OptionalTest, EmplaceObjectWithNoDefaultOrCopyConstructor) {
+  optional<NoDefaultOrCopyConstructor> test;
+  test.emplace(1, 2);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->x());
+  EXPECT_EQ(2, test->y());
+}
+
+class NonConstPointerInConstructor {
+ public:
+  explicit NonConstPointerInConstructor(int* param) { param_ = param; }
+
+  void SetParam(int value) { *param_ = value; }
+
+ private:
+  int* param_;
+};
+
+TEST(OptionalTest, EmplaceObjectWithNonConstPointer) {
+  int value = 0;
+  optional<NonConstPointerInConstructor> test;
+  test.emplace(&value);
+  test->SetParam(5);
+  EXPECT_EQ(5, value);
+}
+
+TEST(OptionalTest, ForwardingConstructorInt) {
+  optional<int> test(in_place, 5);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, ForwardingConstructorWithDefaultConstructor) {
+  optional<MethodCallCounter> test(in_place);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->assignment_calls());
+}
+
+TEST(OptionalTest, ForwardingConstructorObjectWithNoDefaultOrCopyConstructor) {
+  optional<NoDefaultOrCopyConstructor> test(in_place, 1, 2);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->x());
+  EXPECT_EQ(2, test->y());
+}
+
+TEST(OptionalTest, ForwardingConstructorObjectWithNonConstPointer) {
+  int value = 0;
+  optional<NonConstPointerInConstructor> test(in_place, &value);
+  test->SetParam(5);
+  EXPECT_EQ(5, value);
+}
+
+TEST(OptionalTest, OptionalStringTest) {
+  {
+    optional<std::string> test;
+    EXPECT_TRUE(!test);
+
+    test = std::string("foo");
+    EXPECT_STREQ("foo", test->c_str());
+  }
+
+  {
+    optional<std::string> test(std::string("foo"));
+    EXPECT_FALSE(!test);
+
+    EXPECT_STREQ("foo", test->c_str());
+    optional<std::string> test2(test);
+    EXPECT_STREQ("foo", test2->c_str());
+  }
+}
+
+TEST(OptionalTest, OptionalStringInSetTest) {
+  // Optional strings in map test
+  std::set<optional<std::string>> optional_string_set;
+
+  optional_string_set.insert(std::string("foo"));
+  optional_string_set.insert(std::string("bar"));
+
+  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+              optional_string_set.end());
+  EXPECT_FALSE(optional_string_set.find(optional<std::string>()) !=
+               optional_string_set.end());
+
+  optional_string_set.insert(optional<std::string>());
+
+  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(optional<std::string>()) !=
+              optional_string_set.end());
+}
+
+TEST(OptionalTest, StdVectorOfOptionalsWorksFine) {
+  std::vector<optional<int>> test_vector;
+
+  test_vector.reserve(test_vector.capacity() + 1);
+  test_vector.resize(test_vector.capacity());
+
+  // Make sure all current optionals are disengaged.
+  for (std::vector<optional<int>>::const_iterator iter = test_vector.begin();
+       iter != test_vector.end(); ++iter) {
+    EXPECT_TRUE(!*iter);
+  }
+  EXPECT_TRUE(!test_vector[0]);
+
+  test_vector.clear();
+  test_vector.resize(test_vector.capacity() + 1, 5);
+  for (std::vector<optional<int>>::const_iterator iter = test_vector.begin();
+       iter != test_vector.end(); ++iter) {
+    ASSERT_FALSE(!*iter);
+    EXPECT_EQ(5, **iter);
+  }
+  ASSERT_FALSE(!test_vector[0]);
+  EXPECT_EQ(5, *test_vector[0]);
+
+  test_vector.push_back(8);
+  ASSERT_FALSE(!test_vector.back());
+  EXPECT_EQ(8, *test_vector.back());
+}
+
+TEST(OptionalTest, OptionalStringInUnorderedSet) {
+  std::unordered_set<optional<std::string>> optional_string_set;
+
+  optional_string_set.insert(std::string());
+  optional_string_set.insert(std::string("7"));
+  optional_string_set.insert(std::string("123456"));
+
+  EXPECT_TRUE(optional_string_set.find(std::string("7")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("123456")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string()) !=
+              optional_string_set.end());
+  EXPECT_FALSE(optional_string_set.find(optional<std::string>()) !=
+               optional_string_set.end());
+
+  optional_string_set.insert(optional<std::string>());
+
+  EXPECT_TRUE(optional_string_set.find(std::string("7")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("123456")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string()) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(optional<std::string>()) !=
+              optional_string_set.end());
+
+  optional_string_set.erase(std::string());
+
+  EXPECT_FALSE(optional_string_set.find(std::string()) !=
+               optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(optional<std::string>()) !=
+              optional_string_set.end());
+}
+
+TEST(OptionalTest, OptionalIntInUnorderedSet) {
+  std::unordered_set<optional<int>> optional_int_set;
+
+  optional_int_set.insert(0);
+  optional_int_set.insert(7);
+  optional_int_set.insert(123456);
+
+  EXPECT_TRUE(optional_int_set.find(7) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(123456) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(0) != optional_int_set.end());
+  EXPECT_FALSE(optional_int_set.find(optional<int>()) !=
+               optional_int_set.end());
+
+  optional_int_set.insert(optional<int>());
+
+  EXPECT_TRUE(optional_int_set.find(7) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(123456) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(0) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(optional<int>()) != optional_int_set.end());
+
+  optional_int_set.erase(0);
+
+  EXPECT_FALSE(optional_int_set.find(0) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(optional<int>()) != optional_int_set.end());
+}
+
+optional<int> ConditionallyMakeOptional(bool should_make, int value) {
+  if (should_make) {
+    return optional<int>(value);
+  } else {
+    return nullopt;
+  }
+}
+
+TEST(OptionalTest, OptionalCanBeReturnedFromFunction) {
+  optional<int> test1 = ConditionallyMakeOptional(true, 5);
+  ASSERT_FALSE(!test1);
+  EXPECT_EQ(5, test1.value());
+
+  optional<int> test2 = ConditionallyMakeOptional(false, 5);
+  EXPECT_TRUE(!test2);
+}
+
+}  // namespace
+}  // namespace starboard
diff --git a/src/base/state_machine_shell_unittest.cc b/src/starboard/nplb/state_machine_test.cc
similarity index 85%
rename from src/base/state_machine_shell_unittest.cc
rename to src/starboard/nplb/state_machine_test.cc
index 0d2bda5..86888c0 100644
--- a/src/base/state_machine_shell_unittest.cc
+++ b/src/starboard/nplb/state_machine_test.cc
@@ -2,14 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/state_machine_shell.h"
+#include "starboard/common/state_machine.h"
 
-#include <list>
 #include <iostream>
+#include <list>
 
-#include "base/logging.h"
+#include "starboard/log.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+// Enables some verbose logging for debugging the state machine test and
+// implementation.
+#define STATE_MACHINE_TEST_DEBUG 0
+
+namespace starboard {
+namespace {
 namespace hsm {
 
 // Constant to represent no version for TestHsm::Expect.
@@ -42,17 +48,13 @@
 
 // An enumeration of things that the HSM does that we can sense and then
 // assert about.
-enum HsmEvent {
-  kHsmEnter,
-  kHsmExit,
-  kHsmHandled
-};
+enum HsmEvent { kHsmEnter, kHsmExit, kHsmHandled };
 
-} // namespace hsm
+}  // namespace hsm
 
 // --- Test Subclass ---
 
-// StateMachineShell is an abstract class, so we must subclass it to test it.
+// StateMachine is an abstract class, so we must subclass it to test it.
 // This class uses the sample test state machine specified by Miro Samek in his
 // Practical Statecharts book. It covers the interesting transitions and
 // topologies, so if it's fully exercised, it should represent a
@@ -64,24 +66,23 @@
 // This version has:
 //  - A new event, I, in state S11, to test reentrant event handling.
 //  - A new event, J, and new state T0, to test the no top state case.
-class TestHsm : public base::StateMachineShell<hsm::TestState, hsm::TestEvent> {
+class TestHsm : public StateMachine<hsm::TestState, hsm::TestEvent> {
  public:
-
   // --- Some types to aid sensing ---
 
   struct ResultEvent {
     // The state the HSM was in when it handled the event.
-    const StateEnumN state;
+    const optional<hsm::TestState> state;
 
     // The data passed into the event, if any.
-    const void *data;
+    const void* data;
 
     // The state that actually handled the event (could be an ancestor of the
     // current state.
-    const StateEnumN event_state;
+    const optional<hsm::TestState> event_state;
 
     // The event that was handled.
-    const EventEnumN event;
+    const optional<hsm::TestEvent> event;
 
     // The "HSM Event" that occurred causing this to be recorded.
     const hsm::HsmEvent hsm_event;
@@ -104,32 +105,32 @@
   std::list<ResultEvent> results;
 
   TestHsm()
-      : base::StateMachineShell<hsm::TestState, hsm::TestEvent>("TestHsm"),
+      : StateMachine<hsm::TestState, hsm::TestEvent>("TestHsm"),
         foo_(true),
         event_i_count_(0) {
-    if (VLOG_IS_ON(1)) {
-      EnableLogging();
-    }
+#if STATE_MACHINE_TEST_DEBUG
+    EnableLogging();
+#endif
   }
-  virtual ~TestHsm() { }
+  virtual ~TestHsm() {}
 
   // Clears the results list, so nothing bleeds between test cases.
-  void ClearResults() {
-    results.clear();
-  }
+  void ClearResults() { results.clear(); }
 
   // Consumes and validates a ResultEvent from the results list.
   void Expect(hsm::HsmEvent hsm_event,
-              StateEnumN event_state = StateEnumN(),
-              EventEnumN event = EventEnumN(),
-              StateEnumN current_state = StateEnumN(),
-              void *data = NULL,
+              optional<hsm::TestState> event_state = nullopt,
+              optional<hsm::TestEvent> event = nullopt,
+              optional<hsm::TestState> current_state = nullopt,
+              void* data = NULL,
               uint64_t version = hsm::kNoVersion) {
-    VLOG(1) << __FUNCTION__ << ": hsm_event=" << hsm_event
-            << ", event_state=" << GetStateString(event_state)
-            << ", event=" << GetEventString(event)
-            << ", current_state=" << GetStateString(current_state)
-            << ", data=0x" << std::hex << data << ", version=" << version;
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << ": hsm_event=" << hsm_event
+                  << ", event_state=" << GetStateString(event_state)
+                  << ", event=" << GetEventString(event)
+                  << ", current_state=" << GetStateString(current_state)
+                  << ", data=0x" << std::hex << data << ", version=" << version;
+#endif  // STATE_MACHINE_TEST_DEBUG
 
     EXPECT_FALSE(results.empty());
     TestHsm::ResultEvent result = results.front();
@@ -156,10 +157,10 @@
     }
   }
 
-
-  // --- StateMachineShell Implementation ---
+  // --- StateMachine Implementation ---
  protected:
-  virtual StateEnumN GetUserParentState(hsm::TestState state) const OVERRIDE {
+  virtual optional<hsm::TestState> GetUserParentState(
+      hsm::TestState state) const SB_OVERRIDE {
     switch (state) {
       case hsm::kStateS1:
       case hsm::kStateS2:
@@ -171,11 +172,12 @@
       case hsm::kStateS211:
         return hsm::kStateS21;
       default:
-        return StateEnumN();
+        return nullopt;
     }
   }
 
-  virtual StateEnumN GetUserInitialSubstate(hsm::TestState state) const OVERRIDE {
+  virtual optional<hsm::TestState> GetUserInitialSubstate(
+      hsm::TestState state) const SB_OVERRIDE {
     switch (state) {
       case hsm::kStateS0:
         return hsm::kStateS1;
@@ -186,15 +188,16 @@
       case hsm::kStateS21:
         return hsm::kStateS211;
       default:
-        return StateEnumN();
+        return nullopt;
     }
   }
 
-  virtual hsm::TestState GetUserInitialState() const OVERRIDE {
+  virtual hsm::TestState GetUserInitialState() const SB_OVERRIDE {
     return hsm::kStateS0;
   }
 
-  virtual const char *GetUserStateString(hsm::TestState state) const OVERRIDE {
+  virtual const char* GetUserStateString(hsm::TestState state) const
+      SB_OVERRIDE {
     switch (state) {
       case hsm::kStateS0:
         return "S0";
@@ -215,7 +218,8 @@
     }
   }
 
-  virtual const char *GetUserEventString(hsm::TestEvent event) const OVERRIDE {
+  virtual const char* GetUserEventString(hsm::TestEvent event) const
+      SB_OVERRIDE {
     switch (event) {
       case hsm::kEventA:
         return "A";
@@ -244,9 +248,12 @@
 
   virtual Result HandleUserStateEvent(hsm::TestState state,
                                       hsm::TestEvent event,
-                                      void *data) OVERRIDE {
-    VLOG(1) << __FUNCTION__ << "(" << GetStateString(state) << ", "
-            << GetEventString(event) << ", 0x" << std::hex << data << ");";
+                                      void* data) SB_OVERRIDE {
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << "(" << GetStateString(state) << ", "
+                  << GetEventString(event) << ", 0x" << std::hex << data
+                  << ");";
+#endif  // STATE_MACHINE_TEST_DEBUG
 
     Result result(kNotHandled);
     switch (state) {
@@ -388,37 +395,41 @@
     return result;
   }
 
-  virtual void HandleUserStateEnter(hsm::TestState state) OVERRIDE {
-    VLOG(1) << __FUNCTION__ << "(" << GetStateString(state) << ", ENTER);";
+  virtual void HandleUserStateEnter(hsm::TestState state) SB_OVERRIDE {
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << "(" << GetStateString(state)
+                  << ", ENTER);";
+#endif  // STATE_MACHINE_TEST_DEBUG
 
-    AddEvent(state, EventEnumN(), NULL, hsm::kHsmEnter);
+    AddEvent(state, nullopt, NULL, hsm::kHsmEnter);
   }
 
-  virtual void HandleUserStateExit(hsm::TestState state) OVERRIDE {
-    VLOG(1) << __FUNCTION__ << "(" << GetStateString(state) << ", EXIT);";
+  virtual void HandleUserStateExit(hsm::TestState state) SB_OVERRIDE {
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << "(" << GetStateString(state) << ", EXIT);";
+#endif  // STATE_MACHINE_TEST_DEBUG
 
-    AddEvent(state, EventEnumN(), NULL, hsm::kHsmExit);
+    AddEvent(state, nullopt, NULL, hsm::kHsmExit);
   }
 
  private:
   // Adds a new record to the result list.
   void AddEvent(hsm::TestState state,
-                EventEnumN event,
-                void *data,
+                optional<hsm::TestEvent> event,
+                void* data,
                 hsm::HsmEvent hsm_event) {
-    ResultEvent result_event = { this->state(), data, state, event, hsm_event,
-                                 this->version() };
+    ResultEvent result_event = {this->state(), data,      state,
+                                event,         hsm_event, this->version()};
     results.push_back(result_event);
   }
 };
 
-
 // --- Test Definitions ---
 
 // This test validates that a state machine will initialize itself when it
 // handles its first event, even if the user has not explicitly called
 // initialize.
-TEST(StateMachineShellTest, AutoInit) {
+TEST(StateMachineTest, AutoInit) {
   TestHsm hsm;
 
   hsm.ClearResults();
@@ -440,12 +451,11 @@
   EXPECT_EQ(hsm::kStateS11, hsm.state());
 }
 
-
 // This test validates that if Handle() is called from within an event handler,
 // it queues the event rather than trying to Handle the next event
 // reentrantly. This behavior, or something like it, is required to make the
 // state machine truly run-to-completion.
-TEST(StateMachineShellTest, ReentrantHandle) {
+TEST(StateMachineTest, ReentrantHandle) {
   TestHsm hsm;
   hsm.Initialize();
   EXPECT_EQ(0, hsm.version());
@@ -469,7 +479,7 @@
 // state machine behaves as expected. This should cover all normal operation of
 // the state machine framework, except what is extracted into their own test
 // cases above.
-TEST(StateMachineShellTest, KitNKaboodle) {
+TEST(StateMachineTest, KitNKaboodle) {
   TestHsm hsm;
 
   // Test the initial state
@@ -713,7 +723,7 @@
 
   // Test that event data gets passed through to the handler
   hsm.ClearResults();
-  void *data = reinterpret_cast<void *>(7);
+  void* data = reinterpret_cast<void*>(7);
   hsm.Handle(hsm::kEventC, data);
   hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventC, hsm::kStateS11,
              data);
@@ -725,3 +735,6 @@
   EXPECT_EQ(0, hsm.results.size());
   EXPECT_EQ(hsm::kStateS211, hsm.state());
 }
+
+}  // namespace
+}  // namespace starboard
diff --git a/src/starboard/nplb/storage_delete_record_test.cc b/src/starboard/nplb/storage_delete_record_test.cc
index 16ade12..ef0fc73 100644
--- a/src/starboard/nplb/storage_delete_record_test.cc
+++ b/src/starboard/nplb/storage_delete_record_test.cc
@@ -14,6 +14,7 @@
 
 // Sunny Day cases tested in the read/write tests.
 
+#include "starboard/nplb/file_helpers.h"
 #include "starboard/storage.h"
 #include "starboard/user.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,10 +23,25 @@
 namespace nplb {
 namespace {
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
+TEST(SbStorageDeleteRecordTest, RainyDayInvalidUserValidName) {
+  EXPECT_FALSE(SbStorageDeleteRecord(
+      kSbUserInvalid, ScopedRandomFile::MakeRandomFilename().c_str()));
+}
+
+TEST(SbStorageDeleteRecordTest, RainyDayInvalidUserNullName) {
+  EXPECT_FALSE(SbStorageDeleteRecord(kSbUserInvalid, NULL));
+}
+
+#else  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 TEST(SbStorageDeleteRecordTest, RainyDayInvalidUser) {
   EXPECT_FALSE(SbStorageDeleteRecord(kSbUserInvalid));
 }
 
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/storage_helpers.h b/src/starboard/nplb/storage_helpers.h
index efc422e..c587203 100644
--- a/src/starboard/nplb/storage_helpers.h
+++ b/src/starboard/nplb/storage_helpers.h
@@ -28,23 +28,49 @@
 
 // Deletes the storage for the current user.
 static SB_C_INLINE void ClearStorageRecord() {
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  SbStorageDeleteRecord(SbUserGetCurrent(), NULL);
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   SbStorageDeleteRecord(SbUserGetCurrent());
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
 }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+// Deletes the named storage record for the current user.
+static SB_C_INLINE void ClearStorageRecord(const char* name) {
+  SbStorageDeleteRecord(SbUserGetCurrent(), name);
+}
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 // Opens the storage record for the current user, validating that it is valid.
 static SB_C_INLINE SbStorageRecord OpenStorageRecord() {
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  SbStorageRecord record = SbStorageOpenRecord(SbUserGetCurrent(), NULL);
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   SbStorageRecord record = SbStorageOpenRecord(SbUserGetCurrent());
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   EXPECT_TRUE(SbStorageIsValidRecord(record));
   return record;
 }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+// Opens the named storage record for the current user, validating that it is
+// valid.
+static SB_C_INLINE SbStorageRecord OpenStorageRecord(const char* name) {
+  SbStorageRecord record = SbStorageOpenRecord(SbUserGetCurrent(), name);
+  EXPECT_TRUE(SbStorageIsValidRecord(record));
+  return record;
+}
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 // Writes a standard pattern of |size| bytes into the given open storage
 // |record|.
 static SB_C_INLINE void WriteStorageRecord(SbStorageRecord record,
-                                           int64_t size) {
+                                           int64_t size,
+                                           int64_t pattern_offset = 0) {
   char* data = new char[size];
   for (int64_t i = 0; i < size; ++i) {
-    data[i] = static_cast<char>(i + 2 % 0xFF);
+    data[i] = static_cast<char>((i + pattern_offset + 2) % 0xFF);
   }
   EXPECT_TRUE(SbStorageWriteRecord(record, data, size));
   EXPECT_EQ(size, SbStorageGetRecordSize(record));
@@ -65,15 +91,18 @@
 // WriteStorageRecord) to start at |offset| and continue for |length|, and the
 // rest of the buffer, before and after, should be set to 0.
 static SB_C_INLINE void CheckStorageBuffer(char* data,
-                                    int64_t offset,
-                                    int64_t length,
-                                    int64_t total) {
+                                           int64_t offset,
+                                           int64_t length,
+                                           int64_t total,
+                                           int64_t pattern_offset = 0) {
   for (int64_t i = 0; i < offset; ++i) {
     EXPECT_EQ(0, data[i]) << "i = " << i;
   }
 
   for (int64_t i = 0; i < length; ++i) {
-    EXPECT_EQ(static_cast<char>(i + 2 % 0xFF), data[i + offset]) << "i=" << i;
+    EXPECT_EQ(static_cast<char>((i + pattern_offset + 2) % 0xFF),
+              data[i + offset])
+        << "i=" << i;
   }
 
   for (int64_t i = length + offset; i < total; ++i) {
@@ -86,15 +115,16 @@
 // read bytes is |expected_length| and then checks the buffer for the expected
 // pattern written in WriteStorageRecord over the expected range of the buffer.
 static SB_C_INLINE void ReadAndCheckStorage(SbStorageRecord record,
-                                     int64_t offset,
-                                     int64_t expected_length,
-                                     int64_t length,
-                                     int64_t total) {
+                                            int64_t offset,
+                                            int64_t expected_length,
+                                            int64_t length,
+                                            int64_t total,
+                                            int64_t pattern_offset = 0) {
   char* data = new char[total];
   SbMemorySet(data, 0, total);
   EXPECT_EQ(expected_length,
             SbStorageReadRecord(record, data + offset, length));
-  CheckStorageBuffer(data, offset, expected_length, total);
+  CheckStorageBuffer(data, offset, expected_length, total, pattern_offset);
   delete[] data;
 }
 
diff --git a/src/starboard/nplb/storage_open_record_test.cc b/src/starboard/nplb/storage_open_record_test.cc
index ec1e588..c935e4a 100644
--- a/src/starboard/nplb/storage_open_record_test.cc
+++ b/src/starboard/nplb/storage_open_record_test.cc
@@ -14,6 +14,7 @@
 
 // Sunny Day cases tested in the read/write tests.
 
+#include "starboard/nplb/file_helpers.h"
 #include "starboard/storage.h"
 #include "starboard/user.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,10 +23,26 @@
 namespace nplb {
 namespace {
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
+TEST(SbStorageOpenRecordTest, RainyDayInvalidUserValidName) {
+  EXPECT_FALSE(SbStorageIsValidRecord(SbStorageOpenRecord(
+      kSbUserInvalid, ScopedRandomFile::MakeRandomFilename().c_str())));
+}
+
+TEST(SbStorageOpenRecordTest, RainyDayInvalidUserNullName) {
+  EXPECT_FALSE(
+      SbStorageIsValidRecord(SbStorageOpenRecord(kSbUserInvalid, NULL)));
+}
+
+#else  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 TEST(SbStorageOpenRecordTest, RainyDayInvalidUser) {
   EXPECT_FALSE(SbStorageIsValidRecord(SbStorageOpenRecord(kSbUserInvalid)));
 }
 
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/storage_read_record_test.cc b/src/starboard/nplb/storage_read_record_test.cc
index 4f11a0d..336e716 100644
--- a/src/starboard/nplb/storage_read_record_test.cc
+++ b/src/starboard/nplb/storage_read_record_test.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/nplb/file_helpers.h"
 #include "starboard/nplb/storage_helpers.h"
 #include "starboard/storage.h"
 #include "starboard/user.h"
@@ -22,19 +23,26 @@
 namespace {
 
 TEST(SbStorageReadRecordTest, SunnyDay) {
+  int64_t pattern = 0;
   ClearStorageRecord();
 
   SbStorageRecord record = OpenStorageRecord();
-  WriteStorageRecord(record, kStorageSize);
+  WriteStorageRecord(record, kStorageSize, pattern);
   ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
-                      kStorageSize2);
+                      kStorageSize2, pattern);
+  pattern = 6;
+  // Write different data and check again.
+  WriteStorageRecord(record, kStorageSize, pattern);
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
   EXPECT_TRUE(SbStorageCloseRecord(record));
 
   // Now we'll open again and make sure it is the same.
   record = OpenStorageRecord();
   ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
-                      kStorageSize2);
+                      kStorageSize2, pattern);
   EXPECT_TRUE(SbStorageCloseRecord(record));
+  ClearStorageRecord();
 }
 
 TEST(SbStorageReadRecordTest, SunnyDaySmallBuffer) {
@@ -46,12 +54,118 @@
   EXPECT_TRUE(SbStorageCloseRecord(record));
 }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+TEST(SbStorageReadRecordTest, SunnyDayNamed) {
+  int64_t pattern = 0;
+  std::string name = ScopedRandomFile::MakeRandomFilename();
+  ClearStorageRecord(name.c_str());
+
+  SbStorageRecord record = OpenStorageRecord(name.c_str());
+  WriteStorageRecord(record, kStorageSize, pattern);
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
+  // Write different data and check again.
+  pattern = 6;
+  WriteStorageRecord(record, kStorageSize, pattern);
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
+  EXPECT_TRUE(SbStorageCloseRecord(record));
+
+  // Now we'll open again and make sure it is the same.
+  record = OpenStorageRecord(name.c_str());
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
+  EXPECT_TRUE(SbStorageCloseRecord(record));
+
+  ClearStorageRecord(name.c_str());
+}
+
+TEST(SbStorageReadRecordTest, SunnyDayNamed2) {
+  int64_t pattern1 = 2;
+  int64_t pattern2 = 8;
+  std::string name1 = ScopedRandomFile::MakeRandomFilename();
+  std::string name2 = ScopedRandomFile::MakeRandomFilename();
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+
+  SbStorageRecord record1 = OpenStorageRecord(name1.c_str());
+  SbStorageRecord record2 = OpenStorageRecord(name2.c_str());
+  WriteStorageRecord(record1, kStorageSize, pattern1);
+  WriteStorageRecord(record2, kStorageSize, pattern2);
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+
+  // Now we'll open again and make sure they are the same.
+  record1 = OpenStorageRecord(name1.c_str());
+  record2 = OpenStorageRecord(name2.c_str());
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+}
+
+TEST(SbStorageReadRecordTest, SunnyDayNamed3) {
+  int64_t pattern1 = 3;
+  int64_t pattern2 = 5;
+  int64_t pattern3 = 7;
+  std::string name1 = ScopedRandomFile::MakeRandomFilename();
+  std::string name2 = ScopedRandomFile::MakeRandomFilename();
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+  ClearStorageRecord();
+
+  SbStorageRecord record1 = OpenStorageRecord(name1.c_str());
+  SbStorageRecord record2 = OpenStorageRecord(name2.c_str());
+  SbStorageRecord record3 = OpenStorageRecord();
+  WriteStorageRecord(record1, kStorageSize, pattern1);
+  WriteStorageRecord(record2, kStorageSize, pattern2);
+  WriteStorageRecord(record3, kStorageSize, pattern3);
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  ReadAndCheckStorage(record3, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern3);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+  EXPECT_TRUE(SbStorageCloseRecord(record3));
+
+  // Now we'll open again and make sure they are the same.
+  record1 = OpenStorageRecord(name1.c_str());
+  record2 = OpenStorageRecord(name2.c_str());
+  record3 = OpenStorageRecord();
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  ReadAndCheckStorage(record3, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern3);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+  EXPECT_TRUE(SbStorageCloseRecord(record3));
+
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+  ClearStorageRecord();
+}
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 TEST(SbStorageReadRecordTest, SunnyDayNonexistant) {
   ClearStorageRecord();
 
   SbStorageRecord record = OpenStorageRecord();
   ReadAndCheckStorage(record, kStorageOffset, 0, 0, kStorageSize);
   EXPECT_TRUE(SbStorageCloseRecord(record));
+  ClearStorageRecord();
 }
 
 TEST(SbStorageReadRecordTest, RainyDayInvalidRecord) {
diff --git a/src/starboard/nplb/system_get_path_test.cc b/src/starboard/nplb/system_get_path_test.cc
index ea36035..9d2bb59 100644
--- a/src/starboard/nplb/system_get_path_test.cc
+++ b/src/starboard/nplb/system_get_path_test.cc
@@ -14,6 +14,9 @@
 
 #include <string.h>
 
+#include "starboard/memory.h"
+#include "starboard/nplb/file_helpers.h"
+#include "starboard/string.h"
 #include "starboard/system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -29,15 +32,15 @@
                bool expected_result,
                int line) {
 #define LOCAL_CONTEXT "Context : id=" << id << ", line=" << line;
-  char path[kPathSize] = {0};
-  memset(path, 0xCD, kPathSize);
+  char path[kPathSize];
+  SbMemorySet(path, 0xCD, kPathSize);
   bool result = SbSystemGetPath(id, path, kPathSize);
   if (expect_result) {
     EXPECT_EQ(expected_result, result) << LOCAL_CONTEXT;
   }
   if (result) {
     EXPECT_NE('\xCD', path[0]) << LOCAL_CONTEXT;
-    int len = static_cast<int>(strlen(path));
+    int len = static_cast<int>(SbStringGetLength(path));
     EXPECT_GT(len, 0) << LOCAL_CONTEXT;
   } else {
     EXPECT_EQ('\xCD', path[0]) << LOCAL_CONTEXT;
@@ -47,6 +50,7 @@
 
 TEST(SbSystemGetPathTest, ReturnsRequiredPaths) {
   BasicTest(kSbSystemPathContentDirectory, true, true, __LINE__);
+  BasicTest(kSbSystemPathCacheDirectory, true, true, __LINE__);
 }
 
 TEST(SbSystemGetPathTest, FailsGracefullyZeroBufferLength) {
@@ -75,12 +79,86 @@
   BasicTest(kSbSystemPathSourceDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathTempDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathTestOutputDirectory, false, false, __LINE__);
+  BasicTest(kSbSystemPathCacheDirectory, false, false, __LINE__);
 #if SB_API_VERSION >= 4
   BasicTest(kSbSystemPathFontDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathFontConfigurationDirectory, false, false, __LINE__);
 #endif  // SB_API_VERSION >= 4
 }
 
+TEST(SbSystemGetPathTest, CanCreateAndRemoveDirectoryInCache) {
+  char path[kPathSize];
+  SbMemorySet(path, 0xCD, kPathSize);
+  bool result = SbSystemGetPath(kSbSystemPathCacheDirectory, path, kPathSize);
+  EXPECT_TRUE(result);
+  if (result) {
+    EXPECT_NE('\xCD', path[0]);
+    // Delete a directory and confirm that it does not exist.
+    std::string sub_path =
+        SB_FILE_SEP_STRING + ScopedRandomFile::MakeRandomFilename();
+    EXPECT_GT(SbStringConcat(path, sub_path.c_str(), kPathSize), 0);
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+
+    // Create the directory and confirm it exists and can be opened.
+    EXPECT_TRUE(SbDirectoryCreate(path));
+    EXPECT_TRUE(SbFileExists(path));
+    EXPECT_TRUE(SbDirectoryCanOpen(path));
+    SbDirectory directory = SbDirectoryOpen(path, NULL);
+    EXPECT_TRUE(SbDirectoryIsValid(directory));
+
+    // Lastly, close and delete the directory.
+    EXPECT_TRUE(SbDirectoryClose(directory));
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+  }
+}
+TEST(SbSystemGetPathTest, CanWriteAndReadCache) {
+  char path[kPathSize];
+  SbMemorySet(path, 0xCD, kPathSize);
+  bool result = SbSystemGetPath(kSbSystemPathCacheDirectory, path, kPathSize);
+  EXPECT_TRUE(result);
+  if (result) {
+    EXPECT_NE('\xCD', path[0]);
+    int len = static_cast<int>(SbStringGetLength(path));
+    EXPECT_GT(len, 0);
+    // Delete a file and confirm that it does not exist.
+    std::string sub_path =
+        SB_FILE_SEP_STRING + ScopedRandomFile::MakeRandomFilename();
+    EXPECT_GT(SbStringConcat(path, sub_path.c_str(), kPathSize), 0);
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+
+    // Write to the file and check that we can read from it.
+    std::string content_to_write = "test content";
+    {
+      starboard::ScopedFile test_file_writer(
+          path, kSbFileCreateAlways | kSbFileWrite, NULL, NULL);
+      EXPECT_GT(
+          test_file_writer.WriteAll(content_to_write.c_str(),
+                                    static_cast<int>(content_to_write.size())),
+          0);
+    }
+    EXPECT_TRUE(SbFileExists(path));
+    SbFileInfo info;
+    EXPECT_TRUE(SbFileGetPathInfo(path, &info));
+    const int kFileSize = static_cast<int>(info.size);
+    EXPECT_GT(kFileSize, 0);
+    const int kBufferLength = 16 * 1024;
+    char content_read[kBufferLength] = {0};
+    {
+      starboard::ScopedFile test_file_reader(
+          path, kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+      EXPECT_GT(test_file_reader.ReadAll(content_read, kFileSize), 0);
+    }
+    EXPECT_EQ(content_read, content_to_write);
+
+    // Lastly, delete the file.
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+  }
+}
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index dc0b878..76ea308 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -396,6 +396,13 @@
 // The maximum number of users that can be signed in at the same time.
 #define SB_USER_MAX_SIGNED_IN 1
 
+// The Raspberry Pi does not apparently align fields in a heap-allocated struct
+// by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
+
+// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
+
 // --- Timing API ------------------------------------------------------------
 
 // Whether this platform has an API to retrieve how long the current thread
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index 1170664..030fec1 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -79,6 +79,7 @@
         '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
         '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
         '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc',
@@ -138,7 +139,6 @@
         '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
         '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
         '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
         '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
         '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
         '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
diff --git a/src/starboard/shared/alsa/alsa_audio_sink_type.cc b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
index 25688a5..e0cbd79 100644
--- a/src/starboard/shared/alsa/alsa_audio_sink_type.cc
+++ b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
@@ -99,6 +99,11 @@
   }
 #endif  // SB_API_VERSION >= 4
 
+  void SetVolume(double volume) SB_OVERRIDE {
+    ScopedLock lock(mutex_);
+    volume_ = volume;
+  }
+
   bool is_valid() { return playback_handle_ != NULL; }
 
  private:
@@ -124,6 +129,7 @@
   void* context_;
 
   double playback_rate_;
+  double volume_;
   std::vector<uint8_t> resample_buffer_;
 
   int channels_;
@@ -157,6 +163,7 @@
     void* context)
     : type_(type),
       playback_rate_(1.0),
+      volume_(1.0),
       resample_buffer_(channels * kFramesPerRequest *
                        GetSampleSize(sample_type)),
       channels_(channels),
@@ -272,6 +279,7 @@
 bool AlsaAudioSink::PlaybackLoop() {
   SB_DLOG(INFO) << "alsa::AlsaAudioSink enters playback loop";
 
+  // TODO: Also handle |volume_| here.
   double playback_rate = 1.0;
   for (;;) {
     int delayed_frame = AlsaGetBufferedFrames(playback_handle_);
diff --git a/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc b/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc
new file mode 100644
index 0000000..0fe2662
--- /dev/null
+++ b/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/memory.h"
+
+#include "starboard/shared/dlmalloc/page_internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns the number of bytes obtained from the system.  The total
+// number of bytes allocated by malloc, realloc etc., is less than this
+// value. Unlike mallinfo, this function returns only a precomputed
+// result, so can be called frequently to monitor memory consumption.
+// Even if locks are otherwise defined, this function does not use them,
+// so results might not be up to date.
+//
+// See http://gee.cs.oswego.edu/pub/misc/malloc.h for more details.
+size_t SB_ALLOCATOR(malloc_footprint)();
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+int64_t SbSystemGetUsedCPUMemory() {
+  return static_cast<int64_t>(SB_ALLOCATOR(malloc_footprint)());
+}
diff --git a/src/starboard/shared/linux/dev_input/dev_input.cc b/src/starboard/shared/linux/dev_input/dev_input.cc
index bc3202e..d0bd7fe 100644
--- a/src/starboard/shared/linux/dev_input/dev_input.cc
+++ b/src/starboard/shared/linux/dev_input/dev_input.cc
@@ -24,6 +24,8 @@
 #include <vector>
 
 #include <algorithm>
+#include <cmath>
+#include <map>
 #include <string>
 
 #include "starboard/configuration.h"
@@ -44,15 +46,44 @@
 using ::starboard::shared::starboard::Application;
 
 typedef int FileDescriptor;
-const FileDescriptor kInvalidFd = -1;
-const int kKeyboardDeviceId = 1;
+const FileDescriptor kInvalidFd = -ENODEV;
+
+enum InputDeviceIds {
+  kKeyboardDeviceId = 1,
+  kGamepadDeviceId,
+};
+
+enum TouchPadPositionState {
+  kTouchPadPositionNone = 0,
+  kTouchPadPositionX = 1,
+  kTouchPadPositionY = 2,
+  kTouchPadPositionAll = kTouchPadPositionX | kTouchPadPositionY
+};
+
+struct InputDeviceInfo {
+  InputDeviceInfo() : fd(-1), touchpad_position_state(kTouchPadPositionNone) {}
+
+  // File descriptor open for the device
+  FileDescriptor fd;
+  // Absolute Axis info.
+  std::map<int, struct input_absinfo> axis_info;
+  std::map<int, float> axis_value;
+  int touchpad_position_state;
+};
+
+bool IsTouchpadPositionKnown(InputDeviceInfo* device_info) {
+  return device_info->touchpad_position_state == kTouchPadPositionAll;
+}
 
 // Private implementation of DevInput.
 class DevInputImpl : public DevInput {
  public:
   explicit DevInputImpl(SbWindow window);
+  DevInputImpl(SbWindow window, FileDescriptor wake_up_fd);
   ~DevInputImpl() SB_OVERRIDE;
 
+  void InitDevInputImpl(SbWindow window);
+
   Event* PollNextSystemEvent() SB_OVERRIDE;
   Event* WaitForSystemEventWithTimeout(SbTime time) SB_OVERRIDE;
   void WakeSystemEventWait() SB_OVERRIDE;
@@ -61,13 +92,27 @@
   // Converts an input_event into a kSbEventInput Application::Event. The caller
   // is responsible for deleting the returned event.
   Event* InputToApplicationEvent(const struct input_event& event,
+                                 InputDeviceInfo* device_info,
                                  int modifiers);
 
+  // Converts an input_event containing a key input into a kSbEventInput
+  // Application::Event. The caller is responsible for deleting the returned
+  // event.
+  Event* KeyInputToApplicationEvent(const struct input_event& event,
+                                    int modifiers);
+
+  // Converts an input_event containing an axis event into a kSbEventInput
+  // Application::Event. The caller is responsible for deleting the returned
+  // event.
+  Event* AxisInputToApplicationEvent(const struct input_event& event,
+                                     InputDeviceInfo* device_info,
+                                     int modifiers);
+
   // The window to attribute /dev/input events to.
   SbWindow window_;
 
   // A set of read-only file descriptor of keyboard input devices.
-  std::vector<FileDescriptor> keyboard_fds_;
+  std::vector<InputDeviceInfo> input_devices_;
 
   // A file descriptor of the write end of a pipe that can be written to from
   // any thread to wake up this waiter in a thread-safe manner.
@@ -76,6 +121,8 @@
   // A file descriptor of the read end of a pipe that this waiter will wait on
   // to allow it to be signalled safely from other threads.
   FileDescriptor wakeup_read_fd_;
+
+  FileDescriptor wake_up_fd_;
 };
 
 // Helper class to manage a file descriptor set.
@@ -126,16 +173,12 @@
     case KEY_PAGEDOWN:
       return kSbKeyNext;
     case KEY_LEFT:
-    case BTN_DPAD_LEFT:
       return kSbKeyLeft;
     case KEY_RIGHT:
-    case BTN_DPAD_RIGHT:
       return kSbKeyRight;
     case KEY_DOWN:
-    case BTN_DPAD_DOWN:
       return kSbKeyDown;
     case KEY_UP:
-    case BTN_DPAD_UP:
       return kSbKeyUp;
     case KEY_ESC:
       return kSbKeyEscape;
@@ -374,6 +417,49 @@
       return kSbKeyBrightnessDown;
     case KEY_BRIGHTNESSUP:
       return kSbKeyBrightnessUp;
+
+    // Gamepad buttons.
+    //   https://www.kernel.org/doc/Documentation/input/gamepad.txt
+    case BTN_TL:
+      return kSbKeyGamepadLeftTrigger;
+    case BTN_TR:
+      return kSbKeyGamepadRightTrigger;
+    case BTN_DPAD_DOWN:
+      return kSbKeyGamepadDPadDown;
+    case BTN_DPAD_UP:
+      return kSbKeyGamepadDPadUp;
+    case BTN_DPAD_LEFT:
+      return kSbKeyGamepadDPadLeft;
+    case BTN_DPAD_RIGHT:
+      return kSbKeyGamepadDPadRight;
+    // The mapping for the buttons below can vary from controller to controller.
+    // TODO: Include button mapping for controllers with different layout.
+    case BTN_B:
+      return kSbKeyGamepad1;
+    case BTN_C:
+      return kSbKeyGamepad2;
+    case BTN_A:
+      return kSbKeyGamepad3;
+    case BTN_X:
+      return kSbKeyGamepad4;
+    case BTN_Y:
+      return kSbKeyGamepadLeftBumper;
+    case BTN_Z:
+      return kSbKeyGamepadRightBumper;
+    case BTN_TL2:
+      return kSbKeyGamepad5;
+    case BTN_TR2:
+      return kSbKeyGamepad6;
+    case BTN_SELECT:
+      return kSbKeyGamepadLeftStick;
+    case BTN_START:
+      return kSbKeyGamepadRightStick;
+    case BTN_MODE:
+      return kSbKeyGamepadSystem;
+    case BTN_THUMBL:
+      return kSbKeyGamepad1;
+    case BTN_THUMBR:
+      return kSbKeyGamepad1;
   }
   SB_DLOG(WARNING) << "Unknown code: 0x" << std::hex << code;
   return kSbKeyUnknown;
@@ -408,21 +494,129 @@
   return !!(bitset.at(bit / 8) & (1 << (bit % 8)));
 }
 
-// Searches for the keyboard /dev/input devices, opens them and returns the file
-// descriptors that report keyboard events.
-std::vector<FileDescriptor> GetKeyboardFds() {
-  const char kDevicePath[] = "/dev/input";
-  SbDirectory directory = SbDirectoryOpen(kDevicePath, NULL);
-  std::vector<FileDescriptor> fds;
-  if (!SbDirectoryIsValid(directory)) {
-    SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support, "
-                   << "unable to open: " << kDevicePath;
-    return fds;
+bool IsAxisFlat(float median, const struct input_absinfo& axis_info) {
+  SB_DCHECK((axis_info.flat * 2) <= (axis_info.maximum - axis_info.minimum));
+  return (axis_info.flat != 0) && (axis_info.value > median - axis_info.flat) &&
+         (axis_info.value < median + axis_info.flat);
+}
+
+float GetAxisValue(const struct input_absinfo& axis_info) {
+  float median =
+      static_cast<float>(axis_info.maximum + axis_info.minimum) / 2.0f;
+  if (IsAxisFlat(median, axis_info))
+    return 0;
+  float range = static_cast<float>(axis_info.maximum - axis_info.minimum);
+  float radius = range / 2.0f;
+  // Scale the axis value to [-1, 1].
+  float axis_value = (static_cast<float>(axis_info.value) - median) / radius;
+
+  if (axis_info.flat != 0) {
+    // Calculate the flat value scaled to [0, 1].
+    float flat = static_cast<float>(axis_info.flat) / range;
+
+    int sign = axis_value < 0.0f ? -1 : 1;
+    // Rescale the range:
+    // [-1.0f, -flat] to [-1.0f, 0.0f] and [flat, 1.0f] to [0.0f, 1.0f].
+    axis_value = (axis_value - sign * flat) / (1 - flat);
+  }
+  return axis_value;
+}
+
+void GetInputDeviceAbsoluteAxisInfo(int axis,
+                                    const std::vector<uint8_t>& bits,
+                                    InputDeviceInfo* info) {
+  if (IsBitSet(bits, axis)) {
+    struct input_absinfo axis_info;
+    int result = ioctl(info->fd, EVIOCGABS(axis), &axis_info);
+    if (result < 0) {
+      return;
+    }
+    info->axis_info.insert(std::make_pair(axis, axis_info));
+    info->axis_value.insert(std::make_pair(axis, GetAxisValue(axis_info)));
+  }
+}
+
+void GetInputDeviceInfo(InputDeviceInfo* info) {
+  std::vector<uint8_t> axis_bits(BytesNeededForBitSet(KEY_MAX));
+  int result =
+      ioctl(info->fd, EVIOCGBIT(EV_ABS, axis_bits.size()), axis_bits.data());
+  if (result < 0) {
+    return;
+  }
+
+  GetInputDeviceAbsoluteAxisInfo(ABS_X, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_Y, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_Z, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_RZ, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_RX, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_RY, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_HAT0X, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_HAT0Y, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_MT_POSITION_X, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_MT_POSITION_Y, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_MT_TRACKING_ID, axis_bits, info);
+  // TODO: Handle multi-touch using ABS_MT_SLOT.
+}
+
+FileDescriptor OpenDeviceIfKeyboardOrGamepad(const char* path) {
+  FileDescriptor fd = open(path, O_RDONLY | O_NONBLOCK);
+  if (fd < 0) {
+    // Open can fail if the application doesn't have permission to access
+    // the input device directly.
+    return kInvalidFd;
   }
 
   std::vector<uint8_t> ev_bits(BytesNeededForBitSet(EV_CNT));
   std::vector<uint8_t> key_bits(BytesNeededForBitSet(KEY_MAX));
 
+  int result = ioctl(fd, EVIOCGBIT(0, ev_bits.size()), ev_bits.data());
+  if (result < 0) {
+    close(fd);
+    return kInvalidFd;
+  }
+
+  bool has_ev_key = IsBitSet(ev_bits, EV_KEY);
+  if (!has_ev_key) {
+    close(fd);
+    return kInvalidFd;
+  }
+
+  result = ioctl(fd, EVIOCGBIT(EV_KEY, key_bits.size()), key_bits.data());
+  if (result < 0) {
+    close(fd);
+    return kInvalidFd;
+  }
+
+  bool has_key_space = IsBitSet(key_bits, KEY_SPACE);
+  bool has_gamepad_button = IsBitSet(key_bits, BTN_GAMEPAD);
+  if (!has_key_space && !has_gamepad_button) {
+    // If it doesn't have a space key or gamepad button, it may be a mouse.
+    close(fd);
+    return kInvalidFd;
+  }
+
+  result = ioctl(fd, EVIOCGRAB, 1);
+  if (result != 0) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": "
+                   << "Unable to get exclusive access to \"" << path << "\".";
+    close(fd);
+    return kInvalidFd;
+  }
+  return fd;
+}
+
+// Searches for the keyboard and game controller /dev/input devices, opens them
+// and returns the device info with a file descriptor and absolute axis details.
+std::vector<InputDeviceInfo> GetInputDevices() {
+  const char kDevicePath[] = "/dev/input";
+  SbDirectory directory = SbDirectoryOpen(kDevicePath, NULL);
+  std::vector<InputDeviceInfo> input_devices;
+  if (!SbDirectoryIsValid(directory)) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support, "
+                   << "unable to open: " << kDevicePath;
+    return input_devices;
+  }
+
   while (true) {
     SbDirectoryEntry entry;
     if (!SbDirectoryGetNext(directory, &entry)) {
@@ -438,60 +632,25 @@
       continue;
     }
 
-    FileDescriptor fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
-    if (fd < 0) {
-      SB_DLOG(ERROR) << __FUNCTION__ << ": Unable to open \"" << path << "\".";
+    FileDescriptor fd = OpenDeviceIfKeyboardOrGamepad(path.c_str());
+    if (fd == kInvalidFd) {
       continue;
     }
+    InputDeviceInfo info;
+    info.fd = fd;
+    GetInputDeviceInfo(&info);
 
-    int result = ioctl(fd, EVIOCGBIT(0, ev_bits.size()), ev_bits.data());
-
-    if (result < 0) {
-      close(fd);
-      continue;
-    }
-
-    bool has_ev_key = IsBitSet(ev_bits, EV_KEY);
-
-    if (!has_ev_key) {
-      close(fd);
-      continue;
-    }
-
-    result = ioctl(fd, EVIOCGBIT(EV_KEY, key_bits.size()), key_bits.data());
-
-    if (result < 0) {
-      close(fd);
-      continue;
-    }
-
-    bool has_key_space = IsBitSet(key_bits, KEY_SPACE);
-
-    if (!has_key_space) {
-      // If it doesn't have a space key, it may be a mouse
-      close(fd);
-      continue;
-    }
-
-    result = ioctl(fd, EVIOCGRAB, 1);
-    if (result != 0) {
-      SB_DLOG(ERROR) << __FUNCTION__ << ": "
-                     << "Unable to get exclusive access to \"" << path << "\".";
-      close(fd);
-      continue;
-    }
-
-    SB_DCHECK(fd != kInvalidFd);
-    fds.push_back(fd);
+    SB_DCHECK(info.fd != kInvalidFd);
+    input_devices.push_back(info);
   }
 
-  if (fds.empty()) {
+  if (input_devices.empty()) {
     SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support. "
-                   << "No keyboards available.";
+                   << "No keyboards or game controllers available.";
   }
 
   SbDirectoryClose(directory);
-  return fds;
+  return input_devices;
 }
 
 // Returns whether |key_code|'s bit is set in the bitmap |map|, assuming
@@ -513,14 +672,14 @@
   return 0;
 }
 
-// Polls the given keyboard file descriptor for an input_event. If there are no
+// Polls the given input file descriptor for an input_event. If there are no
 // bytes available, assumes that there is no input event to read. If it gets a
 // partial event, it will assume that it will be completed, and spins until it
 // receives an entire event.
-bool PollKeyboardEvent(FileDescriptor fd,
-                       struct input_event* out_event,
-                       int* out_modifiers) {
-  if (fd == kInvalidFd) {
+bool PollInputEvent(InputDeviceInfo* device_info,
+                    struct input_event* out_event,
+                    int* out_modifiers) {
+  if (device_info->fd == kInvalidFd) {
     return false;
   }
 
@@ -531,7 +690,7 @@
   size_t remaining = kEventSize;
   char* buffer = reinterpret_cast<char*>(out_event);
   while (remaining > 0) {
-    int bytes_read = read(fd, buffer, remaining);
+    int bytes_read = read(device_info->fd, buffer, remaining);
     if (bytes_read <= 0) {
       if (errno == EAGAIN || bytes_read == 0) {
         if (remaining == kEventSize) {
@@ -544,18 +703,18 @@
       }
 
       // Some unexpected type of read error occured.
-      SB_DLOG(ERROR) << __FUNCTION__ << ": Error reading keyboard: " << errno
+      SB_DLOG(ERROR) << __FUNCTION__ << ": Error reading input: " << errno
                      << " - " << strerror(errno);
       return false;
     }
 
-    SB_DCHECK(bytes_read <= remaining) << "bytes_read=" << bytes_read
-                                       << ", remaining=" << remaining;
+    SB_DCHECK(bytes_read <= remaining)
+        << "bytes_read=" << bytes_read << ", remaining=" << remaining;
     remaining -= bytes_read;
     buffer += bytes_read;
   }
 
-  if (out_event->type != EV_KEY) {
+  if ((out_event->type != EV_KEY) && (out_event->type != EV_ABS)) {
     return false;
   }
 
@@ -563,7 +722,7 @@
   int modifiers = 0;
   char map[(KEY_MAX / 8) + 1] = {0};
   errno = 0;
-  int result = ioctl(fd, EVIOCGKEY(sizeof(map)), map);
+  int result = ioctl(device_info->fd, EVIOCGKEY(sizeof(map)), map);
   if (result != -1) {
     modifiers |=
         GetModifier(KEY_LEFTSHIFT, KEY_RIGHTSHIFT, kSbKeyModifiersShift, map);
@@ -593,20 +752,35 @@
 
 // Also in starboard/shared/libevent/socket_waiter_internal.cc
 // TODO: Consider consolidating.
-int SetNonBlocking(int fd) {
+int SetNonBlocking(FileDescriptor fd) {
   int flags = fcntl(fd, F_GETFL, 0);
-  if (flags == -1)
+  if (flags == -1) {
     flags = 0;
+  }
   return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 }
 
 DevInputImpl::DevInputImpl(SbWindow window)
     : window_(window),
-      keyboard_fds_(GetKeyboardFds()),
+      input_devices_(GetInputDevices()),
       wakeup_write_fd_(kInvalidFd),
-      wakeup_read_fd_(kInvalidFd) {
+      wakeup_read_fd_(kInvalidFd),
+      wake_up_fd_(kInvalidFd) {
+  InitDevInputImpl(window);
+}
+
+DevInputImpl::DevInputImpl(SbWindow window, FileDescriptor wake_up_fd)
+    : window_(window),
+      input_devices_(GetInputDevices()),
+      wakeup_write_fd_(kInvalidFd),
+      wakeup_read_fd_(kInvalidFd),
+      wake_up_fd_(wake_up_fd) {
+  InitDevInputImpl(window);
+}
+
+void DevInputImpl::InitDevInputImpl(SbWindow window) {
   // Initialize wakeup pipe.
-  int fds[2] = {kInvalidFd, kInvalidFd};
+  FileDescriptor fds[2] = {kInvalidFd, kInvalidFd};
   int result = pipe(fds);
   SB_DCHECK(result == 0) << "result=" << result;
 
@@ -622,9 +796,8 @@
 }
 
 DevInputImpl::~DevInputImpl() {
-  for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin();
-       it != keyboard_fds_.end(); ++it) {
-    close(*it);
+  for (const auto& device : input_devices_) {
+    close(device.fd);
   }
   CloseFdSafely(&wakeup_write_fd_);
   CloseFdSafely(&wakeup_read_fd_);
@@ -633,13 +806,12 @@
 DevInput::Event* DevInputImpl::PollNextSystemEvent() {
   struct input_event event;
   int modifiers = 0;
-  for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin();
-       it != keyboard_fds_.end(); ++it) {
-    if (!PollKeyboardEvent(*it, &event, &modifiers)) {
+  for (auto& device : input_devices_) {
+    if (!PollInputEvent(&device, &event, &modifiers)) {
       continue;
     }
 
-    return InputToApplicationEvent(event, modifiers);
+    return InputToApplicationEvent(event, &device, modifiers);
   }
   return NULL;
 }
@@ -651,9 +823,13 @@
   }
 
   FdSet read_set;
-  for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin();
-       it != keyboard_fds_.end(); ++it) {
-    read_set.Set(*it);
+  if (wake_up_fd_ != kInvalidFd) {
+    read_set.Set(wake_up_fd_);
+  }
+
+  for (std::vector<InputDeviceInfo>::const_iterator it = input_devices_.begin();
+       it != input_devices_.end(); ++it) {
+    read_set.Set(it->fd);
   }
   read_set.Set(wakeup_read_fd_);
 
@@ -693,13 +869,256 @@
   }
 }
 
-DevInput::Event* DevInputImpl::InputToApplicationEvent(
-    const struct input_event& event,
-    int modifiers) {
-  if (event.type != EV_KEY) {
+namespace {
+
+// Creates a key event for an analog button input.
+DevInput::Event* CreateAnalogButtonKeyEvent(SbWindow window,
+                                            float axis_value,
+                                            float previous_axis_value,
+                                            SbKey key,
+                                            SbKeyLocation location,
+                                            int modifiers,
+                                            const struct input_event& event) {
+  SbInputEventType previous_type =
+      (std::abs(previous_axis_value) > 0.5 ? kSbInputEventTypePress
+                                           : kSbInputEventTypeUnpress);
+  SbInputEventType type =
+      (std::abs(axis_value) > 0.5 ? kSbInputEventTypePress
+                                  : kSbInputEventTypeUnpress);
+  if (previous_type == type) {
+    // Key press/unpress state did not change.
     return NULL;
   }
 
+  SbInputData* data = new SbInputData();
+  SbMemorySet(data, 0, sizeof(*data));
+  data->window = window;
+  data->type = type;
+  data->device_type = kSbInputDeviceTypeGamepad;
+  data->device_id = kGamepadDeviceId;
+  data->key = key;
+  data->key_location = location;
+  data->key_modifiers = modifiers;
+  return new DevInput::Event(kSbEventTypeInput, data,
+                             &Application::DeleteDestructor<SbInputData>);
+}
+
+// Creates a move event with key for a stick input.
+DevInput::Event* CreateMoveEventWithKey(SbWindow window,
+                                        SbKey key,
+                                        SbKeyLocation location,
+                                        int modifiers,
+                                        const SbInputVector& input_vector) {
+  SbInputData* data = new SbInputData();
+  SbMemorySet(data, 0, sizeof(*data));
+
+  data->window = window;
+  data->type = kSbInputEventTypeMove;
+  data->device_type = kSbInputDeviceTypeGamepad;
+  data->device_id = kGamepadDeviceId;
+
+  data->key = key;
+  data->key_location = location;
+  data->key_modifiers = modifiers;
+  data->position = input_vector;
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+  data->pressure = NAN;
+  data->size = {NAN, NAN};
+  data->tilt = {NAN, NAN};
+#endif
+
+  return new DevInput::Event(kSbEventTypeInput, data,
+                             &Application::DeleteDestructor<SbInputData>);
+}
+
+DevInput::Event* CreateTouchPadEvent(SbWindow window,
+                                     SbInputEventType type,
+                                     SbKey key,
+                                     SbKeyLocation location,
+                                     int modifiers,
+                                     const SbInputVector& input_vector) {
+  SbInputData* data = new SbInputData();
+  SbMemorySet(data, 0, sizeof(*data));
+
+  data->window = window;
+  data->type = type;
+  data->device_type = kSbInputDeviceTypeTouchPad;
+  data->device_id = kGamepadDeviceId;
+
+  data->key = key;
+  data->key_location = location;
+  data->key_modifiers = modifiers;
+  data->position = input_vector;
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+  data->pressure = NAN;
+  data->size = {NAN, NAN};
+  data->tilt = {NAN, NAN};
+#endif
+
+  return new DevInput::Event(kSbEventTypeInput, data,
+                             &Application::DeleteDestructor<SbInputData>);
+}
+
+}  // namespace
+
+DevInput::Event* DevInputImpl::AxisInputToApplicationEvent(
+    const struct input_event& event,
+    InputDeviceInfo* device_info,
+    int modifiers) {
+  SB_DCHECK(event.type == EV_ABS);
+  SbKey key = kSbKeyUnknown;
+  float axis_value = 0;
+  float previous_axis_value = 0;
+  auto axis_info_it = device_info->axis_info.find(event.code);
+  if (axis_info_it != device_info->axis_info.end()) {
+    struct input_absinfo& axis_info = axis_info_it->second;
+    axis_info.value = event.value;
+    axis_value = GetAxisValue(axis_info);
+    float& stored_axis_value = device_info->axis_value[event.code];
+    previous_axis_value = stored_axis_value;
+    if (previous_axis_value == axis_value) {
+      // If the value is unchanged, don't do anything.
+      return NULL;
+    }
+    stored_axis_value = axis_value;
+  }
+
+  SbKeyLocation location = kSbKeyLocationUnspecified;
+  SbInputVector input_vector;
+  // The mapping for the axis codes can vary from controller to controller.
+  // TODO: Include axis mapping for controllers with different layout.
+  switch (event.code) {
+    case ABS_X:
+      // Report up and left as positive values.
+      input_vector.x = -axis_value;
+      input_vector.y = -device_info->axis_value[ABS_Y];
+      key = kSbKeyGamepadLeftStickLeft;
+      location = kSbKeyLocationLeft;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    case ABS_Y: {
+      // Report up and left as positive values.
+      input_vector.x = -device_info->axis_value[ABS_X];
+      input_vector.y = -axis_value;
+      key = kSbKeyGamepadLeftStickUp;
+      location = kSbKeyLocationLeft;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    }
+    case ABS_Z:
+      // Report up and left as positive values.
+      input_vector.x = -axis_value;
+      input_vector.y = -device_info->axis_value[ABS_RZ];
+      key = kSbKeyGamepadRightStickLeft;
+      location = kSbKeyLocationRight;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    case ABS_RZ:
+      // Report up and left as positive values.
+      input_vector.x = -device_info->axis_value[ABS_Z];
+      input_vector.y = -axis_value;
+      key = kSbKeyGamepadRightStickUp;
+      location = kSbKeyLocationRight;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    case ABS_RX: {
+      key = kSbKeyGamepadLeftTrigger;
+      location = kSbKeyLocationLeft;
+      // For trigger buttons, the range is [0..1].
+      float trigger_value = (axis_value + 1) / 2;
+      float previous_trigger_value = (previous_axis_value + 1) / 2;
+      return CreateAnalogButtonKeyEvent(window_, trigger_value,
+                                        previous_trigger_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_RY: {
+      key = kSbKeyGamepadRightTrigger;
+      location = kSbKeyLocationRight;
+      // For trigger buttons, the range is [0..1].
+      float trigger_value = (axis_value + 1) / 2;
+      float previous_trigger_value = (previous_axis_value + 1) / 2;
+      return CreateAnalogButtonKeyEvent(window_, trigger_value,
+                                        previous_trigger_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_HAT0X: {
+      float axis_value_for_key =
+          std::abs(axis_value) > 0.5f ? axis_value : previous_axis_value;
+      key = (axis_value_for_key < 0) ? kSbKeyGamepadDPadLeft
+                                     : kSbKeyGamepadDPadRight;
+      return CreateAnalogButtonKeyEvent(window_, axis_value,
+                                        previous_axis_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_HAT0Y: {
+      float axis_value_for_key =
+          std::abs(axis_value) > 0.5f ? axis_value : previous_axis_value;
+      key = (axis_value_for_key < 0) ? kSbKeyGamepadDPadUp
+                                     : kSbKeyGamepadDPadDown;
+      return CreateAnalogButtonKeyEvent(window_, axis_value,
+                                        previous_axis_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_MT_TRACKING_ID:
+      if (event.value == -1) {
+        bool touchpad_position_is_known = IsTouchpadPositionKnown(device_info);
+        device_info->touchpad_position_state = kTouchPadPositionNone;
+        if (touchpad_position_is_known) {
+          // Touch point is released, report last known position as unpress.
+          input_vector.x = (device_info->axis_value[ABS_MT_POSITION_X] + 1 / 2);
+          input_vector.y = (device_info->axis_value[ABS_MT_POSITION_Y] + 1 / 2);
+          return CreateTouchPadEvent(window_, kSbInputEventTypeUnpress, key,
+                                     location, modifiers, input_vector);
+        }
+      }
+      return NULL;
+    case ABS_MT_POSITION_X: {
+      // If all positions were known before this event, then this event is a
+      // move.
+      SbInputEventType type = IsTouchpadPositionKnown(device_info)
+                                  ? kSbInputEventTypeMove
+                                  : kSbInputEventTypePress;
+      device_info->touchpad_position_state |= kTouchPadPositionX;
+      if (IsTouchpadPositionKnown(device_info)) {
+        // For touchpads, the range is [0..1].
+        input_vector.x = (axis_value + 1) / 2;
+        input_vector.y = (device_info->axis_value[ABS_MT_POSITION_Y] + 1) / 2;
+        return CreateTouchPadEvent(window_, type, key, location, modifiers,
+                                   input_vector);
+      }
+      // Not all axis positions are known yet.
+      return NULL;
+    }
+    case ABS_MT_POSITION_Y: {
+      // If all positions were known before this event, then this event is a
+      // move.
+      SbInputEventType type = IsTouchpadPositionKnown(device_info)
+                                  ? kSbInputEventTypeMove
+                                  : kSbInputEventTypePress;
+      device_info->touchpad_position_state |= kTouchPadPositionY;
+      if (IsTouchpadPositionKnown(device_info)) {
+        // For touchpads, the range is [0..1].
+        input_vector.x = (device_info->axis_value[ABS_MT_POSITION_X] + 1) / 2;
+        input_vector.y = (axis_value + 1) / 2;
+        return CreateTouchPadEvent(window_, type, key, location, modifiers,
+                                   input_vector);
+      }
+      // Not all axis positions are known yet.
+      return NULL;
+    }
+    default:
+      // Ignored event codes.
+      return NULL;
+  }
+
+  SB_NOTREACHED();
+  return NULL;
+}
+
+DevInput::Event* DevInputImpl::KeyInputToApplicationEvent(
+    const struct input_event& event,
+    int modifiers) {
+  SB_DCHECK(event.type == EV_KEY);
   SB_DCHECK(event.value <= 2);
   SbInputData* data = new SbInputData();
   SbMemorySet(data, 0, sizeof(*data));
@@ -715,6 +1134,21 @@
                    &Application::DeleteDestructor<SbInputData>);
 }
 
+DevInput::Event* DevInputImpl::InputToApplicationEvent(
+    const struct input_event& event,
+    InputDeviceInfo* device_info,
+    int modifiers) {
+  // EV_ABS events are axis values: Sticks, dpad, and touchpad.
+  // https://www.kernel.org/doc/Documentation/input/event-codes.txt
+  switch (event.type) {
+    case EV_ABS:
+      return AxisInputToApplicationEvent(event, device_info, modifiers);
+    case EV_KEY:
+      return KeyInputToApplicationEvent(event, modifiers);
+  }
+  return NULL;
+}
+
 }  // namespace
 
 // static
@@ -722,6 +1156,11 @@
   return new DevInputImpl(window);
 }
 
+// static
+DevInput* DevInput::Create(SbWindow window, int wake_up_fd) {
+  return new DevInputImpl(window, wake_up_fd);
+}
+
 }  // namespace dev_input
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/linux/dev_input/dev_input.h b/src/starboard/shared/linux/dev_input/dev_input.h
index aa8577a..11b94c4 100644
--- a/src/starboard/shared/linux/dev_input/dev_input.h
+++ b/src/starboard/shared/linux/dev_input/dev_input.h
@@ -48,6 +48,11 @@
   // Creates an instance of DevInput for the given window.
   static DevInput* Create(SbWindow window);
 
+  // Creates an instance of DevInput for the given window.
+  // The wake_up_fd will be used in WaitForSystemEventWithTimeout() to return
+  // early when input is available on it.
+  static DevInput* Create(SbWindow window, int wake_up_fd);
+
  protected:
   DevInput() {}
 };
diff --git a/src/starboard/shared/msvc/uwp/toolchain.py b/src/starboard/shared/msvc/uwp/msvc_toolchain.py
similarity index 100%
rename from src/starboard/shared/msvc/uwp/toolchain.py
rename to src/starboard/shared/msvc/uwp/msvc_toolchain.py
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
index cf40ad5..4aaeef1 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
@@ -42,6 +42,9 @@
 #if SB_API_VERSION >= 4
   virtual void SetPlaybackRate(double playback_rate) = 0;
 #endif  // SB_API_VERSION >= 4
+
+  virtual void SetVolume(double volume) = 0;
+
   virtual bool IsType(Type* type) = 0;
 
   // The following two functions will be called during application startup and
diff --git a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
index 597820a..f6ccd09 100644
--- a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
+++ b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
@@ -44,6 +44,10 @@
   }
 #endif  // SB_API_VERSION >= 4
 
+  void SetVolume(double volume) SB_OVERRIDE {
+    SB_UNREFERENCED_PARAMETER(volume);
+  }
+
  private:
   static void* ThreadEntryPoint(void* context);
   void AudioThreadFunc();
diff --git a/src/starboard/shared/starboard/file_storage/storage_delete_record.cc b/src/starboard/shared/starboard/file_storage/storage_delete_record.cc
index 891fbbb..c5c7fe3 100644
--- a/src/starboard/shared/starboard/file_storage/storage_delete_record.cc
+++ b/src/starboard/shared/starboard/file_storage/storage_delete_record.cc
@@ -18,14 +18,23 @@
 #include "starboard/shared/starboard/file_storage/storage_internal.h"
 #include "starboard/user.h"
 
-bool SbStorageDeleteRecord(SbUser user) {
+bool SbStorageDeleteRecord(SbUser user
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ,
+                           const char* name
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ) {
   if (!SbUserIsValid(user)) {
     return false;
   }
 
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+  const char* name = NULL;
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
   char path[SB_FILE_MAX_PATH];
   bool success = starboard::shared::starboard::GetUserStorageFilePath(
-      user, path, SB_ARRAY_SIZE_INT(path));
+      user, name, path, SB_ARRAY_SIZE_INT(path));
   if (!success) {
     return false;
   }
diff --git a/src/starboard/shared/starboard/file_storage/storage_internal.h b/src/starboard/shared/starboard/file_storage/storage_internal.h
index 6bb3362..a56cd34 100644
--- a/src/starboard/shared/starboard/file_storage/storage_internal.h
+++ b/src/starboard/shared/starboard/file_storage/storage_internal.h
@@ -18,6 +18,8 @@
 #ifndef STARBOARD_SHARED_STARBOARD_FILE_STORAGE_STORAGE_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_FILE_STORAGE_STORAGE_INTERNAL_H_
 
+#include <string>
+
 #include "starboard/file.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/storage.h"
@@ -27,6 +29,9 @@
 struct SbStorageRecordPrivate {
   SbUser user;
   SbFile file;
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  std::string name;
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
 };
 
 namespace starboard {
@@ -34,6 +39,7 @@
 namespace starboard {
 // Gets the path to the storage file for the given user.
 static SB_C_INLINE bool GetUserStorageFilePath(SbUser user,
+                                               const char* name,
                                                char* out_path,
                                                int path_size) {
   bool success = SbUserGetProperty(user, kSbUserPropertyHomeDirectory, out_path,
@@ -42,7 +48,12 @@
     return false;
   }
 
-  SbStringConcat(out_path, "/.starboard.storage", path_size);
+  SbStringConcat(out_path, "/.starboard", path_size);
+  if (name && SbStringGetLength(name) > 0) {
+    SbStringConcat(out_path, ".", path_size);
+    SbStringConcat(out_path, name, path_size);
+  }
+  SbStringConcat(out_path, ".storage", path_size);
   return true;
 }
 }  // namespace starboard
diff --git a/src/starboard/shared/starboard/file_storage/storage_open_record.cc b/src/starboard/shared/starboard/file_storage/storage_open_record.cc
index 5be40f4..aaea393 100644
--- a/src/starboard/shared/starboard/file_storage/storage_open_record.cc
+++ b/src/starboard/shared/starboard/file_storage/storage_open_record.cc
@@ -19,14 +19,23 @@
 #include "starboard/shared/starboard/file_storage/storage_internal.h"
 #include "starboard/user.h"
 
-SbStorageRecord SbStorageOpenRecord(SbUser user) {
+SbStorageRecord SbStorageOpenRecord(SbUser user
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ,
+                                    const char* name
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ) {
   if (!SbUserIsValid(user)) {
     return kSbStorageInvalidRecord;
   }
 
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+  const char* name = NULL;
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
   char path[SB_FILE_MAX_PATH];
   bool success = starboard::shared::starboard::GetUserStorageFilePath(
-      user, path, SB_ARRAY_SIZE_INT(path));
+      user, name, path, SB_ARRAY_SIZE_INT(path));
   if (!success) {
     return kSbStorageInvalidRecord;
   }
@@ -43,5 +52,10 @@
   SB_DCHECK(SbStorageIsValidRecord(result));
   result->user = user;
   result->file = file;
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  if (name) {
+    result->name = name;
+  }
+#endif
   return result;
 }
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index dbaa49e..9e63967 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -71,6 +71,7 @@
       sink_sample_type_(GetSinkAudioSampleType()),
       bytes_per_frame_(media::GetBytesPerSample(sink_sample_type_) * channels_),
       playback_rate_(1.0),
+      volume_(1.0),
       paused_(true),
       seeking_(false),
       seeking_to_pts_(0),
@@ -178,6 +179,14 @@
 }
 #endif  // SB_API_VERSION >= 4
 
+void AudioRendererImpl::SetVolume(double volume) {
+  SB_DCHECK(BelongsToCurrentThread());
+  volume_ = volume;
+  if (audio_sink_) {
+    audio_sink_->SetVolume(volume_);
+  }
+}
+
 void AudioRendererImpl::Seek(SbMediaTime seek_to_pts) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(seek_to_pts >= 0);
@@ -287,6 +296,7 @@
   // support play/pause.
   audio_sink_->SetPlaybackRate(playback_rate_ > 0.0 ? 1.0 : 0.0);
 #endif  // SB_API_VERSION >= 4
+  audio_sink_->SetVolume(volume_);
 }
 
 void AudioRendererImpl::UpdateSourceStatus(int* frames_in_buffer,
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
index d9278da..a469037 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
@@ -60,6 +60,7 @@
 #if SB_API_VERSION >= 4
   void SetPlaybackRate(double playback_rate) SB_OVERRIDE;
 #endif  // SB_API_VERSION >= 4
+  void SetVolume(double volume) SB_OVERRIDE;
   void Seek(SbMediaTime seek_to_pts) SB_OVERRIDE;
 
   bool IsEndOfStreamWritten() const SB_OVERRIDE {
@@ -117,6 +118,7 @@
   scoped_ptr<AudioResampler> resampler_;
   AudioTimeStretcher time_stretcher_;
   double playback_rate_;
+  double volume_;
 
   atomic_bool paused_;
   atomic_bool seeking_;
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index 1c192c1..a6ca64b 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -38,6 +38,7 @@
 #if SB_API_VERSION >= 4
   virtual void SetPlaybackRate(double playback_rate) = 0;
 #endif  // SB_API_VERSION >= 4
+  virtual void SetVolume(double volume) = 0;
   virtual void Seek(SbMediaTime seek_to_pts) = 0;
   virtual bool IsEndOfStreamWritten() const = 0;
   virtual bool IsEndOfStreamPlayed() const = 0;
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 76b6e53..c373d90 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -62,7 +62,11 @@
       audio_codec_(audio_codec),
       drm_system_(drm_system),
       audio_header_(audio_header),
-      paused_(false)
+      paused_(false),
+#if SB_API_VERSION >= 4
+      playback_rate_(1.0),
+#endif  // SB_API_VERSION >= 4
+      volume_(1.0)
 #if SB_API_VERSION >= 4
       ,
       output_mode_(output_mode),
@@ -87,12 +91,12 @@
   bounds_ = PlayerWorker::Bounds();
 }
 
-bool FilterBasedPlayerWorkerHandler::IsPunchoutMode() const {

-#if SB_API_VERSION >= 4

-  return (output_mode_ == kSbPlayerOutputModePunchOut);

-#else

-  return true;

-#endif  // SB_API_VERSION >= 4

+bool FilterBasedPlayerWorkerHandler::IsPunchoutMode() const {
+#if SB_API_VERSION >= 4
+  return (output_mode_ == kSbPlayerOutputModePunchOut);
+#else
+  return true;
+#endif  // SB_API_VERSION >= 4
 }
 
 bool FilterBasedPlayerWorkerHandler::Init(
@@ -144,6 +148,11 @@
   ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
   media_components->GetRenderers(&audio_renderer_, &video_renderer_);
 
+#if SB_API_VERSION >= 4
+  audio_renderer_->SetPlaybackRate(playback_rate_);
+#endif  // SB_API_VERSION >= 4
+  audio_renderer_->SetVolume(volume_);
+
   job_queue_->Schedule(update_closure_, kUpdateInterval);
 
   return true;
@@ -276,15 +285,26 @@
 bool FilterBasedPlayerWorkerHandler::SetPlaybackRate(double playback_rate) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
+  playback_rate_ = playback_rate;
+
   if (!audio_renderer_) {
     return false;
   }
 
-  audio_renderer_->SetPlaybackRate(playback_rate);
+  audio_renderer_->SetPlaybackRate(playback_rate_);
   return true;
 }
 #endif  // SB_API_VERSION >= 4
 
+void FilterBasedPlayerWorkerHandler::SetVolume(double volume) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  volume_ = volume;
+  if (audio_renderer_) {
+    audio_renderer_->SetVolume(volume_);
+  }
+}
+
 bool FilterBasedPlayerWorkerHandler::SetBounds(
     const PlayerWorker::Bounds& bounds) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
@@ -328,9 +348,9 @@
     player_worker_->UpdateDroppedVideoFrames(
         video_renderer_->GetDroppedFrames());
 
-   if (IsPunchoutMode()) {

-      shared::starboard::Application::Get()->HandleFrame(

-          player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);

+    if (IsPunchoutMode()) {
+      shared::starboard::Application::Get()->HandleFrame(
+          player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
     }
 
     (*player_worker_.*update_media_time_cb_)(audio_renderer_->GetCurrentTime());
@@ -355,10 +375,10 @@
   }
   video_renderer.reset();
 
-  if (IsPunchoutMode()) {

-    // Clear the video frame as we terminate.

-    shared::starboard::Application::Get()->HandleFrame(

-        player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0);

+  if (IsPunchoutMode()) {
+    // Clear the video frame as we terminate.
+    shared::starboard::Application::Get()->HandleFrame(
+        player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0);
   }
 }
 
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index 8715ab9..20655ca 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -68,6 +68,7 @@
 #if SB_API_VERSION >= 4
   bool SetPlaybackRate(double playback_rate) SB_OVERRIDE;
 #endif  // SB_API_VERSION >= 4
+  void SetVolume(double volume) SB_OVERRIDE;
   bool SetBounds(const PlayerWorker::Bounds& bounds) SB_OVERRIDE;
   void Stop() SB_OVERRIDE;
 
@@ -98,6 +99,10 @@
   scoped_ptr<VideoRenderer> video_renderer_;
 
   bool paused_;
+#if SB_API_VERSION >= 4
+  double playback_rate_;
+#endif  // SB_API_VERSION >= 4
+  double volume_;
   PlayerWorker::Bounds bounds_;
   Closure update_closure_;
 
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index 20268ea..3b46a5d 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -133,8 +133,8 @@
 #endif  // SB_API_VERSION >= 4
 
 void SbPlayerPrivate::SetVolume(double volume) {
-  SB_UNREFERENCED_PARAMETER(volume);
-  SB_NOTIMPLEMENTED();
+  volume_ = volume;
+  worker_->SetVolume(volume_);
 }
 
 void SbPlayerPrivate::UpdateMediaTime(SbMediaTime media_time, int ticket) {
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index 38a0a25..24e834c 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -270,6 +270,11 @@
 }
 #endif  // SB_API_VERSION >= 4
 
+void PlayerWorker::DoSetVolume(double volume) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+  handler_->SetVolume(volume);
+}
+
 void PlayerWorker::DoStop() {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index a1d9333..84be839 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -83,6 +83,8 @@
 #if SB_API_VERSION >= 4
     virtual bool SetPlaybackRate(double playback_rate) = 0;
 #endif  // SB_API_VERSION >= 4
+    virtual void SetVolume(double volume) = 0;
+
     virtual bool SetBounds(const Bounds& bounds) = 0;
 
     // Once this function returns, all processing on the Handler and related
@@ -147,6 +149,10 @@
   }
 #endif  // SB_API_VERSION >= 4
 
+  void SetVolume(double volume) {
+    job_queue_->Schedule(Bind(&PlayerWorker::DoSetVolume, this, volume));
+  }
+
   void UpdateDroppedVideoFrames(int dropped_video_frames) {
     host_->UpdateDroppedVideoFrames(dropped_video_frames);
   }
@@ -177,6 +183,7 @@
 #if SB_API_VERSION >= 4
   void DoSetPlaybackRate(double rate);
 #endif  // SB_API_VERSION >= 4
+  void DoSetVolume(double volume);
   void DoStop();
 
   void UpdateDecoderState(SbMediaType type, SbPlayerDecoderState state);
diff --git a/src/starboard/shared/stub/drm_create_system.cc b/src/starboard/shared/stub/drm_create_system.cc
index a9a1c84..64139a9 100644
--- a/src/starboard/shared/stub/drm_create_system.cc
+++ b/src/starboard/shared/stub/drm_create_system.cc
@@ -14,6 +14,24 @@
 
 #include "starboard/drm.h"
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
+SbDrmSystem SbDrmCreateSystem(
+    const char* key_system,
+    void* context,
+    SbDrmSessionUpdateRequestFunc update_request_callback,
+    SbDrmSessionUpdatedFunc session_updated_callback,
+    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback) {
+  SB_UNREFERENCED_PARAMETER(context);
+  SB_UNREFERENCED_PARAMETER(key_system);
+  SB_UNREFERENCED_PARAMETER(update_request_callback);
+  SB_UNREFERENCED_PARAMETER(session_updated_callback);
+  SB_UNREFERENCED_PARAMETER(key_statuses_changed_callback);
+  return kSbDrmSystemInvalid;
+}
+
+#else  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 SbDrmSystem SbDrmCreateSystem(
     const char* key_system,
     void* context,
@@ -25,3 +43,5 @@
   SB_UNREFERENCED_PARAMETER(session_updated_callback);
   return kSbDrmSystemInvalid;
 }
+
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
diff --git a/src/starboard/shared/stub/storage_delete_record.cc b/src/starboard/shared/stub/storage_delete_record.cc
index ba13cd1..a669054 100644
--- a/src/starboard/shared/stub/storage_delete_record.cc
+++ b/src/starboard/shared/stub/storage_delete_record.cc
@@ -14,6 +14,11 @@
 
 #include "starboard/storage.h"
 
-bool SbStorageDeleteRecord(SbUser /*user*/) {
+bool SbStorageDeleteRecord(SbUser /*user*/
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ,
+                           const char* /*name*/
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ) {
   return false;
 }
diff --git a/src/starboard/shared/stub/storage_open_record.cc b/src/starboard/shared/stub/storage_open_record.cc
index bcd5f3d..b0f503e 100644
--- a/src/starboard/shared/stub/storage_open_record.cc
+++ b/src/starboard/shared/stub/storage_open_record.cc
@@ -14,6 +14,11 @@
 
 #include "starboard/storage.h"
 
-SbStorageRecord SbStorageOpenRecord(SbUser /*user*/) {
+SbStorageRecord SbStorageOpenRecord(SbUser /*user*/
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ,
+                                    const char* /*name*/
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ) {
   return kSbStorageInvalidRecord;
 }
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/test_webapi_extension/my_new_enum.idl
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/test_webapi_extension/my_new_enum.idl
index be4e805..1a6f73c 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/test_webapi_extension/my_new_enum.idl
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+enum MyNewEnum {
+  "apples",
+  "oranges",
+  "peaches"
+};
diff --git a/src/starboard/shared/test_webapi_extension/my_new_interface.cc b/src/starboard/shared/test_webapi_extension/my_new_interface.cc
new file mode 100644
index 0000000..18af5b4
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/my_new_interface.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/test_webapi_extension/my_new_interface.h"
+
+namespace cobalt {
+namespace webapi_extension {
+
+MyNewInterface::MyNewInterface(const scoped_refptr<dom::Window>& window) {
+  UNREFERENCED_PARAMETER(window);
+  // Provide an initial value for the enum.
+  enum_value_ = kMyNewEnumApples;
+}
+
+MyNewInterface::~MyNewInterface() OVERRIDE {}
+
+}  // namespace webapi_extension
+}  // namespace cobalt
diff --git a/src/starboard/shared/test_webapi_extension/my_new_interface.h b/src/starboard/shared/test_webapi_extension/my_new_interface.h
new file mode 100644
index 0000000..eb30d4e
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/my_new_interface.h
@@ -0,0 +1,55 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
+#define STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
+
+#include <string>
+
+#include "cobalt/dom/window.h"
+#include "cobalt/script/wrappable.h"
+#include "starboard/shared/test_webapi_extension/my_new_enum.h"
+
+namespace cobalt {
+namespace webapi_extension {
+
+class MyNewInterface : public script::Wrappable {
+ public:
+  explicit MyNewInterface(const scoped_refptr<dom::Window>& window);
+
+  const std::string& foo() const { return foo_; }
+  void set_foo(const std::string& value) { foo_ = value; }
+
+  void SetMyNewEnum(MyNewEnum value) { enum_value_ = value; }
+  MyNewEnum GetMyNewEnum() const { return enum_value_; }
+
+  // All types derived from script::Wrappable must have this annotation.
+  DEFINE_WRAPPABLE_TYPE(MyNewInterface);
+
+ private:
+  // Since script::Wrappable inherits from base::RefCounted<>, we make the
+  // destructor private.
+  ~MyNewInterface() OVERRIDE;
+
+  std::string foo_;
+
+  MyNewEnum enum_value_;
+
+  DISALLOW_COPY_AND_ASSIGN(MyNewInterface);
+};
+
+}  // namespace webapi_extension
+}  // namespace cobalt
+
+#endif  // STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/test_webapi_extension/my_new_interface.idl
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/test_webapi_extension/my_new_interface.idl
index be4e805..3e64764 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/test_webapi_extension/my_new_interface.idl
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+interface MyNewInterface {
+  attribute DOMString foo;
 
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+  void SetMyNewEnum(MyNewEnum value);
+  MyNewEnum GetMyNewEnum();
+};
diff --git a/src/starboard/shared/test_webapi_extension/test_webapi_extension.gypi b/src/starboard/shared/test_webapi_extension/test_webapi_extension.gypi
new file mode 100644
index 0000000..74de31a
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/test_webapi_extension.gypi
@@ -0,0 +1,29 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file can be included from a platform's gyp_configuration.gypi file to
+# setup the test custom webapi_extension defined in this directory to be
+# injected into that platform.
+{
+  'variables': {
+    'cobalt_webapi_extension_source_idl_files': [
+      'my_new_interface.idl'
+    ],
+    'cobalt_webapi_extension_generated_header_idl_files': [
+      'my_new_enum.idl'
+    ],
+    'cobalt_webapi_extension_gyp_target':
+      '<(DEPTH)/starboard/shared/test_webapi_extension/webapi_extension.gyp:cobalt_test_webapi_extension',
+  },
+}
\ No newline at end of file
diff --git a/src/starboard/shared/test_webapi_extension/webapi_extension.cc b/src/starboard/shared/test_webapi_extension/webapi_extension.cc
new file mode 100644
index 0000000..06bb89c
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/webapi_extension.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/webapi_extension.h"
+
+#include "base/compiler_specific.h"
+#include "cobalt/script/global_environment.h"
+#include "starboard/shared/test_webapi_extension/my_new_interface.h"
+
+namespace cobalt {
+namespace browser {
+
+base::optional<std::string> GetWebAPIExtensionObjectPropertyName() {
+  return std::string("myInterface");
+}
+
+scoped_refptr<script::Wrappable> CreateWebAPIExtensionObject(
+    const scoped_refptr<dom::Window>& window,
+    script::GlobalEnvironment* global_environment) {
+  UNREFERENCED_PARAMETER(global_environment);
+
+  return scoped_refptr<script::Wrappable>(
+      new webapi_extension::MyNewInterface(window));
+}
+
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/starboard/shared/test_webapi_extension/webapi_extension.gyp b/src/starboard/shared/test_webapi_extension/webapi_extension.gyp
new file mode 100644
index 0000000..5c238e0
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/webapi_extension.gyp
@@ -0,0 +1,35 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'targets': [
+    {
+      'target_name': 'cobalt_test_webapi_extension',
+      'type': 'static_library',
+
+      # List of all source files and header files needed to support the IDL
+      # definitions.
+      'sources': [
+        'my_new_interface.h',
+        'my_new_interface.cc',
+        'webapi_extension.cc',
+      ],
+
+      'dependencies': [
+        '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/script/script.gyp:script',
+        '<(DEPTH)/base/base.gyp:base',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
index eaf8bd3..6f71b80 100644
--- a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
+++ b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
@@ -98,7 +98,7 @@
 
 WebTokenRequestResult^ RequestToken(WebTokenRequest^ request) {
   using starboard::shared::uwp::WaitForResult;
-  IAsyncOperation<WebTokenRequestResult ^> ^ request_operation = nullptr;
+  IAsyncOperation<WebTokenRequestResult^>^ request_operation = nullptr;
   base::WaitableEvent request_operation_set(false, false);
   // Ensure WebAuthenticationCoreManager::RequestTokenAsync is called on the
   // UI thread, since documentation states that "This method cannot be called
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi
index ee1b5bd..85f0f72 100644
--- a/src/starboard/shared/uwp/starboard_platform.gypi
+++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -22,6 +22,8 @@
       'system_clear_platform_error.cc',
       'system_get_device_type.cc',
       'system_get_property.cc',
+      'system_get_total_cpu_memory.cc',
+      'system_get_used_cpu_memory.cc',
       'system_raise_platform_error.cc',
       'window_create.cc',
       'window_destroy.cc',
diff --git a/src/starboard/shared/uwp/system_get_device_type.cc b/src/starboard/shared/uwp/system_get_device_type.cc
index 81179ca..33ce323 100644
--- a/src/starboard/shared/uwp/system_get_device_type.cc
+++ b/src/starboard/shared/uwp/system_get_device_type.cc
@@ -23,7 +23,7 @@
 using Windows::System::Profile::AnalyticsVersionInfo;
 
 SbSystemDeviceType SbSystemGetDeviceType() {
-  AnalyticsVersionInfo ^ version_info = AnalyticsInfo::VersionInfo;
+  AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
   std::string family = starboard::shared::win32::platformStringToString(
       version_info->DeviceFamily);
 
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/uwp/system_get_total_cpu_memory.cc
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/uwp/system_get_total_cpu_memory.cc
index be4e805..293fd7d 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/uwp/system_get_total_cpu_memory.cc
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#include "starboard/system.h"
 
-#include "starboard/shared/win32/atomic_public.h"
+using Windows::System::MemoryManager;
 
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+int64_t SbSystemGetTotalCPUMemory() {
+  return static_cast<int64_t>(MemoryManager::AppMemoryUsageLimit);
+}
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/uwp/system_get_used_cpu_memory.cc
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/uwp/system_get_used_cpu_memory.cc
index be4e805..adc9c0d 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/uwp/system_get_used_cpu_memory.cc
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#include "starboard/system.h"
 
-#include "starboard/shared/win32/atomic_public.h"
+using Windows::System::MemoryManager;
 
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+int64_t SbSystemGetUsedCPUMemory() {
+  return static_cast<int64_t>(MemoryManager::AppMemoryUsage);
+}
diff --git a/src/starboard/shared/win32/audio_decoder.cc b/src/starboard/shared/win32/audio_decoder.cc
index d6749f2..879202c 100644
--- a/src/starboard/shared/win32/audio_decoder.cc
+++ b/src/starboard/shared/win32/audio_decoder.cc
@@ -56,19 +56,24 @@
 };
 
 AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
-                           const SbMediaAudioHeader& audio_header)
-    : sample_type_(kSbMediaAudioSampleTypeFloat32),
-      stream_ended_(false),
-      audio_codec_(audio_codec),
-      audio_header_(audio_header) {
+                           const SbMediaAudioHeader& audio_header,
+                           SbDrmSystem drm_system)
+    : audio_codec_(audio_codec),
+      audio_header_(audio_header),
+      drm_system_(drm_system),
+      sample_type_(kSbMediaAudioSampleTypeFloat32),
+      stream_ended_(false) {
   SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
   decoder_impl_ = AbstractWin32AudioDecoder::Create(
-      audio_codec_, GetStorageType(), GetSampleType(), audio_header_);
+      audio_codec_, GetStorageType(), GetSampleType(), audio_header_,
+      drm_system_);
   decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
   callback_scheduler_.reset(new CallbackScheduler());
 }
 
 AudioDecoder::~AudioDecoder() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   decoder_thread_.reset(nullptr);
   decoder_impl_.reset(nullptr);
   callback_scheduler_.reset(nullptr);
@@ -76,7 +81,9 @@
 
 void AudioDecoder::Decode(const scoped_refptr<InputBuffer>& input_buffer,
                           const Closure& consumed_cb) {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
   SB_DCHECK(input_buffer);
+
   callback_scheduler_->SetCallbackOnce(consumed_cb);
   callback_scheduler_->OnCallbackSignaled();
   const bool can_take_more_data = decoder_thread_->QueueInput(input_buffer);
@@ -91,22 +98,29 @@
 }
 
 void AudioDecoder::WriteEndOfStream() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   ::starboard::ScopedLock lock(mutex_);
   stream_ended_ = true;
   decoder_thread_->QueueEndOfStream();
 }
 
 scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   DecodedAudioPtr data = decoded_data_.PopFront();
   SB_DCHECK(data);
   return data;
 }
 
 void AudioDecoder::Reset() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   decoder_thread_.reset(nullptr);
   decoder_impl_.reset(nullptr);
   decoder_impl_ = AbstractWin32AudioDecoder::Create(
-      audio_codec_, GetStorageType(), GetSampleType(), audio_header_);
+      audio_codec_, GetStorageType(), GetSampleType(), audio_header_,
+      drm_system_);
   decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
   decoded_data_.Clear();
   stream_ended_ = false;
@@ -114,14 +128,20 @@
 }
 
 SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   return sample_type_;
 }
 
 int AudioDecoder::GetSamplesPerSecond() const {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   return audio_header_.samples_per_second;
 }
 
 void AudioDecoder::Initialize(const Closure& output_cb) {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   SB_DCHECK(output_cb.is_valid());
   SB_DCHECK(!output_cb_.is_valid());
   output_cb_ = output_cb;
diff --git a/src/starboard/shared/win32/audio_decoder.h b/src/starboard/shared/win32/audio_decoder.h
index f036287..5bd335c 100644
--- a/src/starboard/shared/win32/audio_decoder.h
+++ b/src/starboard/shared/win32/audio_decoder.h
@@ -18,11 +18,13 @@
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/job_queue.h"
+#include "starboard/shared/starboard/thread_checker.h"
 #include "starboard/shared/win32/atomic_queue.h"
 #include "starboard/shared/win32/audio_decoder_thread.h"
 #include "starboard/shared/win32/media_common.h"
@@ -32,16 +34,14 @@
 namespace shared {
 namespace win32 {
 
-using JobQueue = ::starboard::shared::starboard::player::JobQueue;
-using JobOwner = JobQueue::JobOwner;
-
 class AudioDecoder
     : public ::starboard::shared::starboard::player::filter::AudioDecoder,
-      private JobOwner,
+      private ::starboard::shared::starboard::player::JobQueue::JobOwner,
       private AudioDecodedCallback {
  public:
   AudioDecoder(SbMediaAudioCodec audio_codec,
-               const SbMediaAudioHeader& audio_header);
+               const SbMediaAudioHeader& audio_header,
+               SbDrmSystem drm_system);
   ~AudioDecoder() SB_OVERRIDE;
 
   void Decode(const scoped_refptr<InputBuffer>& input_buffer,
@@ -60,9 +60,13 @@
 
  private:
   class CallbackScheduler;
-  SbMediaAudioHeader audio_header_;
-  SbMediaAudioSampleType sample_type_;
+
+  ::starboard::shared::starboard::ThreadChecker thread_checker_;
+
   SbMediaAudioCodec audio_codec_;
+  SbMediaAudioHeader audio_header_;
+  SbDrmSystem drm_system_;
+  SbMediaAudioSampleType sample_type_;
   bool stream_ended_;
 
   AtomicQueue<DecodedAudioPtr> decoded_data_;
@@ -71,7 +75,7 @@
   scoped_ptr<AudioDecoderThread> decoder_thread_;
   Closure output_cb_;
 
-  ::starboard::Mutex mutex_;
+  Mutex mutex_;
 };
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/audio_sink.cc b/src/starboard/shared/win32/audio_sink.cc
index c5d8de2..5967211 100644
--- a/src/starboard/shared/win32/audio_sink.cc
+++ b/src/starboard/shared/win32/audio_sink.cc
@@ -72,6 +72,10 @@
     ScopedLock lock(mutex_);
     playback_rate_ = playback_rate;
   }
+  void SetVolume(double volume) SB_OVERRIDE {
+    ScopedLock lock(mutex_);
+    volume_ = volume;
+  }
 
  private:
   static void* ThreadEntryPoint(void* context);
@@ -99,6 +103,7 @@
   ::starboard::Mutex mutex_;
   bool destroying_;
   double playback_rate_;
+  double volume_;
 };
 
 XAudioAudioSink::XAudioAudioSink(
@@ -118,7 +123,8 @@
       frame_buffers_size_in_frames_(frame_buffers_size_in_frames),
       wfx_(wfx),
       destroying_(false),
-      playback_rate_(1.0) {
+      playback_rate_(1.0),
+      volume_(1.0) {
   // TODO: Check MaxFrequencyRatio
   CHECK_HRESULT_OK(
       type_->x_audio2_->CreateSourceVoice(&source_voice_, &wfx, 0,
@@ -187,6 +193,7 @@
     int frames_in_buffer, offset_in_frames;
     bool is_playing, is_eos_reached;
     bool is_playback_rate_zero;
+    // TODO: Support |volume_| here as well...
     {
       ScopedLock lock(mutex_);
       is_playback_rate_zero = playback_rate_ == 0.0;
diff --git a/src/starboard/shared/win32/decode_target_internal.cc b/src/starboard/shared/win32/decode_target_internal.cc
index 70b8e32..8188621 100644
--- a/src/starboard/shared/win32/decode_target_internal.cc
+++ b/src/starboard/shared/win32/decode_target_internal.cc
@@ -26,6 +26,7 @@
 #include "third_party/angle/include/EGL/egl.h"
 #include "third_party/angle/include/EGL/eglext.h"
 #include "third_party/angle/include/GLES2/gl2.h"
+#include "third_party/angle/include/GLES2/gl2ext.h"
 
 using Microsoft::WRL::ComPtr;
 using starboard::shared::win32::VideoFramePtr;
@@ -73,24 +74,24 @@
   SbDecodeTargetInfoPlane* planeY = &(info.planes[kSbDecodeTargetPlaneY]);
   SbDecodeTargetInfoPlane* planeUV = &(info.planes[kSbDecodeTargetPlaneUV]);
 
-  planeY->width = texture_desc.Width;
-  planeY->height = texture_desc.Height;
+  planeY->width = info.width;
+  planeY->height = info.height;
   planeY->content_region.left = 0;
-  planeY->content_region.top = 0;
-  planeY->content_region.right = texture_desc.Width;
-  planeY->content_region.bottom = texture_desc.Height;
+  planeY->content_region.top = info.height;
+  planeY->content_region.right = frame->width();
+  planeY->content_region.bottom = info.height - frame->height();
 
-  planeUV->width = texture_desc.Width / 2;
-  planeUV->height = texture_desc.Height / 2;
-  planeUV->content_region.left = 0;
-  planeUV->content_region.top = 0;
-  planeUV->content_region.right = texture_desc.Width / 2;
-  planeUV->content_region.bottom = texture_desc.Height / 2;
+  planeUV->width = info.width / 2;
+  planeUV->height = info.height / 2;
+  planeUV->content_region.left = planeY->content_region.left / 2;
+  planeUV->content_region.top = planeY->content_region.top / 2;
+  planeUV->content_region.right = planeY->content_region.right / 2;
+  planeUV->content_region.bottom = planeY->content_region.bottom / 2;
 
   EGLint luma_texture_attributes[] = {EGL_WIDTH,
-                                      static_cast<EGLint>(texture_desc.Width),
+                                      static_cast<EGLint>(info.width),
                                       EGL_HEIGHT,
-                                      static_cast<EGLint>(texture_desc.Height),
+                                      static_cast<EGLint>(info.height),
                                       EGL_TEXTURE_TARGET,
                                       EGL_TEXTURE_2D,
                                       EGL_TEXTURE_FORMAT,
@@ -119,16 +120,16 @@
                                  dxgi_buffer.GetAddressOf());
   SB_DCHECK(SUCCEEDED(hr));
 
-  EGLSurface surface = eglCreatePbufferFromClientBuffer(
-      display, EGL_D3D_TEXTURE_ANGLE, d3texture.Get(), config,
-      luma_texture_attributes);
+  surface[0] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
+                                                d3texture.Get(), config,
+                                                luma_texture_attributes);
 
-  SB_DCHECK(surface != EGL_NO_SURFACE);
+  SB_DCHECK(surface[0] != EGL_NO_SURFACE);
 
   glBindTexture(GL_TEXTURE_2D, gl_textures[0]);
   SB_DCHECK(glGetError() == GL_NO_ERROR);
 
-  ok = eglBindTexImage(display, surface, EGL_BACK_BUFFER);
+  ok = eglBindTexImage(display, surface[0], EGL_BACK_BUFFER);
   SB_DCHECK(ok);
 
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -136,6 +137,7 @@
 
   planeY->texture = gl_textures[0];
   planeY->gl_texture_target = GL_TEXTURE_2D;
+  planeY->gl_texture_format = GL_RED_EXT;
 
   // This tells ANGLE that the texture it creates should draw
   // the chroma channel on R8G8.
@@ -145,24 +147,24 @@
 
   EGLint chroma_texture_attributes[] = {
       EGL_WIDTH,
-      static_cast<EGLint>(texture_desc.Width) / 2,
+      static_cast<EGLint>(info.width) / 2,
       EGL_HEIGHT,
-      static_cast<EGLint>(texture_desc.Height) / 2,
+      static_cast<EGLint>(info.height) / 2,
       EGL_TEXTURE_TARGET,
       EGL_TEXTURE_2D,
       EGL_TEXTURE_FORMAT,
       EGL_TEXTURE_RGBA,
       EGL_NONE};
-  surface = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
-                                             d3texture.Get(), config,
-                                             chroma_texture_attributes);
+  surface[1] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
+                                                d3texture.Get(), config,
+                                                chroma_texture_attributes);
 
-  SB_DCHECK(surface != EGL_NO_SURFACE);
+  SB_DCHECK(surface[1] != EGL_NO_SURFACE);
 
   glBindTexture(GL_TEXTURE_2D, gl_textures[1]);
   SB_DCHECK(glGetError() == GL_NO_ERROR);
 
-  ok = eglBindTexImage(display, surface, EGL_BACK_BUFFER);
+  ok = eglBindTexImage(display, surface[1], EGL_BACK_BUFFER);
   SB_DCHECK(ok);
 
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -170,6 +172,7 @@
 
   planeUV->texture = gl_textures[1];
   planeUV->gl_texture_target = GL_TEXTURE_2D;
+  planeUV->gl_texture_format = GL_RG_EXT;
 
   hr = d3texture->SetPrivateData(kCobaltDxgiBuffer, 0, nullptr);
   SB_DCHECK(SUCCEEDED(hr));
@@ -178,6 +181,14 @@
 SbDecodeTargetPrivate::~SbDecodeTargetPrivate() {
   glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneY].texture));
   glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneUV].texture));
+
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+  eglReleaseTexImage(display, surface[0], EGL_BACK_BUFFER);
+  eglDestroySurface(display, surface[0]);
+
+  eglReleaseTexImage(display, surface[1], EGL_BACK_BUFFER);
+  eglDestroySurface(display, surface[1]);
 }
 
 void SbDecodeTargetRelease(SbDecodeTarget decode_target) {
diff --git a/src/starboard/shared/win32/decode_target_internal.h b/src/starboard/shared/win32/decode_target_internal.h
index 6204200..81aa66e 100644
--- a/src/starboard/shared/win32/decode_target_internal.h
+++ b/src/starboard/shared/win32/decode_target_internal.h
@@ -23,6 +23,9 @@
   // Publicly accessible information about the decode target.
   SbDecodeTargetInfo info;
   ::starboard::shared::win32::VideoFramePtr frame;
+  // EGLSurface is defined as void* in "third_party/angle/include/EGL/egl.h".
+  // Use void* directly here to avoid `egl.h` being included broadly.
+  void* surface[2];
   explicit SbDecodeTargetPrivate(starboard::shared::win32::VideoFramePtr frame);
   ~SbDecodeTargetPrivate();
 };
diff --git a/src/starboard/shared/win32/gyp_configuration.py b/src/starboard/shared/win32/gyp_configuration.py
index 0bf0e26..93f92f1 100644
--- a/src/starboard/shared/win32/gyp_configuration.py
+++ b/src/starboard/shared/win32/gyp_configuration.py
@@ -85,5 +85,5 @@
   def GetToolchain(self):
     sys.path.append(
         os.path.join(STARBOARD_ROOT, 'shared', 'msvc', 'uwp'))
-    from toolchain import MSVCUWPToolchain  # pylint: disable=g-import-not-at-top,g-bad-import-order
+    from msvc_toolchain import MSVCUWPToolchain  # pylint: disable=g-import-not-at-top,g-bad-import-order
     return MSVCUWPToolchain()
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/win32/media_is_output_protected.cc
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/win32/media_is_output_protected.cc
index be4e805..73d2f07 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/win32/media_is_output_protected.cc
@@ -1,20 +1,21 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

+// Licensed under the Apache License, Version 2.0 (the "License");

+// you may not use this file except in compliance with the License.

+// You may obtain a copy of the License at

+//

+//     http://www.apache.org/licenses/LICENSE-2.0

+//

+// Unless required by applicable law or agreed to in writing, software

+// distributed under the License is distributed on an "AS IS" BASIS,

+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+// See the License for the specific language governing permissions and

+// limitations under the License.

+

+#include "starboard/media.h"

+

+bool SbMediaIsOutputProtected() {

+  // Pretend that HDCP is always on.

+  // TODO: Fix this with proper HDCP query.

+  return true;

+}

diff --git a/src/starboard/shared/win32/player_components_impl.cc b/src/starboard/shared/win32/player_components_impl.cc
index cc86d10..fa2c4af 100644
--- a/src/starboard/shared/win32/player_components_impl.cc
+++ b/src/starboard/shared/win32/player_components_impl.cc
@@ -35,9 +35,13 @@
   using VideoRendererImpl = ::starboard::shared::win32::VideoRendererImpl;
 
   AudioDecoderImpl* audio_decoder = new AudioDecoderImpl(
-      audio_parameters.audio_codec, audio_parameters.audio_header);
+      audio_parameters.audio_codec, audio_parameters.audio_header,
+      audio_parameters.drm_system);
 
-  VideoDecoderImpl* video_decoder = new VideoDecoderImpl(video_parameters);
+  VideoDecoderImpl* video_decoder = new VideoDecoderImpl(
+      video_parameters.video_codec, video_parameters.output_mode,
+      video_parameters.decode_target_graphics_context_provider,
+      video_parameters.drm_system);
 
   AudioRendererImpl* audio_renderer =
       new AudioRendererImpl(scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
diff --git a/src/starboard/shared/win32/video_decoder.cc b/src/starboard/shared/win32/video_decoder.cc
index 40fd85d..0a08c9a 100644
--- a/src/starboard/shared/win32/video_decoder.cc
+++ b/src/starboard/shared/win32/video_decoder.cc
@@ -24,17 +24,24 @@
 namespace shared {
 namespace win32 {
 
-VideoDecoder::VideoDecoder(const VideoParameters& params)
-    : video_codec_(params.video_codec),
+VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec,
+                           SbPlayerOutputMode output_mode,
+                           SbDecodeTargetGraphicsContextProvider*
+                               decode_target_graphics_context_provider,
+                           SbDrmSystem drm_system)
+    : video_codec_(video_codec),
+      drm_system_(drm_system),
       host_(NULL),
-      output_mode_(params.output_mode),
+      output_mode_(output_mode),
       decode_target_graphics_context_provider_(
-          params.decode_target_graphics_context_provider) {
-  impl_ = AbstractWin32VideoDecoder::Create(video_codec_);
+          decode_target_graphics_context_provider) {
+  impl_ = AbstractWin32VideoDecoder::Create(video_codec_, drm_system_);
   video_decoder_thread_.reset(new VideoDecoderThread(impl_.get(), this));
 }
 
 VideoDecoder::~VideoDecoder() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   video_decoder_thread_.reset(nullptr);
   impl_.reset(nullptr);
 }
@@ -71,7 +78,7 @@
   SB_DCHECK(host_);
   video_decoder_thread_.reset(nullptr);
   impl_.reset(nullptr);
-  impl_ = AbstractWin32VideoDecoder::Create(video_codec_);
+  impl_ = AbstractWin32VideoDecoder::Create(video_codec_, drm_system_);
   video_decoder_thread_.reset(new VideoDecoderThread(impl_.get(), this));
 }
 
@@ -83,7 +90,7 @@
 }
 
 void VideoDecoder::OnVideoDecoded(VideoFramePtr data) {
-  Status sts = data->IsEndOfStream() ? kBufferFull : kNeedMoreInput;
+  Status sts = (data && data->IsEndOfStream()) ? kBufferFull : kNeedMoreInput;
   host_->OnDecoderStatusUpdate(sts, data);
 }
 
diff --git a/src/starboard/shared/win32/video_decoder.h b/src/starboard/shared/win32/video_decoder.h
index 1fd2bfa..5d3734b 100644
--- a/src/starboard/shared/win32/video_decoder.h
+++ b/src/starboard/shared/win32/video_decoder.h
@@ -18,6 +18,7 @@
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration.h"
+#include "starboard/drm.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
@@ -38,7 +39,11 @@
       private ::starboard::shared::starboard::player::JobQueue::JobOwner,
       private VideoDecodedCallback {
  public:
-  explicit VideoDecoder(const VideoParameters& params);
+  VideoDecoder(SbMediaVideoCodec video_codec,
+               SbPlayerOutputMode output_mode,
+               SbDecodeTargetGraphicsContextProvider*
+                   decode_target_graphics_context_provider,
+               SbDrmSystem drm_system);
   ~VideoDecoder() SB_OVERRIDE;
 
   void SetHost(Host* host) SB_OVERRIDE;
@@ -53,22 +58,23 @@
   void OnVideoDecoded(VideoFramePtr data) SB_OVERRIDE;
 
  private:
+  ::starboard::shared::starboard::ThreadChecker thread_checker_;
+
   // These variables will be initialized inside ctor or SetHost() and will not
   // be changed during the life time of this class.
   const SbMediaVideoCodec video_codec_;
+  SbDrmSystem drm_system_;
   Host* host_;
 
   // Decode-to-texture related state.
-  SbPlayerOutputMode output_mode_;
-
-  SbDecodeTargetGraphicsContextProvider*
+  const SbPlayerOutputMode output_mode_;
+  SbDecodeTargetGraphicsContextProvider* const
       decode_target_graphics_context_provider_;
 
   scoped_ptr<AbstractWin32VideoDecoder> impl_;
   AtomicQueue<VideoFramePtr> decoded_data_;
-  ::starboard::Mutex mutex_;
+  Mutex mutex_;
   scoped_ptr<VideoDecoderThread> video_decoder_thread_;
-  ::starboard::shared::starboard::ThreadChecker thread_checker_;
 };
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/video_decoder_thread.cc b/src/starboard/shared/win32/video_decoder_thread.cc
index 64a7b2e..d2f8ba2 100644
--- a/src/starboard/shared/win32/video_decoder_thread.cc
+++ b/src/starboard/shared/win32/video_decoder_thread.cc
@@ -102,6 +102,7 @@
     if (number_written > 0) {
       processing_elements_.fetch_sub(static_cast<int32_t>(number_written));
       work_done = true;
+      callback_->OnVideoDecoded(NULL);
     }
 
     while (VideoFramePtr decoded_datum =
diff --git a/src/starboard/shared/win32/win32_audio_decoder.cc b/src/starboard/shared/win32/win32_audio_decoder.cc
index 0b85f30..4142764 100644
--- a/src/starboard/shared/win32/win32_audio_decoder.cc
+++ b/src/starboard/shared/win32/win32_audio_decoder.cc
@@ -103,18 +103,19 @@
 };
 
 class AbstractWin32AudioDecoderImpl : public AbstractWin32AudioDecoder,
-                                    public MediaBufferConsumerInterface {
+                                      public MediaBufferConsumerInterface {
  public:
   AbstractWin32AudioDecoderImpl(SbMediaAudioCodec codec,
-                              SbMediaAudioFrameStorageType audio_frame_fmt,
-                              SbMediaAudioSampleType sample_type,
-                              const SbMediaAudioHeader& audio_header)
+                                SbMediaAudioFrameStorageType audio_frame_fmt,
+                                SbMediaAudioSampleType sample_type,
+                                const SbMediaAudioHeader& audio_header,
+                                SbDrmSystem drm_system)
       : codec_(codec),
         audio_frame_fmt_(audio_frame_fmt),
         sample_type_(sample_type),
         audio_header_(audio_header) {
     MediaBufferConsumerInterface* media_cb = this;
-    impl_.reset(new DecoderImpl("audio", media_cb));
+    impl_.reset(new DecoderImpl("audio", media_cb, drm_system));
     EnsureAudioDecoderCreated();
   }
 
@@ -151,6 +152,8 @@
     media_buffer->Unlock();
   }
 
+  void OnNewOutputType(const ComPtr<IMFMediaType>& /*type*/) override {}
+
   ComPtr<IMFMediaType> Configure(IMFTransform* decoder) {
     ComPtr<IMFMediaType> input_type;
     HRESULT hr = MFCreateMediaType(&input_type);
@@ -209,7 +212,6 @@
     SB_DCHECK(decoder);
 
     impl_->set_decoder(decoder);
-    impl_->ActivateDecryptor(media_type);
 
     // TODO: MFWinAudioFormat_PCM?
     ComPtr<IMFMediaType> output_type =
@@ -234,17 +236,40 @@
     const int size = buff.size();
     const int64_t media_timestamp = buff.pts();
 
-    // These parameters are used for decryption. But these are not used right
-    // now and so remain empty.
     std::vector<uint8_t> key_id;
     std::vector<uint8_t> iv;
     std::vector<Subsample> subsamples;
 
+    const SbDrmSampleInfo* drm_info = buff.drm_info();
+
+    if (drm_info != NULL && drm_info->initialization_vector_size != 0) {
+      key_id.assign(drm_info->identifier,
+                    drm_info->identifier + drm_info->identifier_size);
+      iv.assign(drm_info->initialization_vector,
+                drm_info->initialization_vector +
+                    drm_info->initialization_vector_size);
+      subsamples.reserve(drm_info->subsample_count);
+      for (int32_t i = 0; i < drm_info->subsample_count; ++i) {
+        Subsample subsample = {
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].clear_byte_count),
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].encrypted_byte_count)};
+        subsamples.push_back(subsample);
+      }
+    }
+
     const std::int64_t win32_time_stamp = ConvertToWin32Time(media_timestamp);
 
     // Adjust the offset for 7 bytes to remove the ADTS header.
-    const uint8_t* audio_start = static_cast<const uint8_t*>(data) + 7;
-    const int audio_size = size - 7;
+    const int kADTSHeaderSize = 7;
+    const uint8_t* audio_start =
+        static_cast<const uint8_t*>(data) + kADTSHeaderSize;
+    const int audio_size = size - kADTSHeaderSize;
+    if (!subsamples.empty()) {
+      SB_DCHECK(subsamples[0].clear_bytes == kADTSHeaderSize);
+      subsamples[0].clear_bytes = 0;
+    }
 
     const bool write_ok = impl_->TryWriteInputBuffer(
         audio_start, audio_size, win32_time_stamp, key_id, iv, subsamples);
@@ -282,10 +307,11 @@
     SbMediaAudioCodec code,
     SbMediaAudioFrameStorageType audio_frame_fmt,
     SbMediaAudioSampleType sample_type,
-    const SbMediaAudioHeader& audio_header) {
-  return scoped_ptr<AbstractWin32AudioDecoder>(new
-    AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
-      audio_header));
+    const SbMediaAudioHeader& audio_header,
+    SbDrmSystem drm_system) {
+  return scoped_ptr<AbstractWin32AudioDecoder>(
+      new AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
+                                        audio_header, drm_system));
 }
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/win32_audio_decoder.h b/src/starboard/shared/win32/win32_audio_decoder.h
index 4cc5f27..3bfe78c 100644
--- a/src/starboard/shared/win32/win32_audio_decoder.h
+++ b/src/starboard/shared/win32/win32_audio_decoder.h
@@ -18,6 +18,7 @@
 #include <vector>
 
 #include "starboard/common/scoped_ptr.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/video_frame_internal.h"
@@ -35,7 +36,8 @@
       SbMediaAudioCodec codec,
       SbMediaAudioFrameStorageType audio_frame_fmt,
       SbMediaAudioSampleType sample_type,
-      const SbMediaAudioHeader& audio_header);
+      const SbMediaAudioHeader& audio_header,
+      SbDrmSystem drm_system);
   virtual ~AbstractWin32AudioDecoder() {}
 
   // INPUT:
diff --git a/src/starboard/shared/win32/win32_decoder_impl.cc b/src/starboard/shared/win32/win32_decoder_impl.cc
index b06fcdc..b41b418 100644
--- a/src/starboard/shared/win32/win32_decoder_impl.cc
+++ b/src/starboard/shared/win32/win32_decoder_impl.cc
@@ -117,11 +117,15 @@
 }  // namespace
 
 DecoderImpl::DecoderImpl(const std::string& type,
-                         MediaBufferConsumerInterface* media_buffer_consumer)
+                         MediaBufferConsumerInterface* media_buffer_consumer,
+                         SbDrmSystem drm_system)
     : type_(type),
       media_buffer_consumer_(media_buffer_consumer),
       discontinuity_(false) {
   SB_DCHECK(media_buffer_consumer_);
+  drm_system_ =
+      static_cast<::starboard::xb1::shared::playready::SbDrmSystemPlayready*>(
+          drm_system);
 }
 
 DecoderImpl::~DecoderImpl() {
@@ -137,13 +141,29 @@
   return decoder;
 }
 
-void DecoderImpl::ActivateDecryptor(ComPtr<IMFMediaType> input_type) {
+void DecoderImpl::ActivateDecryptor() {
+  SB_DCHECK(decoder_);
+
   if (!decryptor_) {
     return;
   }
 
-  HRESULT hr = decryptor_->SetInputType(kStreamId, input_type.Get(),
-                                        0);  // MFT_SET_TYPE_TEST_FLAGS.
+  ComPtr<IMFMediaType> decryptor_input_type;
+
+  HRESULT hr = decoder_->GetInputCurrentType(
+      kStreamId, decryptor_input_type.GetAddressOf());
+  CheckResult(hr);
+
+  GUID original_sub_type;
+  {
+    ComPtr<IMFMediaType> output_type;
+    hr = decoder_->GetOutputCurrentType(kStreamId, output_type.GetAddressOf());
+    CheckResult(hr);
+    output_type->GetGUID(MF_MT_SUBTYPE, &original_sub_type);
+  }
+
+  const DWORD kFlags = 0;
+  hr = decryptor_->SetInputType(kStreamId, decryptor_input_type.Get(), kFlags);
   CheckResult(hr);
 
   // Ensure that the decryptor and the decoder agrees on the protection of
@@ -158,7 +178,6 @@
   BYTE* crypt_seed = NULL;
   DWORD crypt_seed_size = 0;
   ComPtr<IMFMediaType> decoder_input_type;
-  ComPtr<IMFMediaType> decoder_output_type;
 
   hr = decryptor_.As(&decryption_sample_protection);
   CheckResult(hr);
@@ -211,6 +230,21 @@
   // Start the decryptor, note that this should be better abstracted.
   SendMFTMessage(decryptor_.Get(), MFT_MESSAGE_NOTIFY_BEGIN_STREAMING);
   SendMFTMessage(decryptor_.Get(), MFT_MESSAGE_NOTIFY_START_OF_STREAM);
+
+  for (int index = 0;; ++index) {
+    ComPtr<IMFMediaType> output_type;
+    hr = decoder_->GetOutputAvailableType(0, index, &output_type);
+    if (SUCCEEDED(hr)) {
+      GUID sub_type;
+      output_type->GetGUID(MF_MT_SUBTYPE, &sub_type);
+      if (IsEqualGUID(sub_type, original_sub_type)) {
+        hr = decoder_->SetOutputType(0, output_type.Get(), 0);
+        CheckResult(hr);
+        break;
+      }
+    }
+    output_type.Reset();
+  }
 }
 
 bool DecoderImpl::TryWriteInputBuffer(
@@ -236,6 +270,19 @@
   // Better check the key id size is 16 and iv size is 8 or 16.
   bool encrypted = !key_id.empty() && !iv.empty();
   if (encrypted) {
+    if (!decryptor_) {
+      scoped_refptr<::starboard::xb1::shared::playready::PlayreadyLicense>
+          license = drm_system_->GetLicense(key_id.data(),
+                                            static_cast<int>(key_id.size()));
+      if (license && license->usable()) {
+        decryptor_ = license->decryptor();
+        ActivateDecryptor();
+      }
+    }
+    if (!decryptor_) {
+      SB_NOTREACHED();
+      return false;
+    }
     size_t iv_size = iv.size();
     const char kEightZeros[8] = {0};
     if (iv_size == 16 && SbMemoryCompare(iv.data() + 8, kEightZeros, 8) == 0) {
@@ -315,27 +362,33 @@
 
 bool DecoderImpl::DeliverOneOutputOnAllTransforms() {
   SB_DCHECK(decoder_);
-  bool delivered = false;
   if (decryptor_) {
-    if (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decryptor_)) {
-      HRESULT hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+    if (!cached_decryptor_output_) {
+      cached_decryptor_output_ = DeliverOutputOnTransform(decryptor_);
+    }
+    while (cached_decryptor_output_) {
+      HRESULT hr =
+          decoder_->ProcessInput(kStreamId, cached_decryptor_output_.Get(), 0);
       if (hr == MF_E_NOTACCEPTING) {
-        // The protocol says that when ProcessInput() returns MF_E_NOTACCEPTING,
-        // there must be some output available. Retrieve the output and the next
-        // ProcessInput() should succeed.
-        ComPtr<IMFSample> sample_inner = DeliverOutputOnTransform(decoder_);
-        if (sample_inner) {
-          DeliverDecodedSample(sample_inner);
-          delivered = true;
-        }
-        hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+        break;
+      } else {
         CheckResult(hr);
-        return delivered;
+        cached_decryptor_output_ = DeliverOutputOnTransform(decryptor_);
       }
-      CheckResult(hr);
-      delivered = true;
+    }
+    if (cached_decryptor_output_) {
+      // The protocol says that when ProcessInput() returns MF_E_NOTACCEPTING,
+      // there must be some output available. Retrieve the output and the next
+      // ProcessInput() should succeed.
+      ComPtr<IMFSample> decoder_output = DeliverOutputOnTransform(decoder_);
+      if (decoder_output) {
+        DeliverDecodedSample(decoder_output);
+      }
+      return decoder_output != NULL;
     }
   }
+
+  bool delivered = false;
   if (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decoder_)) {
     DeliverDecodedSample(sample);
     delivered = true;
@@ -371,18 +424,6 @@
   HRESULT hr = transform->GetOutputStreamInfo(kStreamId, &output_stream_info);
   CheckResult(hr);
 
-  // Each media sample (IMFSample interface) of output data from the MFT
-  // contains complete, unbroken units of data. The definition of a unit
-  // of data depends on the media type: For uncompressed video, a video
-  // frame; for compressed data, a compressed packet; for uncompressed audio,
-  // a single audio frame.
-  //
-  // For uncompressed audio formats, this flag is always implied. (It is valid
-  // to set the flag, but not required.) An uncompressed audio frame should
-  // never span more than one media sample.
-  SB_DCHECK((output_stream_info.dwFlags & MFT_OUTPUT_STREAM_WHOLE_SAMPLES) !=
-            0);
-
   if (StreamAllocatesMemory(output_stream_info.dwFlags)) {
     // Try to let the IMFTransform allocate the memory if possible.
     return;
@@ -431,9 +472,21 @@
 
     hr = transform->SetOutputType(kStreamId, media_type.Get(), 0);
     CheckResult(hr);
-    return NULL;
-  } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT ||
-             output_data_buffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) {
+
+    media_buffer_consumer_->OnNewOutputType(media_type);
+
+    hr = transform->ProcessOutput(kFlags, kNumberOfBuffers, &output_data_buffer,
+                                  &status);
+
+    SB_DCHECK(!output_data_buffer.pEvents);
+
+    output = output_data_buffer.pSample;
+    ReleaseIfNotNull(&output_data_buffer.pEvents);
+    ReleaseIfNotNull(&output_data_buffer.pSample);
+  }
+
+  if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT ||
+      output_data_buffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) {
     return NULL;
   }
 
diff --git a/src/starboard/shared/win32/win32_decoder_impl.h b/src/starboard/shared/win32/win32_decoder_impl.h
index 38374ff..cef0d5d 100644
--- a/src/starboard/shared/win32/win32_decoder_impl.h
+++ b/src/starboard/shared/win32/win32_decoder_impl.h
@@ -25,10 +25,14 @@
 #include <vector>
 
 #include "starboard/common/scoped_ptr.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/win32/media_common.h"
 #include "starboard/types.h"
 
+// TODO: Win32 code shouldn't depend on Xb1 code.  Refactor this.
+#include "starboard/xb1/shared/playready/drm_system_playready.h"
+
 namespace starboard {
 namespace shared {
 namespace win32 {
@@ -38,6 +42,8 @@
   virtual void Consume(Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer,
                        int64_t win32_timestamp) = 0;
 
+  virtual void OnNewOutputType(const ComPtr<IMFMediaType>& type) = 0;
+
  protected:
   ~MediaBufferConsumerInterface() {}
 };
@@ -50,12 +56,13 @@
 class DecoderImpl {
  public:
   DecoderImpl(const std::string& type,
-              MediaBufferConsumerInterface* media_buffer_consumer);
+              MediaBufferConsumerInterface* media_buffer_consumer,
+              SbDrmSystem drm_system);
   ~DecoderImpl();
 
   static Microsoft::WRL::ComPtr<IMFTransform> CreateDecoder(CLSID clsid);
 
-  void ActivateDecryptor(Microsoft::WRL::ComPtr<IMFMediaType> input_type);
+  void ActivateDecryptor();
   bool TryWriteInputBuffer(const void* data,
                            int size,
                            std::int64_t win32_timestamp,
@@ -84,9 +91,11 @@
   const std::string type_;  // For debugging purpose.
   // This is the target for the all completed media buffers.
   MediaBufferConsumerInterface* media_buffer_consumer_;
+  ::starboard::xb1::shared::playready::SbDrmSystemPlayready* drm_system_;
   bool discontinuity_;
 
   Microsoft::WRL::ComPtr<IMFTransform> decryptor_;
+  Microsoft::WRL::ComPtr<IMFSample> cached_decryptor_output_;
   Microsoft::WRL::ComPtr<IMFTransform> decoder_;
 };
 
diff --git a/src/starboard/shared/win32/win32_video_decoder.cc b/src/starboard/shared/win32/win32_video_decoder.cc
index 93bc487..667b2e5 100644
--- a/src/starboard/shared/win32/win32_video_decoder.cc
+++ b/src/starboard/shared/win32/win32_video_decoder.cc
@@ -36,31 +36,22 @@
 // This magic number is taken directly from sample from MS.  Can be further
 // tuned in case if there is playback issues.
 const int kSampleAllocatorFramesMax = 5;
+// This is the minimum allocated frames in the output ring buffer.  Can be
+// further tuned to save memory.  Note that use a value that is too small leads
+// to hang when calling ProcessOutput().
+const int kMinimumOutputSampleCount = 10;
 
 // CLSID_CMSVideoDecoderMFT {62CE7E72-4C71-4D20-B15D-452831A87D9D}
 const GUID CLSID_VideoDecoder = {0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45,
                                  0x28,       0x31,   0xA8,   0x7D, 0x9D};
 
-ComPtr<ID3D11Texture2D> GetTexture2D(ComPtr<IMFMediaBuffer> media_buffer) {
-  ComPtr<IMFDXGIBuffer> dxgi_buffer;
-  HRESULT hr = media_buffer.As(&dxgi_buffer);
-  CheckResult(hr);
-  SB_DCHECK(dxgi_buffer.Get());
-
-  ComPtr<ID3D11Texture2D> dx_texture;
-  hr = dxgi_buffer->GetResource(IID_PPV_ARGS(&dx_texture));
-  CheckResult(hr);
-  return dx_texture;
-}
-
 class VideoFrameFactory {
  public:
   static VideoFramePtr Construct(SbMediaTime timestamp,
-                                 ComPtr<IMFMediaBuffer> media_buffer) {
-    ComPtr<ID3D11Texture2D> texture = GetTexture2D(media_buffer);
-    D3D11_TEXTURE2D_DESC tex_desc;
-    texture->GetDesc(&tex_desc);
-    VideoFramePtr out(new VideoFrame(tex_desc.Width, tex_desc.Height,
+                                 ComPtr<IMFMediaBuffer> media_buffer,
+                                 uint32_t width,
+                                 uint32_t height) {
+    VideoFramePtr out(new VideoFrame(width, height,
                                      timestamp, media_buffer.Detach(),
                                      nullptr, DeleteTextureFn));
     frames_in_flight_.increment();
@@ -84,10 +75,10 @@
 class AbstractWin32VideoDecoderImpl : public AbstractWin32VideoDecoder,
                                     public MediaBufferConsumerInterface {
  public:
-  explicit AbstractWin32VideoDecoderImpl(SbMediaVideoCodec codec)
+  AbstractWin32VideoDecoderImpl(SbMediaVideoCodec codec, SbDrmSystem drm_system)
       : codec_(codec), surface_width_(0), surface_height_(0) {
     MediaBufferConsumerInterface* media_cb = this;
-    impl_.reset(new DecoderImpl("video", media_cb));
+    impl_.reset(new DecoderImpl("video", media_cb, drm_system));
     EnsureVideoDecoderCreated();
   }
 
@@ -95,10 +86,32 @@
                        int64_t win32_timestamp) {
     const SbMediaTime media_timestamp = ConvertToMediaTime(win32_timestamp);
     VideoFramePtr frame_output =
-        VideoFrameFactory::Construct(media_timestamp, media_buffer);
+        VideoFrameFactory::Construct(media_timestamp, media_buffer,
+            surface_width_, surface_height_);
     output_queue_.PushBack(frame_output);
   }
 
+  void OnNewOutputType(const ComPtr<IMFMediaType>& type) override {
+    MFVideoArea aperture;
+    HRESULT hr = type->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
+        reinterpret_cast<UINT8*>(&aperture), sizeof(MFVideoArea), nullptr);
+    if (SUCCEEDED(hr)) {
+      // TODO: consider offset as well
+      surface_width_ = aperture.Area.cx;
+      surface_height_ = aperture.Area.cy;
+      return;
+    }
+
+    uint32_t width;
+    uint32_t height;
+    hr = MFGetAttributeSize(type.Get(), MF_MT_FRAME_SIZE,
+                            &width, &height);
+    if (SUCCEEDED(hr)) {
+      surface_width_ = width;
+      surface_height_ = height;
+    }
+  }
+
   ComPtr<IMFMediaType> Configure(IMFTransform* decoder) {
     ComPtr<IMFMediaType> input_type;
     HRESULT hr = MFCreateMediaType(&input_type);
@@ -160,8 +173,6 @@
     SB_DCHECK(1 == input_stream_count);
     SB_DCHECK(1 == output_stream_count);
 
-    impl_->ActivateDecryptor(media_type);
-
     ComPtr<IMFMediaType> output_type =
         FindMediaType(MFVideoFormat_YV12, decoder.Get());
 
@@ -174,6 +185,10 @@
     hr = decoder->GetAttributes(attributes.GetAddressOf());
     CheckResult(hr);
 
+    hr = attributes->SetUINT32(MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT,
+                               kMinimumOutputSampleCount);
+    CheckResult(hr);
+
     UINT32 value = 0;
     hr = attributes->GetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, &value);
     SB_DCHECK(hr == S_OK || hr == MF_E_ATTRIBUTENOTFOUND);
@@ -225,19 +240,35 @@
       return false;  // Wait for more data.
     }
 
-    // This would be used for decrypting content.
-    // For now this is empty.
     std::vector<uint8_t> key_id;
     std::vector<uint8_t> iv;
+    std::vector<Subsample> subsamples;
 
-    std::vector<Subsample> empty_subsample;
+    // TODO: Merge this with similar code in the audio decoder.
+    const SbDrmSampleInfo* drm_info = buff->drm_info();
+
+    if (drm_info != NULL && drm_info->initialization_vector_size != 0) {
+      key_id.assign(drm_info->identifier,
+                    drm_info->identifier + drm_info->identifier_size);
+      iv.assign(drm_info->initialization_vector,
+                drm_info->initialization_vector +
+                    drm_info->initialization_vector_size);
+      subsamples.reserve(drm_info->subsample_count);
+      for (int32_t i = 0; i < drm_info->subsample_count; ++i) {
+        Subsample subsample = {
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].clear_byte_count),
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].encrypted_byte_count)};
+        subsamples.push_back(subsample);
+      }
+    }
 
     const SbMediaTime media_timestamp = buff->pts();
     const int64_t win32_timestamp = ConvertToWin32Time(media_timestamp);
 
     const bool write_ok = impl_->TryWriteInputBuffer(
-        buff->data(), buff->size(), win32_timestamp,
-        key_id, iv, empty_subsample);
+        buff->data(), buff->size(), win32_timestamp, key_id, iv, subsamples);
 
     return write_ok;
   }
@@ -271,12 +302,14 @@
   uint32_t surface_height_;
   HardwareDecoderContext dx_decoder_ctx_;
 };
+
 }  // anonymous namespace.
 
 scoped_ptr<AbstractWin32VideoDecoder> AbstractWin32VideoDecoder::Create(
-    SbMediaVideoCodec codec) {
+    SbMediaVideoCodec codec,
+    SbDrmSystem drm_system) {
   return scoped_ptr<AbstractWin32VideoDecoder>(
-      new AbstractWin32VideoDecoderImpl(codec));
+      new AbstractWin32VideoDecoderImpl(codec, drm_system));
 }
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/win32_video_decoder.h b/src/starboard/shared/win32/win32_video_decoder.h
index 84ff7b7..a32158b 100644
--- a/src/starboard/shared/win32/win32_video_decoder.h
+++ b/src/starboard/shared/win32/win32_video_decoder.h
@@ -20,6 +20,7 @@
 
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/video_frame_internal.h"
@@ -33,7 +34,8 @@
 // VideoDecoder for Win32.
 class AbstractWin32VideoDecoder {
  public:
-  static scoped_ptr<AbstractWin32VideoDecoder> Create(SbMediaVideoCodec codec);
+  static scoped_ptr<AbstractWin32VideoDecoder> Create(SbMediaVideoCodec codec,
+                                                      SbDrmSystem drm_system);
   virtual ~AbstractWin32VideoDecoder() {}
 
   virtual bool TryWrite(const scoped_refptr<InputBuffer>& buff) = 0;
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 19fb1f2..c4a5cb6 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -43,6 +43,9 @@
 namespace starboard {
 namespace shared {
 namespace x11 {
+
+using ::starboard::shared::dev_input::DevInput;
+
 namespace {
 
 enum {
@@ -647,28 +650,6 @@
 }
 #endif
 
-bool XNextEventTimed(Display* display, XEvent* out_event, SbTime duration) {
-  if (XPending(display) == 0) {
-    if (duration <= SbTime()) {
-      return false;
-    }
-
-    int fd = ConnectionNumber(display);
-    fd_set read_set;
-    FD_ZERO(&read_set);
-    FD_SET(fd, &read_set);
-    struct timeval tv;
-    SbTime clamped_duration = std::max(duration, SbTime());
-    ToTimevalDuration(clamped_duration, &tv);
-    if (select(fd + 1, &read_set, NULL, NULL, &tv) == 0) {
-      return false;
-    }
-  }
-
-  XNextEvent(display, out_event);
-  return true;
-}
-
 void XSendAtom(Window window, Atom atom) {
   // XLib is not thread-safe. Since we may be coming from another thread, we
   // have to open another connection to the display to inject the wake-up event.
@@ -732,6 +713,10 @@
 
   SbWindow window = new SbWindowPrivate(display_, options);
   windows_.push_back(window);
+  if (!dev_input_) {
+    // evdev input will be sent to the first created window only.
+    dev_input_.reset(DevInput::Create(window, ConnectionNumber(display_)));
+  }
   return window;
 }
 
@@ -745,6 +730,12 @@
       std::find(windows_.begin(), windows_.end(), window);
   SB_DCHECK(iterator != windows_.end());
   windows_.erase(iterator);
+
+  if (windows_.empty()) {
+    SB_DCHECK(dev_input_);
+    dev_input_.reset();
+  }
+
   delete window;
   if (windows_.empty()) {
     StopX();
@@ -849,23 +840,29 @@
 ApplicationX11::WaitForSystemEventWithTimeout(SbTime time) {
   SB_DCHECK(display_);
 
-  XEvent x_event;
-
   shared::starboard::Application::Event* pending_event = GetPendingEvent();
   if (pending_event) {
     return pending_event;
   }
 
-  if (XNextEventTimed(display_, &x_event, time)) {
+  SB_DCHECK(dev_input_);
+  shared::starboard::Application::Event* evdev_event =
+      dev_input_->WaitForSystemEventWithTimeout(time);
+
+  if (!evdev_event && XPending(display_) != 0) {
+    XEvent x_event;
+    XNextEvent(display_, &x_event);
     return XEventToEvent(&x_event);
-  } else {
-    return NULL;
   }
+
+  return evdev_event;
 }
 
 void ApplicationX11::WakeSystemEventWait() {
   SB_DCHECK(!windows_.empty());
   XSendAtom((*windows_.begin())->window, wake_up_atom_);
+  SB_DCHECK(dev_input_);
+  dev_input_->WakeSystemEventWait();
 }
 
 bool ApplicationX11::EnsureX() {
@@ -879,7 +876,7 @@
   XSetErrorHandler(ErrorHandler);
   display_ = XOpenDisplay(NULL);
   if (!display_) {
-    const char *display_environment = getenv("DISPLAY");
+    const char* display_environment = getenv("DISPLAY");
     if (display_environment == NULL) {
       SB_LOG(ERROR) << "Unable to open display, DISPLAY not set.";
     } else {
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index a0235b1..cadfc90 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -20,9 +20,11 @@
 #include <queue>
 #include <vector>
 
+#include "base/memory/scoped_ptr.h"
 #include "starboard/configuration.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/linux/dev_input/dev_input.h"
 #include "starboard/shared/starboard/application.h"
 #include "starboard/shared/starboard/queue_application.h"
 #include "starboard/types.h"
@@ -117,6 +119,9 @@
   // Indicates whether a key press event that requires a matching release has
   // been dispatched.
   bool paste_buffer_key_release_pending_;
+
+  // The /dev/input input handler. Only set when there is an open window.
+  scoped_ptr<::starboard::shared::dev_input::DevInput> dev_input_;
 };
 
 }  // namespace x11
diff --git a/src/starboard/storage.h b/src/starboard/storage.h
index 37ccc10..33ba547 100644
--- a/src/starboard/storage.h
+++ b/src/starboard/storage.h
@@ -51,13 +51,34 @@
   return record != kSbStorageInvalidRecord;
 }
 
-// Opens and returns a SbStorageRecord for |user|, blocking I/O on the calling
-// thread until it returns. If |user| is not a valid |SbUser|, the function
-// returns |kSbStorageInvalidRecord|.
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Opens and returns the default SbStorageRecord for |user|, blocking I/O on the
+// calling thread until the open is completed. If |user| is not a valid
+// |SbUser|, the function returns |kSbStorageInvalidRecord|. Will return an
+// |SbStorageRecord| of size zero if the record does not yet exist. Opening an
+// already-open |SbStorageRecord| has undefined behavior.
 //
-// |user|: The user for which the storage record is opened.
+// |user|: The user for which the storage record will be opened.
 SB_EXPORT SbStorageRecord SbStorageOpenRecord(SbUser user);
 
+#else  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Opens and returns the SbStorageRecord for |user| named |name|, blocking I/O
+// on the calling thread until the open is completed. If |user| is not a valid
+// |SbUser|, the function returns |kSbStorageInvalidRecord|. Will return an
+// |SbStorageRecord| of size zero if the record does not yet exist. Opening an
+// already-open |SbStorageRecord| has undefined behavior.
+//
+// If |name| is NULL, opens the default storage record for the user, like what
+// would have been saved with the previous version of SbStorageOpenRecord.
+//
+// |user|: The user for which the storage record will be opened.
+// |name|: The filesystem-safe name of the record to open.
+SB_EXPORT SbStorageRecord SbStorageOpenRecord(SbUser user, const char* name);
+
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
 // Closes |record|, synchronously ensuring that all written data is flushed.
 // This function performs blocking I/O on the calling thread.
 //
@@ -91,9 +112,15 @@
 
 // Replaces the data in |record| with |data_size| bytes from |data|. This
 // function always deletes any previous data in that record. The return value
-// indicates whether the write succeeded. This function makes a best-effort
-// to read the entire record, and it performs performs blocking I/O on the
-// calling thread until the entire record is read or an error is encountered.
+// indicates whether the write succeeded. This function makes a best-effort to
+// write the entire record, and it may perform blocking I/O on the calling
+// thread until the entire record is written or an error is encountered.
+//
+// While |SbStorageWriteRecord()| may defer the persistence,
+// |SbStorageReadRecord()| is expected to work as expected immediately
+// afterwards, even without a call to |SbStorageCloseRecord()|. The data should
+// be persisted after a short time, even if there is an unexpected process
+// termination before |SbStorageCloseRecord()| is called.
 //
 // |record|: The record to be written to.
 // |data|: The data to write to the record.
@@ -104,21 +131,43 @@
                                     const char* data,
                                     int64_t data_size);
 
-// Deletes the |SbStorageRecord| for the specified user. The return value
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Deletes the default |SbStorageRecord| for the |user|. The return value
 // indicates whether the record existed and was successfully deleted. If the
 // record did not exist or could not be deleted, the function returns |false|.
 //
 // This function must not be called while the user's storage record is open.
 // This function performs blocking I/O on the calling thread.
 //
-// |user|: The user for whom the record is deleted.
+// |user|: The user for whom the record will be deleted.
 SB_EXPORT bool SbStorageDeleteRecord(SbUser user);
 
+#else  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Deletes the |SbStorageRecord| for |user| named |name|. The return value
+// indicates whether the record existed and was successfully deleted. If the
+// record did not exist or could not be deleted, the function returns |false|.
+//
+// If |name| is NULL, deletes the default storage record for the user, like what
+// would have been deleted with the previous version of SbStorageDeleteRecord.
+//
+// This function must not be called while the user's storage record is open.
+// This function performs blocking I/O on the calling thread.
+//
+// |user|: The user for whom the record will be deleted.
+// |name|: The filesystem-safe name of the record to open.
+SB_EXPORT bool SbStorageDeleteRecord(SbUser user, const char* name);
+
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
 
 #ifdef __cplusplus
+#include <string>
+
 namespace starboard {
 
 // Inline scoped wrapper for SbStorageRecord.
@@ -126,18 +175,28 @@
  public:
   StorageRecord()
       : user_(SbUserGetCurrent()), record_(kSbStorageInvalidRecord) {
-    if (SbUserIsValid(user_)) {
-      record_ = SbStorageOpenRecord(user_);
-    }
+    Initialize();
   }
 
   explicit StorageRecord(SbUser user)
       : user_(user), record_(kSbStorageInvalidRecord) {
-    if (SbUserIsValid(user_)) {
-      record_ = SbStorageOpenRecord(user_);
-    }
+    Initialize();
   }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  explicit StorageRecord(const char* name)
+      : user_(SbUserGetCurrent()),
+        name_(name),
+        record_(kSbStorageInvalidRecord) {
+    Initialize();
+  }
+
+  StorageRecord(SbUser user, const char* name)
+      : user_(user), name_(name), record_(kSbStorageInvalidRecord) {
+    Initialize();
+  }
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
   ~StorageRecord() { Close(); }
   bool IsValid() { return SbStorageIsValidRecord(record_); }
   int64_t GetSize() { return SbStorageGetRecordSize(record_); }
@@ -160,11 +219,36 @@
 
   bool Delete() {
     Close();
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+    if (!name_.empty()) {
+      return SbStorageDeleteRecord(user_, name_.c_str());
+    } else {
+      return SbStorageDeleteRecord(user_, NULL);
+    }
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
     return SbStorageDeleteRecord(user_);
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   }
 
  private:
+  void Initialize() {
+    if (SbUserIsValid(user_)) {
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+      if (!name_.empty()) {
+        record_ = SbStorageOpenRecord(user_, name_.c_str());
+      } else {
+        record_ = SbStorageOpenRecord(user_, NULL);
+      }
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+      record_ = SbStorageOpenRecord(user_);
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+    }
+  }
+
   SbUser user_;
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  std::string name_;
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   SbStorageRecord record_;
 };
 
diff --git a/src/starboard/stub/BUILD.gn b/src/starboard/stub/BUILD.gn
new file mode 100644
index 0000000..18ad876
--- /dev/null
+++ b/src/starboard/stub/BUILD.gn
@@ -0,0 +1,428 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/fastbuild.gni")
+import("//starboard/build/delegated_config.gni")
+
+# =============================================================================
+# DEFAULT COMPILER CONFIGS
+# =============================================================================
+
+config("compiler_defaults") {
+  cflags = [
+    # We'll pretend not to be Linux, but Starboard instead.
+    "-U__linux__",
+    "-Werror",
+    "-fcolor-diagnostics",
+    # Default visibility to hidden, to enable dead stripping.
+    "-fvisibility=hidden",
+    # Warn for implicit type conversions that may change a value.
+    "-Wconversion",
+    "-Wno-c++11-compat",
+    # This (rightfully) complains about "override", which we use heavily.
+    "-Wno-c++11-extensions",
+    # Warns on switches on enums that cover all enum values but also contain a
+    # default: branch. Chrome is full of that.
+    "-Wno-covered-switch-default",
+    # protobuf uses hash_map.
+    "-Wno-deprecated",
+    "-fno-exceptions",
+    # Don't warn about the "struct foo f = {0};" initialization pattern.
+    "-Wno-missing-field-initializers",
+    # Do not warn for implicit sign conversions.
+    "-Wno-sign-conversion",
+    "-fno-strict-aliasing",  # See http://crbug.com/32204
+    # TODO(pkasting): In C++11 this is legal, so this should be removed when we
+    # change to that.  (This is also why we don't bother fixing all these cases
+    # today.)
+    "-Wno-unnamed-type-template-args",
+    # Triggered by the COMPILE_ASSERT macro.
+    "-Wno-unused-local-typedef",
+    # Do not warn if a function or variable cannot be implicitly
+    # instantiated.
+    "-Wno-undefined-var-template",
+  ]
+
+  defines = [
+    "__STDC_FORMAT_MACROS",
+  ]
+
+  cflags_cc = [
+    "-std=gnu++11",
+  ]
+}
+
+config("compiler_defaults_debug") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_devel") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_qa") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+config("compiler_defaults_gold") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+# =============================================================================
+# DELEGATED CONFIGS
+# =============================================================================
+
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+  ]
+}
+
+config("no_pedantic_warnings") {
+  cflags = [
+    # "this" pointer cannot be NULL...pointer may be assumed
+    # to always convert to true.
+    "-Wno-undefined-bool-conversion",
+    # Skia doesn't use overrides.
+    "-Wno-inconsistent-missing-override",
+    # Do not warn about unused function params.
+    "-Wno-unused-parameter",
+    # Do not warn for implicit type conversions that may change a value.
+    "-Wno-conversion",
+    # shifting a negative signed value is undefined
+    "-Wno-shift-negative-value",
+    # Width of bit-field exceeds width of its type- value will be truncated
+    "-Wno-bitfield-width",
+  ]
+}
+
+
+delegated_config("optimizations") {
+  path = "//starboard/build/toolchain/linux/config"
+  prefixes = [ "no", "debuggable", "full" ]
+  generate_default = false
+}
+
+config("default_optimizations") {
+  if (cobalt_config == "debug") {
+    configs = [ ":no_optimizations" ]
+  } else {
+    configs = [ ":debuggable_optimizations" ]
+  }
+}
+
+
+delegated_config("rtti") {
+  path = "//starboard/build/toolchain/linux/config"
+  generate_default = false
+}
+
+config("default_rtti") {
+  if (cobalt_config == "debug" || cobalt_config == "devel") {
+    configs = [ ":rtti" ]
+  } else {
+    configs = [ ":no_rtti" ]
+  }
+}
+
+
+config("wexit_time_destructors") {
+  configs = [ "//starboard/build/toolchain/linux/config:wexit_time_destructors" ]
+}
+
+# =============================================================================
+# starboard_platform TARGET
+# =============================================================================
+
+static_library("starboard_platform") {
+  sources = [
+    "//starboard/shared/starboard/application.cc",
+    "//starboard/shared/starboard/command_line.cc",
+    "//starboard/shared/starboard/command_line.h",
+    "//starboard/shared/starboard/event_cancel.cc",
+    "//starboard/shared/starboard/event_schedule.cc",
+    "//starboard/shared/starboard/file_mode_string_to_flags.cc",
+    "//starboard/shared/starboard/log_message.cc",
+    "//starboard/shared/starboard/player/filter/stub_player_components_impl.cc",
+    "//starboard/shared/starboard/queue_application.cc",
+    "//starboard/shared/stub/accessibility_get_display_settings.cc",
+    "//starboard/shared/stub/accessibility_get_text_to_speech_settings.cc",
+    "//starboard/shared/stub/atomic_public.h",
+    "//starboard/shared/stub/audio_sink_create.cc",
+    "//starboard/shared/stub/audio_sink_destroy.cc",
+    "//starboard/shared/stub/audio_sink_get_max_channels.cc",
+    "//starboard/shared/stub/audio_sink_get_nearest_supported_sample_frequency.cc",
+    "//starboard/shared/stub/audio_sink_is_audio_frame_storage_type_supported.cc",
+    "//starboard/shared/stub/audio_sink_is_audio_sample_type_supported.cc",
+    "//starboard/shared/stub/audio_sink_is_valid.cc",
+    "//starboard/shared/stub/byte_swap.cc",
+    "//starboard/shared/stub/character_is_alphanumeric.cc",
+    "//starboard/shared/stub/character_is_digit.cc",
+    "//starboard/shared/stub/character_is_hex_digit.cc",
+    "//starboard/shared/stub/character_is_space.cc",
+    "//starboard/shared/stub/character_is_upper.cc",
+    "//starboard/shared/stub/character_to_lower.cc",
+    "//starboard/shared/stub/character_to_upper.cc",
+    "//starboard/shared/stub/condition_variable_broadcast.cc",
+    "//starboard/shared/stub/condition_variable_create.cc",
+    "//starboard/shared/stub/condition_variable_destroy.cc",
+    "//starboard/shared/stub/condition_variable_signal.cc",
+    "//starboard/shared/stub/condition_variable_wait.cc",
+    "//starboard/shared/stub/condition_variable_wait_timed.cc",
+    "//starboard/shared/stub/cryptography_create_transformer.cc",
+    "//starboard/shared/stub/cryptography_destroy_transformer.cc",
+    "//starboard/shared/stub/cryptography_get_tag.cc",
+    "//starboard/shared/stub/cryptography_set_authenticated_data.cc",
+    "//starboard/shared/stub/cryptography_set_initialization_vector.cc",
+    "//starboard/shared/stub/cryptography_transform.cc",
+    "//starboard/shared/stub/directory_can_open.cc",
+    "//starboard/shared/stub/directory_close.cc",
+    "//starboard/shared/stub/directory_create.cc",
+    "//starboard/shared/stub/directory_get_next.cc",
+    "//starboard/shared/stub/directory_open.cc",
+    "//starboard/shared/stub/double_absolute.cc",
+    "//starboard/shared/stub/double_exponent.cc",
+    "//starboard/shared/stub/double_floor.cc",
+    "//starboard/shared/stub/double_is_finite.cc",
+    "//starboard/shared/stub/double_is_nan.cc",
+    "//starboard/shared/stub/drm_close_session.cc",
+    "//starboard/shared/stub/drm_create_system.cc",
+    "//starboard/shared/stub/drm_destroy_system.cc",
+    "//starboard/shared/stub/drm_generate_session_update_request.cc",
+    "//starboard/shared/stub/drm_system_internal.h",
+    "//starboard/shared/stub/drm_update_session.cc",
+    "//starboard/shared/stub/file_can_open.cc",
+    "//starboard/shared/stub/file_close.cc",
+    "//starboard/shared/stub/file_delete.cc",
+    "//starboard/shared/stub/file_exists.cc",
+    "//starboard/shared/stub/file_flush.cc",
+    "//starboard/shared/stub/file_get_info.cc",
+    "//starboard/shared/stub/file_get_path_info.cc",
+    "//starboard/shared/stub/file_open.cc",
+    "//starboard/shared/stub/file_read.cc",
+    "//starboard/shared/stub/file_seek.cc",
+    "//starboard/shared/stub/file_truncate.cc",
+    "//starboard/shared/stub/file_write.cc",
+    "//starboard/shared/stub/log.cc",
+    "//starboard/shared/stub/log_flush.cc",
+    "//starboard/shared/stub/log_format.cc",
+    "//starboard/shared/stub/log_is_tty.cc",
+    "//starboard/shared/stub/log_raw.cc",
+    "//starboard/shared/stub/log_raw_dump_stack.cc",
+    "//starboard/shared/stub/log_raw_format.cc",
+    "//starboard/shared/stub/media_can_play_mime_and_key_system.cc",
+    "//starboard/shared/stub/media_get_audio_configuration.cc",
+    "//starboard/shared/stub/media_get_audio_output_count.cc",
+    "//starboard/shared/stub/media_is_audio_supported.cc",
+    "//starboard/shared/stub/media_is_output_protected.cc",
+    "//starboard/shared/stub/media_is_supported.cc",
+    "//starboard/shared/stub/media_is_transfer_characteristics_supported.cc",
+    "//starboard/shared/stub/media_is_video_supported.cc",
+    "//starboard/shared/stub/media_set_output_protection.cc",
+    "//starboard/shared/stub/memory_allocate_aligned_unchecked.cc",
+    "//starboard/shared/stub/memory_allocate_unchecked.cc",
+    "//starboard/shared/stub/memory_compare.cc",
+    "//starboard/shared/stub/memory_copy.cc",
+    "//starboard/shared/stub/memory_find_byte.cc",
+    "//starboard/shared/stub/memory_flush.cc",
+    "//starboard/shared/stub/memory_free.cc",
+    "//starboard/shared/stub/memory_free_aligned.cc",
+    "//starboard/shared/stub/memory_get_stack_bounds.cc",
+    "//starboard/shared/stub/memory_map.cc",
+    "//starboard/shared/stub/memory_move.cc",
+    "//starboard/shared/stub/memory_reallocate_unchecked.cc",
+    "//starboard/shared/stub/memory_set.cc",
+    "//starboard/shared/stub/memory_unmap.cc",
+    "//starboard/shared/stub/microphone_close.cc",
+    "//starboard/shared/stub/microphone_create.cc",
+    "//starboard/shared/stub/microphone_destroy.cc",
+    "//starboard/shared/stub/microphone_get_available.cc",
+    "//starboard/shared/stub/microphone_is_sample_rate_supported.cc",
+    "//starboard/shared/stub/microphone_open.cc",
+    "//starboard/shared/stub/microphone_read.cc",
+    "//starboard/shared/stub/mutex_acquire.cc",
+    "//starboard/shared/stub/mutex_acquire_try.cc",
+    "//starboard/shared/stub/mutex_create.cc",
+    "//starboard/shared/stub/mutex_destroy.cc",
+    "//starboard/shared/stub/mutex_release.cc",
+    "//starboard/shared/stub/once.cc",
+    "//starboard/shared/stub/player_create.cc",
+    "//starboard/shared/stub/player_destroy.cc",
+    "//starboard/shared/stub/player_get_current_frame.cc",
+    "//starboard/shared/stub/player_get_info.cc",
+    "//starboard/shared/stub/player_output_mode_supported.cc",
+    "//starboard/shared/stub/player_seek.cc",
+    "//starboard/shared/stub/player_set_bounds.cc",
+    "//starboard/shared/stub/player_set_pause.cc",
+    "//starboard/shared/stub/player_set_playback_rate.cc",
+    "//starboard/shared/stub/player_set_volume.cc",
+    "//starboard/shared/stub/player_write_end_of_stream.cc",
+    "//starboard/shared/stub/player_write_sample.cc",
+    "//starboard/shared/stub/socket_accept.cc",
+    "//starboard/shared/stub/socket_bind.cc",
+    "//starboard/shared/stub/socket_clear_last_error.cc",
+    "//starboard/shared/stub/socket_connect.cc",
+    "//starboard/shared/stub/socket_create.cc",
+    "//starboard/shared/stub/socket_destroy.cc",
+    "//starboard/shared/stub/socket_free_resolution.cc",
+    "//starboard/shared/stub/socket_get_interface_address.cc",
+    "//starboard/shared/stub/socket_get_last_error.cc",
+    "//starboard/shared/stub/socket_get_local_address.cc",
+    "//starboard/shared/stub/socket_get_local_interface_address.cc",
+    "//starboard/shared/stub/socket_is_connected.cc",
+    "//starboard/shared/stub/socket_is_connected_and_idle.cc",
+    "//starboard/shared/stub/socket_join_multicast_group.cc",
+    "//starboard/shared/stub/socket_listen.cc",
+    "//starboard/shared/stub/socket_receive_from.cc",
+    "//starboard/shared/stub/socket_resolve.cc",
+    "//starboard/shared/stub/socket_send_to.cc",
+    "//starboard/shared/stub/socket_set_broadcast.cc",
+    "//starboard/shared/stub/socket_set_receive_buffer_size.cc",
+    "//starboard/shared/stub/socket_set_reuse_address.cc",
+    "//starboard/shared/stub/socket_set_send_buffer_size.cc",
+    "//starboard/shared/stub/socket_set_tcp_keep_alive.cc",
+    "//starboard/shared/stub/socket_set_tcp_no_delay.cc",
+    "//starboard/shared/stub/socket_set_tcp_window_scaling.cc",
+    "//starboard/shared/stub/socket_waiter_add.cc",
+    "//starboard/shared/stub/socket_waiter_create.cc",
+    "//starboard/shared/stub/socket_waiter_destroy.cc",
+    "//starboard/shared/stub/socket_waiter_remove.cc",
+    "//starboard/shared/stub/socket_waiter_wait.cc",
+    "//starboard/shared/stub/socket_waiter_wait_timed.cc",
+    "//starboard/shared/stub/socket_waiter_wake_up.cc",
+    "//starboard/shared/stub/speech_recognizer_cancel.cc",
+    "//starboard/shared/stub/speech_recognizer_create.cc",
+    "//starboard/shared/stub/speech_recognizer_destroy.cc",
+    "//starboard/shared/stub/speech_recognizer_start.cc",
+    "//starboard/shared/stub/speech_recognizer_stop.cc",
+    "//starboard/shared/stub/speech_synthesis_cancel.cc",
+    "//starboard/shared/stub/speech_synthesis_set_language.cc",
+    "//starboard/shared/stub/speech_synthesis_speak.cc",
+    "//starboard/shared/stub/storage_close_record.cc",
+    "//starboard/shared/stub/storage_delete_record.cc",
+    "//starboard/shared/stub/storage_get_record_size.cc",
+    "//starboard/shared/stub/storage_open_record.cc",
+    "//starboard/shared/stub/storage_read_record.cc",
+    "//starboard/shared/stub/storage_write_record.cc",
+    "//starboard/shared/stub/string_compare.cc",
+    "//starboard/shared/stub/string_compare_all.cc",
+    "//starboard/shared/stub/string_compare_no_case.cc",
+    "//starboard/shared/stub/string_compare_no_case_n.cc",
+    "//starboard/shared/stub/string_compare_wide.cc",
+    "//starboard/shared/stub/string_concat.cc",
+    "//starboard/shared/stub/string_concat_wide.cc",
+    "//starboard/shared/stub/string_copy.cc",
+    "//starboard/shared/stub/string_copy_wide.cc",
+    "//starboard/shared/stub/string_duplicate.cc",
+    "//starboard/shared/stub/string_find_character.cc",
+    "//starboard/shared/stub/string_find_last_character.cc",
+    "//starboard/shared/stub/string_find_string.cc",
+    "//starboard/shared/stub/string_format.cc",
+    "//starboard/shared/stub/string_format_wide.cc",
+    "//starboard/shared/stub/string_get_length.cc",
+    "//starboard/shared/stub/string_get_length_wide.cc",
+    "//starboard/shared/stub/string_parse_double.cc",
+    "//starboard/shared/stub/string_parse_signed_integer.cc",
+    "//starboard/shared/stub/string_parse_uint64.cc",
+    "//starboard/shared/stub/string_parse_unsigned_integer.cc",
+    "//starboard/shared/stub/string_scan.cc",
+    "//starboard/shared/stub/system_binary_search.cc",
+    "//starboard/shared/stub/system_break_into_debugger.cc",
+    "//starboard/shared/stub/system_clear_last_error.cc",
+    "//starboard/shared/stub/system_clear_platform_error.cc",
+    "//starboard/shared/stub/system_get_connection_type.cc",
+    "//starboard/shared/stub/system_get_device_type.cc",
+    "//starboard/shared/stub/system_get_error_string.cc",
+    "//starboard/shared/stub/system_get_last_error.cc",
+    "//starboard/shared/stub/system_get_locale_id.cc",
+    "//starboard/shared/stub/system_get_number_of_processors.cc",
+    "//starboard/shared/stub/system_get_path.cc",
+    "//starboard/shared/stub/system_get_property.cc",
+    "//starboard/shared/stub/system_get_random_data.cc",
+    "//starboard/shared/stub/system_get_random_uint64.cc",
+    "//starboard/shared/stub/system_get_stack.cc",
+    "//starboard/shared/stub/system_get_total_cpu_memory.cc",
+    "//starboard/shared/stub/system_get_total_gpu_memory.cc",
+    "//starboard/shared/stub/system_get_used_cpu_memory.cc",
+    "//starboard/shared/stub/system_get_used_gpu_memory.cc",
+    "//starboard/shared/stub/system_has_capability.cc",
+    "//starboard/shared/stub/system_hide_splash_screen.cc",
+    "//starboard/shared/stub/system_is_debugger_attached.cc",
+    "//starboard/shared/stub/system_raise_platform_error.cc",
+    "//starboard/shared/stub/system_request_pause.cc",
+    "//starboard/shared/stub/system_request_stop.cc",
+    "//starboard/shared/stub/system_request_suspend.cc",
+    "//starboard/shared/stub/system_request_unpause.cc",
+    "//starboard/shared/stub/system_sort.cc",
+    "//starboard/shared/stub/system_symbolize.cc",
+    "//starboard/shared/stub/thread_create.cc",
+    "//starboard/shared/stub/thread_create_local_key.cc",
+    "//starboard/shared/stub/thread_destroy_local_key.cc",
+    "//starboard/shared/stub/thread_detach.cc",
+    "//starboard/shared/stub/thread_get_current.cc",
+    "//starboard/shared/stub/thread_get_id.cc",
+    "//starboard/shared/stub/thread_get_local_value.cc",
+    "//starboard/shared/stub/thread_get_name.cc",
+    "//starboard/shared/stub/thread_is_equal.cc",
+    "//starboard/shared/stub/thread_join.cc",
+    "//starboard/shared/stub/thread_set_local_value.cc",
+    "//starboard/shared/stub/thread_set_name.cc",
+    "//starboard/shared/stub/thread_sleep.cc",
+    "//starboard/shared/stub/thread_types_public.h",
+    "//starboard/shared/stub/thread_yield.cc",
+    "//starboard/shared/stub/time_get_monotonic_now.cc",
+    "//starboard/shared/stub/time_get_monotonic_thread_now.cc",
+    "//starboard/shared/stub/time_get_now.cc",
+    "//starboard/shared/stub/time_zone_get_current.cc",
+    "//starboard/shared/stub/time_zone_get_dst_name.cc",
+    "//starboard/shared/stub/time_zone_get_name.cc",
+    "//starboard/shared/stub/user_get_current.cc",
+    "//starboard/shared/stub/user_get_property.cc",
+    "//starboard/shared/stub/user_get_signed_in.cc",
+    "//starboard/shared/stub/window_create.cc",
+    "//starboard/shared/stub/window_destroy.cc",
+    "//starboard/shared/stub/window_get_platform_handle.cc",
+    "//starboard/shared/stub/window_get_size.cc",
+    "//starboard/shared/stub/window_set_default_options.cc",
+    "application_stub.cc",
+    "application_stub.h",
+    "atomic_public.h",
+    "main.cc",
+    "thread_types_public.h",
+  ]
+
+  # Include private stubs, if present.
+  private_sources = exec_script("//starboard/tools/find_private_files.py",
+                                [
+                                  rebase_path("//", root_build_dir),
+                                  "shared/stub/*.cc",
+                                ],
+                                "list lines",
+                                [ "//starboard/private/shared/stub" ])
+  sources += rebase_path(private_sources, ".", root_build_dir)
+
+  defines = [ "STARBOARD_IMPLEMENTATION" ]
+}
diff --git a/src/starboard/stub/buildconfig.gni b/src/starboard/stub/buildconfig.gni
new file mode 100644
index 0000000..8e4bdee
--- /dev/null
+++ b/src/starboard/stub/buildconfig.gni
@@ -0,0 +1,36 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+###############################################################################
+# This file is imported into BUILDCONFIG.gn. As a side effect, any variable
+# defined here becomes a global variable in all GN files.
+#
+# Adding new variables to this file could result in variable name conflicts, so
+# keep the number of variables in this file to a minimum!
+#
+# If you need to define a temporary variable, prefix it with an underscore like
+# _this. Prefixing a variable with an underscore makes it a private variable
+# that won't bleed into other files.
+###############################################################################
+
+# Target OS and CPU.
+# BUILDCONFIG.gn sets target_os and target_cpu to the values of these two
+# variables below. Unfortunately, due to GN's restrictions on how variables can
+# be changed, we can't directly set target_os and target_cpu here.
+target_os_ = "linux"
+target_cpu_ = "x64"
+
+# The target and host toolchain
+target_toolchain = "//starboard/build/toolchain/linux:clang_x64"
+host_toolchain = "//starboard/build/toolchain/linux:clang_$host_cpu"
diff --git a/src/starboard/stub/configuration.gni b/src/starboard/stub/configuration.gni
new file mode 100644
index 0000000..55a886e
--- /dev/null
+++ b/src/starboard/stub/configuration.gni
@@ -0,0 +1,29 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Use a stub rasterizer and graphical setup
+rasterizer_type = "stub"
+
+# No GL drivers available
+gl_type = "none"
+
+# Use media source extension implementation that is conformed to the
+# Candidate Recommandation of July 5th 2016.
+cobalt_use_media_source_2016 = true
+
+declare_args() {
+  # Set to true to enable distributed compilation using Goma. By default we
+  # use Goma for stub and linux.
+  use_goma = true
+}
diff --git a/src/starboard/tools/abstract_launcher.py b/src/starboard/tools/abstract_launcher.py
index 65bc1ea..a840cde 100644
--- a/src/starboard/tools/abstract_launcher.py
+++ b/src/starboard/tools/abstract_launcher.py
@@ -1,4 +1,5 @@
-#!/usr/bin/python
+#
+# Copyright 2017 Google Inc. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -10,59 +11,25 @@
 # 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."""
+# limitations under the License.
 """Abstraction for running Cobalt development tools."""
 
-import abc
-import imp
+import importlib
 import os
-import re
 import sys
 
-platform_module = imp.load_source(
-    "platform", os.path.abspath(
-        os.path.join(os.path.dirname(__file__), "platform.py")))
+if "environment" in sys.modules:
+  environment = sys.modules["environment"]
+else:
+  env_path = os.path.abspath(os.path.dirname(__file__))
+  if env_path not in sys.path:
+    sys.path.append(env_path)
+  environment = importlib.import_module("environment")
 
 
-def _GetAllPlatforms(path):
-  """Retrieves information about all available Cobalt ports.
+import abc
 
-  Args:
-    path:  Root path that will be crawled to find ports.
-
-  Returns:
-    Dict mapping available cobalt ports to their respective location in
-    the filesystem.
-  """
-  platform_dict = {}
-  for port in platform_module.PlatformInfo.EnumeratePorts(path):
-    port_name = re.search(".*starboard-(.*)", port.port_name).group(1)
-    platform_dict[port_name] = port.path
-  return platform_dict
-
-
-def _GetProjectRoot():
-  """Gets the root of this project.
-
-  Returns:
-    Path to the root of this project
-
-  Raises:
-    RuntimeError: There is no root.
-  """
-  current_path = os.path.normpath(os.path.dirname(__file__))
-  while not os.access(os.path.join(current_path, ".gclient"), os.R_OK):
-    next_path = os.path.dirname(current_path)
-    if next_path == current_path:
-      current_path = None
-      break
-    current_path = next_path
-  client_root = current_path
-
-  if not client_root:
-    raise RuntimeError("No project root declared.")
-
-  return client_root
+import starboard.tools.platform as platform_module
 
 
 def _GetLauncherForPlatform(platform_path):
@@ -74,23 +41,9 @@
   Returns:
     The module containing the platform's launcher implementation.
   """
-
-  # Necessary because gyp_configuration modules use relative imports.
-  # "cobalt/build" needs to be in sys.path to keep the imports working
-  sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                               os.pardir, os.pardir,
-                                               "cobalt", "build")))
-
-  # Necessary because the gyp_configuration for linux-x64x11 imports
-  # directly from "starboard/".  All of this import logic will eventually be
-  # moved to a configuration system.
-  sys.path.append(os.path.abspath(os.path.join(
-      os.path.dirname(__file__), os.pardir, os.pardir)))
-
-  module_path = os.path.abspath(os.path.join(platform_path,
-                                             "gyp_configuration.py"))
-
-  gyp_module = imp.load_source("platform_module", module_path)
+  if platform_path not in sys.path:
+    sys.path.append(platform_path)
+  gyp_module = importlib.import_module("gyp_configuration")
   return gyp_module.CreatePlatformConfig().GetLauncher()
 
 
@@ -112,8 +65,7 @@
   """
 
   #  Creates launcher for provided platform if the platform has a valid port
-  client_root = _GetProjectRoot()
-  platform_dict = _GetAllPlatforms(client_root)
+  platform_dict = platform_module.GetAllPorts()
   if platform in platform_dict:
     platform_path = platform_dict[platform]
     launcher_module = _GetLauncherForPlatform(platform_path)
@@ -140,7 +92,11 @@
 
   @abc.abstractmethod
   def Run(self):
-    """Runs the launcher's executable.  Must be implemented in subclasses."""
+    """Runs the launcher's executable.  Must be implemented in subclasses.
+
+    Returns:
+      The return code from the launcher's executable.
+    """
     pass
 
   @abc.abstractmethod
diff --git a/src/starboard/tools/environment.py b/src/starboard/tools/environment.py
new file mode 100644
index 0000000..e792803
--- /dev/null
+++ b/src/starboard/tools/environment.py
@@ -0,0 +1,94 @@
+#
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License."""
+"""Sets up a Starboard environment for use by the host application.
+
+Applications that use Starboard should contain a Python file, called
+"starboard_configuration.py", somewhere in the ancestor path of their
+"starboard" directory.
+
+The file should contain a variable, called PORT_ROOTS. Assign to it a
+list of paths to directories in which Starboard ports are located.
+Each path should be represented as a list of directory names whose locations
+are relative to the configuration file.  Example:
+
+PORT_ROOTS = [
+    ["some_directory", "starboard"]
+    ["somewhere_else", "starboard"]
+]
+
+IF YOU ARE GOING TO USE STARBOARD FILES OUTSIDE OF STARBOARD, YOU NEED TO IMPORT
+THIS MODULE FIRST.  Otherwise, sys.path will not be configured properly, none of
+your code will work, and you'll be sad.
+"""
+
+import importlib
+import os
+import sys
+
+PATHS = [
+    # Necessary because the gyp_configuration for linux-x64x11 imports
+    # directly from "starboard/".
+    os.path.abspath(os.path.join(
+        os.path.dirname(__file__), os.pardir, os.pardir)),
+
+    # Necessary because gyp_configuration modules use relative imports.
+    # "cobalt/build" needs to be in sys.path to keep the imports working
+    os.path.abspath(os.path.join(
+        os.path.dirname(__file__), os.pardir, os.pardir,
+        "cobalt", "build"))
+]
+
+# This module should only add the above paths to sys.path when they are not
+# present in the list already.
+for new_path in PATHS:
+  if new_path not in sys.path:
+    sys.path.append(new_path)
+
+
+def _GetStarboardConfigPath():
+  """Gets the path to the "starboard_configuration.py" file from the host app.
+
+  Returns:
+    Path to the "starboard_configuration.py" file from the host app
+
+  Raises:
+    RuntimeError: There is no configuration file.
+  """
+  current_path = os.path.normpath(os.path.dirname(__file__))
+  while not os.path.exists(os.path.join(current_path,
+                                        "starboard_configuration.py")):
+    next_path = os.path.dirname(current_path)
+    if next_path == current_path:
+      current_path = None
+      break
+    current_path = next_path
+  config_path = current_path
+
+  if not config_path:
+    raise RuntimeError("No starboard configuration declared.")
+
+  return config_path
+
+
+def GetStarboardPortRoots():
+  """Gets paths to the host app's Starboard port directories."""
+  config_path = _GetStarboardConfigPath()
+  sys.path.append(config_path)
+  config_module = importlib.import_module("starboard_configuration")
+  port_roots = config_module.PORT_ROOTS
+  port_root_paths = [os.path.abspath(os.path.join(
+      config_path, os.sep.join(root))) for root in port_roots]
+  return port_root_paths
+
diff --git a/src/starboard/tools/platform.py b/src/starboard/tools/platform.py
index 7c760f8..76e3438 100644
--- a/src/starboard/tools/platform.py
+++ b/src/starboard/tools/platform.py
@@ -16,8 +16,19 @@
 """Functionality to enumerate and represent starboard ports."""
 
 import importlib
-import logging
 import os
+import sys
+
+if "environment" in sys.modules:
+  environment = sys.modules["environment"]
+else:
+  env_path = os.path.abspath(os.path.dirname(__file__))
+  if env_path not in sys.path:
+    sys.path.append(env_path)
+  environment = importlib.import_module("environment")
+
+
+import logging
 import re
 
 
@@ -61,11 +72,37 @@
   return re.sub(r'[^a-zA-Z0-9_]', r'-', directory[start:])
 
 
+def _GetAllPlatforms(port_root_paths):
+  """Retrieves information about all available Cobalt ports.
+
+  Args:
+    port_root_paths:  List of paths that will be crawled to find ports.
+
+  Returns:
+    Dict mapping each available port to its location in the filesystem.
+  """
+  platform_dict = {}
+  for path in port_root_paths:
+    for port in PlatformInfo.EnumeratePorts(path):
+      platform_dict[port.port_name] = port.path
+  return platform_dict
+
+
+def GetAllPorts():
+  """Gets all available starboard ports from the host app.
+
+  Returns:
+    Dictionary mapping port names to their path in the filesystem.
+  """
+  port_root_paths = environment.GetStarboardPortRoots()
+  return _GetAllPlatforms(port_root_paths)
+
+
 class PlatformInfo(object):
   """Information about a specific starboard port."""
 
   @classmethod
-  def EnumeratePorts(cls, root_path, exclusion_set = None):
+  def EnumeratePorts(cls, root_path, exclusion_set=None):
     """Generator that iterates over starboard ports found under |path|."""
     if not exclusion_set:
       exclusion_set = set()
diff --git a/src/starboard/win/lib/gyp_configuration.gypi b/src/starboard/win/lib/gyp_configuration.gypi
index 0dec384..e491c37 100644
--- a/src/starboard/win/lib/gyp_configuration.gypi
+++ b/src/starboard/win/lib/gyp_configuration.gypi
@@ -16,6 +16,16 @@
   'variables': {
     'javascript_engine': 'mozjs',
     'cobalt_enable_jit': 0,
+    # TODO: In theory, there are tools that can combine static libraries into
+    # thick static libraries with all their transitive dependencies. Using
+    # shared_library here can have unexpected consequences. Explore building
+    # this into a thick static library instead.
+    'final_executable_type': 'shared_library',
+    'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer',
+    'sb_enable_lib': 1,
+    'angle_build_winrt': 0,
+    'winrt': 0,
+    'enable_d3d11_feature_level_11': 1,
   },
   'includes': [
     '../shared/gyp_configuration.gypi',
@@ -23,25 +33,18 @@
   'target_defaults': {
     'default_configuration': 'win-lib_debug',
     'configurations': {
-     'lib_base': {
-       'abstract': 1,
-       'msvs_settings': {
-         'VCLinkerTool': {
-           'SubSystem': '2', # WINDOWS
-         }
-       }
-      },
+
       'win-lib_debug': {
-        'inherit_from': ['msvs_debug', 'lib_base'],
+        'inherit_from': ['win32_base', 'msvs_debug'],
       },
       'win-lib_devel': {
-       'inherit_from': ['msvs_devel', 'lib_base'],
+       'inherit_from': ['win32_base', 'msvs_devel'],
       },
       'win-lib_qa': {
-        'inherit_from': ['msvs_qa', 'lib_base'],
+        'inherit_from': ['win32_base', 'msvs_qa'],
       },
       'win-lib_gold': {
-        'inherit_from': ['msvs_gold', 'lib_base'],
+        'inherit_from': ['win32_base', 'msvs_gold'],
       },
     },  # end of configurations
   },
diff --git a/src/starboard/win/lib/main.cc b/src/starboard/win/lib/main.cc
deleted file mode 100644
index 2ab7084..0000000
--- a/src/starboard/win/lib/main.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <windows.h>
-
-#include <WinSock2.h>
-
-#include <string>
-#include <vector>
-
-#include "starboard/configuration.h"
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/shared/win32/thread_private.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-using starboard::shared::win32::wchar_tToUTF8;
-
-// TODO: Share more of this logic with xb1 & win-console?
-int main(Platform::Array<Platform::String ^> ^ args) {
-  if (!IsDebuggerPresent()) {
-    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
-  }
-
-  const int kWinSockVersionMajor = 2;
-  const int kWinSockVersionMinor = 2;
-  WSAData wsaData;
-  int init_result = WSAStartup(
-      MAKEWORD(kWinSockVersionMajor, kWinSockVersionMajor), &wsaData);
-
-  SB_CHECK(init_result == 0);
-  // WSAStartup returns the highest version that is supported up to the version
-  // we request.
-  SB_CHECK(LOBYTE(wsaData.wVersion) == kWinSockVersionMajor &&
-           HIBYTE(wsaData.wVersion) == kWinSockVersionMinor);
-
-  starboard::shared::win32::RegisterMainThread();
-
-  std::vector<std::string> string_args;
-  for (auto it = args->begin(); it != args->end(); ++it) {
-    Platform::String ^ s = *it;
-    string_args.push_back(wchar_tToUTF8(s->Data(), s->Length()));
-  }
-
-  std::vector<const char*> utf8_args;
-  for (auto it = string_args.begin(); it != string_args.end(); ++it) {
-    utf8_args.push_back(it->data());
-  }
-
-  starboard::shared::uwp::ApplicationUwp application;
-  int return_value = application.Run(static_cast<int>(utf8_args.size()),
-                                     const_cast<char**>(utf8_args.data()));
-
-  WSACleanup();
-
-  return return_value;
-}
diff --git a/src/starboard/win/lib/starboard_platform.gyp b/src/starboard/win/lib/starboard_platform.gyp
index d61cc40..04fd716 100644
--- a/src/starboard/win/lib/starboard_platform.gyp
+++ b/src/starboard/win/lib/starboard_platform.gyp
@@ -14,7 +14,6 @@
 {
   'includes': [
     '../shared/starboard_platform.gypi',
-    '../../shared/uwp/starboard_platform.gypi'
   ],
   'variables': {
     'starboard_platform_dependent_files': [
@@ -22,6 +21,21 @@
       'configuration_public.h',
       'thread_types_public.h',
       '../shared/system_get_path.cc',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
+      '<@(uwp_incompatible_win32)',
+      '<@(stub_media_player)'
     ],
-  }
+  },
 }
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi
index 7134bee..43ec50b 100644
--- a/src/starboard/win/shared/starboard_platform.gypi
+++ b/src/starboard/win/shared/starboard_platform.gypi
@@ -17,6 +17,16 @@
     'winrt%': 1,
     'stub_media_player': [
     ],
+
+    'stub_drm_system': [
+      '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+      '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
+    ],
+
     # TODO: Move this and the win32 dependencies below to a shared/win32/starboard_platform.gypi?
     'uwp_incompatible_win32': [
       '<(DEPTH)/starboard/shared/win32/application_win32_key_event.cc',
@@ -27,6 +37,8 @@
       '<(DEPTH)/starboard/shared/win32/system_clear_platform_error.cc',
       '<(DEPTH)/starboard/shared/win32/system_get_device_type.cc',
       '<(DEPTH)/starboard/shared/win32/system_get_property.cc',
+      '<(DEPTH)/starboard/shared/win32/system_get_total_cpu_memory.cc',
+      '<(DEPTH)/starboard/shared/win32/system_get_used_cpu_memory.cc',
       '<(DEPTH)/starboard/shared/win32/system_raise_platform_error.cc',
       '<(DEPTH)/starboard/shared/win32/window_create.cc',
       '<(DEPTH)/starboard/shared/win32/window_destroy.cc',
@@ -226,12 +238,6 @@
         '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
-        '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
         '<(DEPTH)/starboard/shared/stub/image_decode.cc',
         '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
         '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
@@ -334,8 +340,6 @@
         '<(DEPTH)/starboard/shared/win32/system_get_connection_type.cc',
         '<(DEPTH)/starboard/shared/win32/system_get_error_string.cc',
         '<(DEPTH)/starboard/shared/win32/system_get_number_of_processors.cc',
-        '<(DEPTH)/starboard/shared/win32/system_get_total_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/win32/system_get_used_cpu_memory.cc',
         '<(DEPTH)/starboard/shared/win32/socket_set_broadcast.cc',
         '<(DEPTH)/starboard/shared/win32/socket_set_receive_buffer_size.cc',
         '<(DEPTH)/starboard/shared/win32/socket_set_reuse_address.cc',
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/win/win32/lib/atomic_public.h
similarity index 81%
rename from src/starboard/win/lib/atomic_public.h
rename to src/starboard/win/win32/lib/atomic_public.h
index be4e805..b85fb12 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/win/win32/lib/atomic_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
+#define STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
 
 #include "starboard/shared/win32/atomic_public.h"
 
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/win/lib/configuration_public.h b/src/starboard/win/win32/lib/configuration_public.h
similarity index 82%
rename from src/starboard/win/lib/configuration_public.h
rename to src/starboard/win/win32/lib/configuration_public.h
index 800aeb1..8b5f8b1 100644
--- a/src/starboard/win/lib/configuration_public.h
+++ b/src/starboard/win/win32/lib/configuration_public.h
@@ -15,9 +15,9 @@
 // Other source files should never include this header directly, but should
 // include the generic "starboard/configuration.h" instead.
 
-#ifndef STARBOARD_WIN_LIB_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_WIN_LIB_CONFIGURATION_PUBLIC_H_
+#ifndef STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
 
 #include "starboard/win/shared/configuration_public.h"
 
-#endif  // STARBOARD_WIN_LIB_CONFIGURATION_PUBLIC_H_
+#endif  // STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/win/win32/lib/gyp_configuration.gypi b/src/starboard/win/win32/lib/gyp_configuration.gypi
new file mode 100644
index 0000000..7d8ddb2
--- /dev/null
+++ b/src/starboard/win/win32/lib/gyp_configuration.gypi
@@ -0,0 +1,51 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 0,
+    # TODO: In theory, there are tools that can combine static libraries into
+    # thick static libraries with all their transitive dependencies. Using
+    # shared_library here can have unexpected consequences. Explore building
+    # this into a thick static library instead.
+    'final_executable_type': 'shared_library',
+    'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer',
+    'sb_enable_lib': 1,
+    'angle_build_winrt': 0,
+    'winrt': 0,
+    'enable_d3d11_feature_level_11': 1,
+  },
+  'includes': [
+    '../../shared/gyp_configuration.gypi',
+  ],
+  'target_defaults': {
+    'default_configuration': 'win-win32-lib_debug',
+    'configurations': {
+
+      'win-win32-lib_debug': {
+        'inherit_from': ['win32_base', 'msvs_debug'],
+      },
+      'win-win32-lib_devel': {
+       'inherit_from': ['win32_base', 'msvs_devel'],
+      },
+      'win-win32-lib_qa': {
+        'inherit_from': ['win32_base', 'msvs_qa'],
+      },
+      'win-win32-lib_gold': {
+        'inherit_from': ['win32_base', 'msvs_gold'],
+      },
+    },  # end of configurations
+  },
+}
diff --git a/src/starboard/win/lib/gyp_configuration.py b/src/starboard/win/win32/lib/gyp_configuration.py
similarity index 88%
rename from src/starboard/win/lib/gyp_configuration.py
rename to src/starboard/win/win32/lib/gyp_configuration.py
index e136082..0d1deeb 100644
--- a/src/starboard/win/lib/gyp_configuration.py
+++ b/src/starboard/win/win32/lib/gyp_configuration.py
@@ -21,13 +21,13 @@
     os.path.realpath(
         os.path.join(
             os.path.dirname(__file__), os.pardir,
-            os.pardir, 'shared', 'win32')))
+            os.pardir, os.pardir, 'shared', 'win32')))
 import gyp_configuration
 
 
 def CreatePlatformConfig():
   try:
-    return gyp_configuration.PlatformConfig('win-lib')
+    return gyp_configuration.PlatformConfig('win-win32-lib')
   except RuntimeError as e:
     logging.critical(e)
     return None
diff --git a/src/starboard/win/win32/lib/starboard_platform.gyp b/src/starboard/win/win32/lib/starboard_platform.gyp
new file mode 100644
index 0000000..7216017
--- /dev/null
+++ b/src/starboard/win/win32/lib/starboard_platform.gyp
@@ -0,0 +1,23 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../starboard_platform.gypi',
+  ],
+  'variables': {
+    'starboard_platform_dependent_files': [
+      '<@(base_win32_starboard_platform_dependent_files)',
+    ]
+  },
+}
diff --git a/src/starboard/win/lib/starboard_platform_tests.gyp b/src/starboard/win/win32/lib/starboard_platform_tests.gyp
similarity index 96%
rename from src/starboard/win/lib/starboard_platform_tests.gyp
rename to src/starboard/win/win32/lib/starboard_platform_tests.gyp
index 1eb83ba..8961deb 100644
--- a/src/starboard/win/lib/starboard_platform_tests.gyp
+++ b/src/starboard/win/win32/lib/starboard_platform_tests.gyp
@@ -38,7 +38,7 @@
       'variables': {
         'executable_name': 'starboard_platform_tests',
       },
-      'includes': [ '../../build/deploy.gypi' ],
+      'includes': [ '../../../build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/win/lib/thread_types_public.h b/src/starboard/win/win32/lib/thread_types_public.h
similarity index 81%
rename from src/starboard/win/lib/thread_types_public.h
rename to src/starboard/win/win32/lib/thread_types_public.h
index 0f8f791..adc6221 100644
--- a/src/starboard/win/lib/thread_types_public.h
+++ b/src/starboard/win/win32/lib/thread_types_public.h
@@ -14,9 +14,9 @@
 
 // Includes threading primitive types and initializers.
 
-#ifndef STARBOARD_WIN_LIB_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_WIN_LIB_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
 
 #include "starboard/shared/win32/thread_types_public.h"
 
-#endif  // STARBOARD_WIN_LIB_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/win/win32/starboard_platform.gyp b/src/starboard/win/win32/starboard_platform.gyp
index 9373b0a..2027d45 100644
--- a/src/starboard/win/win32/starboard_platform.gyp
+++ b/src/starboard/win/win32/starboard_platform.gyp
@@ -1,40 +1,24 @@
-# Copyright 2017 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-{
-  'includes': [
-        '../shared/starboard_platform.gypi',
-    ],
-    'variables': {
-      'starboard_platform_dependent_files': [
-        'atomic_public.h',
-        'configuration_public.h',
-        'thread_types_public.h',
-        '../shared/system_get_path.cc',
-        'main.cc',
-        '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
-        '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
-        '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
-        '<@(uwp_incompatible_win32)',
-        '<@(stub_media_player)'
-      ]
-    },
-  }
+# Copyright 2017 Google Inc. All Rights Reserved.

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+{

+  'includes': [

+    'starboard_platform.gypi',

+  ],

+  'variables': {

+    'starboard_platform_dependent_files': [

+      'main.cc',

+      '<@(base_win32_starboard_platform_dependent_files)',

+    ]

+  },

+}

diff --git a/src/starboard/win/win32/starboard_platform.gypi b/src/starboard/win/win32/starboard_platform.gypi
new file mode 100644
index 0000000..254ae7b
--- /dev/null
+++ b/src/starboard/win/win32/starboard_platform.gypi
@@ -0,0 +1,42 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../shared/starboard_platform.gypi',
+  ],
+  'variables': {
+    'base_win32_starboard_platform_dependent_files': [
+      'atomic_public.h',
+      'configuration_public.h',
+      'thread_types_public.h',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
+      '<(DEPTH)/starboard/win/shared/system_get_path.cc',
+      '<@(uwp_incompatible_win32)',
+      '<@(stub_media_player)',
+      '<@(stub_drm_system)' ,
+    ],
+  },
+}
diff --git a/src/starboard_configuration.py b/src/starboard_configuration.py
new file mode 100644
index 0000000..36eb366
--- /dev/null
+++ b/src/starboard_configuration.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+#
+# 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."""
+"""Definition of interface used by host apps to configure Starboard"""
+
+# List of paths to directories in which Starboard ports are located.
+# Each path should be represented as a list of directory names whose locations
+# are relative to the location of this file.
+PORT_ROOTS = [
+    ["starboard"],
+    ["third_party", "starboard"]
+]
diff --git a/src/testing/gmock/BUILD.gn b/src/testing/gmock/BUILD.gn
new file mode 100644
index 0000000..1d27016
--- /dev/null
+++ b/src/testing/gmock/BUILD.gn
@@ -0,0 +1,53 @@
+# Copyright 2014 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("gmock_direct_config") {
+  include_dirs = [ "include" ]
+}
+
+static_library("gmock") {
+  testonly = true
+  sources = [
+    "include/gmock/gmock-actions.h",
+    "include/gmock/gmock-cardinalities.h",
+    "include/gmock/gmock-generated-actions.h",
+    "include/gmock/gmock-generated-function-mockers.h",
+    "include/gmock/gmock-generated-matchers.h",
+    "include/gmock/gmock-generated-nice-strict.h",
+    "include/gmock/gmock-matchers.h",
+    "include/gmock/gmock-spec-builders.h",
+    "include/gmock/gmock.h",
+    "include/gmock/internal/gmock-generated-internal-utils.h",
+    "include/gmock/internal/gmock-internal-utils.h",
+    "include/gmock/internal/gmock-port.h",
+    "src/gmock-cardinalities.cc",
+    "src/gmock-internal-utils.cc",
+    "src/gmock-matchers.cc",
+    "src/gmock-spec-builders.cc",
+    "src/gmock.cc",
+    "../gmock_mutant.h",
+  ]
+
+  include_dirs = [ "." ]
+
+  public_configs = [ ":gmock_direct_config" ]
+
+  public_deps = [
+    "//testing/gtest",
+  ]
+}
diff --git a/src/testing/gtest/BUILD.gn b/src/testing/gtest/BUILD.gn
new file mode 100644
index 0000000..0324adf
--- /dev/null
+++ b/src/testing/gtest/BUILD.gn
@@ -0,0 +1,78 @@
+# Copyright 2014 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("gtest_direct_config") {
+  include_dirs = [ "include" ]
+
+  defines = [
+    "UNIT_TEST",
+    "GTEST_HAS_POSIX_RE=0",
+  ]
+  if (target_cpu != "ps4" && target_os != "win") {
+    defines += [ "GTEST_USE_OWN_TR1_TUPLE=1" ]
+  }
+}
+
+static_library("gtest") {
+  testonly = true
+  sources = [
+    "include/gtest/gtest-death-test.h",
+    "include/gtest/gtest-message.h",
+    "include/gtest/gtest-param-test.h",
+    "include/gtest/gtest-printers.h",
+    "include/gtest/gtest-spi.h",
+    "include/gtest/gtest-test-part.h",
+    "include/gtest/gtest-typed-test.h",
+    "include/gtest/gtest.h",
+    "include/gtest/gtest_pred_impl.h",
+    "include/gtest/internal/gtest-death-test-internal.h",
+    "include/gtest/internal/gtest-filepath.h",
+    "include/gtest/internal/gtest-internal.h",
+    "include/gtest/internal/gtest-linked_ptr.h",
+    "include/gtest/internal/gtest-param-util-generated.h",
+    "include/gtest/internal/gtest-param-util.h",
+    "include/gtest/internal/gtest-port.h",
+    "include/gtest/internal/gtest-string.h",
+    "include/gtest/internal/gtest-tuple.h",
+    "include/gtest/internal/gtest-type-util.h",
+    "src/gtest-death-test.cc",
+    "src/gtest-filepath.cc",
+    "src/gtest-internal-inl.h",
+    "src/gtest-port.cc",
+    "src/gtest-printers.cc",
+    "src/gtest-test-part.cc",
+    "src/gtest-typed-test.cc",
+    "src/gtest.cc",
+    "../multiprocess_func_list.cc",
+    "../multiprocess_func_list.h",
+    "../platform_test.h",
+  ]
+
+  include_dirs = [ "." ]
+
+  public_configs = [ ":gtest_direct_config" ]
+
+  # Ideally we would want to remove
+  # //starboard/build/config:default_rtti from the list of configs too.
+  # Unfortunately GN doesn't provide us a way to remove things from
+  # dependents' configs list.
+  # It happens to be that all_dependent_configs get processed after default
+  # configs, so the -frtti flag comes after the -fno-rtti flag on the command
+  # line, so it happens to work in this case.
+  all_dependent_configs = [ "//starboard/build/config:rtti" ]
+}
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.h b/src/third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.h
index 7ae203d..bf0323e 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.h
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.h
@@ -218,7 +218,7 @@
     virtual gl::Error generateMipmapUsingD3D(TextureStorage *storage,
                                              const gl::TextureState &textureState) = 0;
     virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0;
-    virtual TextureStorage *createTextureStorage2D(IUnknown *texture, bool bindChroma, UINT arrayIndex) = 0;
+    virtual TextureStorage *createTextureStorage2D(IUnknown *texture, bool bindChroma, IUnknown *dxgiBuffer) = 0;
     virtual TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage,
                                                          RenderTargetD3D *renderTargetD3D) = 0;
     virtual TextureStorage *createTextureStorageExternal(
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
index 2d0e74e..4f10d28 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
@@ -76,7 +76,7 @@
       mShareHandle(0),
       mD3DTexture(nullptr),
       mBuftype(buftype),
-      mArrayIndex(0),
+      mDXGIBuffer(nullptr),
       mBindChroma(false)
 {
     if (window != nullptr && !mFixedSize)
@@ -104,14 +104,12 @@
                 GetPrivateData(kCobaltNv12BindChroma, &out, nullptr);
             mBindChroma = (SUCCEEDED(hr)) && (out != 0);
 
-            // kCobaltDxgiBuffer
-            IMFDXGIBuffer* dxgi_buffer = nullptr;
-            out = sizeof(dxgi_buffer);
+            out = sizeof(mDXGIBuffer);
             hr = static_cast<ID3D11DeviceChild*>(mD3DTexture)->
-                GetPrivateData(kCobaltDxgiBuffer, &out, &dxgi_buffer);
+                GetPrivateData(kCobaltDxgiBuffer, &out, &mDXGIBuffer);
             ASSERT(SUCCEEDED(hr));
-            if (dxgi_buffer != nullptr) {
-              dxgi_buffer->GetSubresourceIndex(&mArrayIndex);
+            if (mDXGIBuffer != nullptr) {
+              mDXGIBuffer->AddRef();
             }
             break;
         }
@@ -126,6 +124,7 @@
     releaseSwapChain();
     SafeDelete(mNativeWindow);
     SafeRelease(mD3DTexture);
+    SafeRelease(mDXGIBuffer);
 }
 
 void SurfaceD3D::releaseSwapChain()
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h b/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h
index b698228..9d6f39a 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h
@@ -67,9 +67,9 @@
       return mBindChroma;
     }
 
-    UINT getArrayIndex() const
+    IUnknown* getDxgiBuffer() const
     {
-      return mArrayIndex;
+      return mDXGIBuffer;
     }
 
   protected:
@@ -106,7 +106,7 @@
     HANDLE mShareHandle;
     IUnknown *mD3DTexture;
     EGLenum mBuftype;
-    UINT mArrayIndex;
+    IUnknown *mDXGIBuffer;
     bool mBindChroma;
 };
 
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp
index e197dc9..fc7d6d1 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp
@@ -1177,7 +1177,7 @@
     if (surfaceD3D->getSwapChain() == nullptr)
         mTexStorage = mRenderer->createTextureStorage2D(
             surfaceD3D->getD3DTexture(), surfaceD3D->getBindChroma(),
-            surfaceD3D->getArrayIndex());
+            surfaceD3D->getDxgiBuffer());
     else
         mTexStorage = mRenderer->createTextureStorage2D(surfaceD3D->getSwapChain());
     mEGLImageTarget = false;
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 6c2e9ad..518b65e 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -4045,9 +4045,9 @@
 
 TextureStorage *Renderer11::createTextureStorage2D(IUnknown *texture,
                                                    bool bindChroma,
-                                                   UINT arrayIndex)
+                                                   IUnknown *dxgiBuffer)
 {
-    return new TextureStorage11_2D(this, texture, bindChroma, arrayIndex);
+    return new TextureStorage11_2D(this, texture, bindChroma, dxgiBuffer);
 }
 
 TextureStorage *Renderer11::createTextureStorageEGLImage(EGLImageD3D *eglImage,
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index 58c2406..459a913 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -265,7 +265,7 @@
     TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) override;
     TextureStorage *createTextureStorage2D(IUnknown *texture,
                                            bool bindChroma,
-                                           UINT arrayIndex) override;
+                                           IUnknown *dxgiBuffer) override;
     TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage,
                                                  RenderTargetD3D *renderTargetD3D) override;
     TextureStorage *createTextureStorageExternal(
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
index eb67992..a7cdcc0 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
@@ -8,6 +8,8 @@
 // classes TextureStorage11_2D and TextureStorage11_Cube, which act as the interface to the D3D11
 // texture.
 
+#include <mfobjects.h>
+
 #include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h"
 
 #include <tuple>
@@ -30,6 +32,22 @@
 
 #include "starboard/log.h"
 
+namespace
+{
+// This GUID is used for a D3D private data property that allows
+// us to keep the associated IMFDXGIBuffer (if any) alive as long
+// as the shader resource view is alive. Video decoders re-use the same
+// textures, but they track their lifetime by watching the lifetime of
+// the IMFDXGIBuffer. So the IMFDXGIBuffer must be kept alive as long
+// as the texture may still be used to draw a given video frame.
+static const GUID kCobaltKeepAlive = { /* 99a98f3c-37d9-46db-b6a6-bc83c96090e9 */
+    0x99a98f3c,
+    0x37d9,
+    0x46db,
+    {0xb6, 0xa6, 0xbc, 0x83, 0xc9, 0x60, 0x90, 0xe9}
+  };
+}
+
 namespace rx
 {
 
@@ -717,7 +735,7 @@
       mUseLevelZeroTexture(false),
       mSwizzleTexture(nullptr),
       mBindChroma(false),
-      mArrayIndex(0)
+      mDxgiBuffer(nullptr)
 {
     mTexture->AddRef();
 
@@ -739,7 +757,7 @@
 TextureStorage11_2D::TextureStorage11_2D(Renderer11 *renderer,
                                          IUnknown *texture,
                                          bool bindChroma,
-                                         UINT arrayIndex)
+                                         IUnknown *dxgiBuffer)
     : TextureStorage11(renderer,
                        0,
                        0,
@@ -750,9 +768,13 @@
       mUseLevelZeroTexture(false),
       mSwizzleTexture(nullptr),
       mBindChroma(bindChroma),
-      mArrayIndex(arrayIndex)
+      mDxgiBuffer(dxgiBuffer)
 {
     mTexture->AddRef();
+    if (mDxgiBuffer != nullptr)
+    {
+        mDxgiBuffer->AddRef();
+    }
 
     for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
     {
@@ -789,7 +811,9 @@
       mLevelZeroTexture(nullptr),
       mLevelZeroRenderTarget(nullptr),
       mUseLevelZeroTexture(hintLevelZeroOnly && levels > 1),
-      mSwizzleTexture(nullptr)
+      mSwizzleTexture(nullptr),
+      mBindChroma(nullptr),
+      mDxgiBuffer(nullptr)
 {
     for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
     {
@@ -827,6 +851,7 @@
     }
 
     SafeRelease(mTexture);
+    SafeRelease(mDxgiBuffer);
     SafeRelease(mSwizzleTexture);
 
     SafeRelease(mLevelZeroTexture);
@@ -1207,10 +1232,16 @@
         d3Texture->GetDesc(&texture_desc);
         if (texture_desc.Format == DXGI_FORMAT_NV12)
         {
+            UINT arrayIndex = 0;
+            if (mDxgiBuffer != nullptr)
+            {
+                static_cast<IMFDXGIBuffer*>(mDxgiBuffer)->
+                    GetSubresourceIndex(&arrayIndex);
+            }
             srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
             srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + baseLevel;
             srvDesc.Texture2DArray.MipLevels       = mipLevels;
-            srvDesc.Texture2DArray.FirstArraySlice = mArrayIndex;
+            srvDesc.Texture2DArray.FirstArraySlice = arrayIndex;
             srvDesc.Texture2DArray.ArraySize       = 1;
 
             if (mBindChroma)
@@ -1256,6 +1287,9 @@
 
     d3d11::SetDebugName(*outSRV, "TexStorage2D.SRV");
 
+    result = (*outSRV)->SetPrivateDataInterface(kCobaltKeepAlive, mDxgiBuffer);
+    ASSERT(SUCCEEDED(result));
+
     return gl::NoError();
 }
 
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
index 006b427..366c7ea3 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
@@ -157,7 +157,7 @@
     TextureStorage11_2D(Renderer11 *renderer,
                         IUnknown *texture,
                         bool bindChroma,
-                        unsigned int arrayIndex);
+                        IUnknown *dxgiBuffer);
     TextureStorage11_2D(Renderer11 *renderer, GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly = false);
     ~TextureStorage11_2D() override;
 
@@ -212,7 +212,7 @@
 
     Image11 *mAssociatedImages[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
     bool mBindChroma;
-    UINT mArrayIndex;
+    IUnknown *mDxgiBuffer;
 };
 
 class TextureStorage11_External : public TextureStorage11
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
index 6c62416..3b8b293 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
@@ -1307,7 +1307,12 @@
     extensions->textureNPOT = GetNPOTTextureSupport(featureLevel);
     extensions->drawBuffers = GetMaximumSimultaneousRenderTargets(featureLevel) > 1;
     extensions->textureStorage = true;
-    extensions->textureFilterAnisotropic = true;
+    // Anisotropic filtering isn't supported completely; in particular, it
+    // does not work correctly when interacting with glSamplerParameterf as
+    // GL_TEXTURE_MAX_ANISOTROPY_EXT is not considered a valid parameter name.
+    // So, although there is partial support at least, we explicitly disable it
+    // as normal use of the parameter causes GL errors.
+    extensions->textureFilterAnisotropic = false;
     extensions->maxTextureAnisotropy = GetMaximumAnisotropy(featureLevel);
     extensions->occlusionQueryBoolean = GetOcclusionQuerySupport(featureLevel);
     extensions->fence = GetEventQuerySupport(featureLevel);
diff --git a/src/third_party/angle/src/libGLESv2.gypi b/src/third_party/angle/src/libGLESv2.gypi
index 706102c..14b9bf2 100644
--- a/src/third_party/angle/src/libGLESv2.gypi
+++ b/src/third_party/angle/src/libGLESv2.gypi
@@ -1176,7 +1176,14 @@
                 {
                     'msvs_requires_importlibrary' : 'true',
                 }],
+                ['angle_gl_library_type== "shared_library"',
+                {
+                    'defines':[
+                      'LIBGLESV2_DLL'
+                    ]
+                }],
             ],
+
         },
 
         {
diff --git a/src/third_party/angle/src/libGLESv2/global_state.cpp b/src/third_party/angle/src/libGLESv2/global_state.cpp
index c5f3dfe..121e7e0 100644
--- a/src/third_party/angle/src/libGLESv2/global_state.cpp
+++ b/src/third_party/angle/src/libGLESv2/global_state.cpp
@@ -76,7 +76,7 @@
 
 }  // namespace egl
 
-#ifdef ANGLE_PLATFORM_WINDOWS
+#if defined(ANGLE_PLATFORM_WINDOWS) && defined(LIBGLESV2_DLL)
 namespace egl
 {
 
@@ -145,4 +145,4 @@
 
     return TRUE;
 }
-#endif  // ANGLE_PLATFORM_WINDOWS
+#endif  // defined(ANGLE_PLATFORM_WINDOWS) && defined(LIBGLESV2_DLL)
diff --git a/src/third_party/blink/Source/bindings/scripts/utilities.py b/src/third_party/blink/Source/bindings/scripts/utilities.py
index 02a1c6c..ed8e5e9 100644
--- a/src/third_party/blink/Source/bindings/scripts/utilities.py
+++ b/src/third_party/blink/Source/bindings/scripts/utilities.py
@@ -14,26 +14,21 @@
 import string
 import subprocess
 
-# Interfaces related to css are under the cssom directory.
-# All of Cobalt's interfaces are under the dom directory.
-# Interfaces related to testing the bindings generation are under testing.
-# Interfaces to our custom debugging functionality (e.g. console) is in debug.
+# All of Cobalt's interfaces are under either the cobalt/, starboard/ or
+# third_party/ directory.
+# Note that an IDL's "component" is not actually used for anything in Cobalt and
+# so this list just acts as a file path whitelist (i.e. one of these items must
+# appear in the path).
 KNOWN_COMPONENTS = frozenset(
     [
-        'audio',
-        'cssom',
-        'debug',
-        'dom',
-        'fetch',
-        'h5vcc',
-        'media_session',
-        'page_visibility',
-        'speech',
+        'cobalt',
+        'starboard',
+        'third_party',
+        # This is required to pass the Cobalt run_cobalt_bindings_tests.py,
+        # which is run on presubmit.
         'testing',
-        'web_animations',
-        'webdriver',
-        'websocket',
-        'xhr',
+        # Required to identify the generated window_constructors.idl.
+        'gen',
     ])
 
 # List of regular expressions finding tokens that would appear in a name that
diff --git a/src/third_party/dlmalloc/BUILD.gn b/src/third_party/dlmalloc/BUILD.gn
new file mode 100644
index 0000000..3811508
--- /dev/null
+++ b/src/third_party/dlmalloc/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright (c) 2013 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+static_library("dlmalloc") {
+  sources = [
+    "dlmalloc.c",
+  ]
+
+  if (target_os == "win") {
+    # Compile dlmalloc.c as C++ on MSVC.
+    cflags += [ "/TP" ]
+  } else if (target_cpu == "android") {
+    cflags += [ "-std=c99" ]
+  }
+}
diff --git a/src/third_party/libevent/BUILD.gn b/src/third_party/libevent/BUILD.gn
new file mode 100644
index 0000000..7cc526c
--- /dev/null
+++ b/src/third_party/libevent/BUILD.gn
@@ -0,0 +1,76 @@
+# Copyright (c) 2012 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.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import("//starboard/build/config/base.gni")
+
+# Starboard porters can set platform defaults for the following variable in
+# //$starboard_path/configuration.gni.
+
+# Use the system libevent instead of the bundled one.
+if (!defined(use_system_libevent)) {
+  use_system_libevent = false
+}
+
+if (!use_system_libevent) {
+  static_library("libevent") {
+    sources = [
+      "epoll.c",
+      "event.c",
+      "evutil.c",
+      "log.c",
+    ]
+
+    defines = [ "HAVE_CONFIG_H" ]
+
+    include_dirs = [ "starboard" ]
+
+    if (sb_libevent_method != "poll" && sb_libevent_method != "epoll") {
+      sources += [ "poll.c" ]
+    }
+
+    if (target_os == "linux") {
+      sources += [
+        "epoll_sub.c",
+      ]
+
+      include_dirs += [ "starboard/linux" ]
+    }
+
+    # TODO: Make this android specific, not a linux copy.
+    if (target_os == "android") {
+      sources += [
+        "epoll_sub.c",
+      ]
+
+      include_dirs += [ "starboard/linux" ]
+    }
+
+    if (target_os == "orbis") {
+      include_dirs += [ "starboard/ps4" ]
+    }
+  }
+} else {
+  config("libevent_config") {
+    defines = [ "USE_SYSTEM_LIBEVENT" ]
+  }
+
+  group("libevent") {
+    public_configs = [ ":libevent_config" ]
+  }
+}
diff --git a/src/third_party/libvpx/BUILD.gn b/src/third_party/libvpx/BUILD.gn
new file mode 100644
index 0000000..2e8535a
--- /dev/null
+++ b/src/third_party/libvpx/BUILD.gn
@@ -0,0 +1,163 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+libvpx_source = "//third_party/libvpx"
+
+static_library("libvpx") {
+  # This list was generated by running configure and then
+  # make libvpx_srcs.txt
+  sources = [
+    "platforms/linux-x64/vp9_rtcd.h",
+    "platforms/linux-x64/vpx_config.c",
+    "platforms/linux-x64/vpx_config.h",
+    "platforms/linux-x64/vpx_dsp_rtcd.h",
+    "platforms/linux-x64/vpx_scale_rtcd.h",
+    "platforms/linux-x64/vpx_version.h",
+    "vp9/common/vp9_alloccommon.c",
+    "vp9/common/vp9_alloccommon.h",
+    "vp9/common/vp9_blockd.c",
+    "vp9/common/vp9_blockd.h",
+    "vp9/common/vp9_common.h",
+    "vp9/common/vp9_common_data.c",
+    "vp9/common/vp9_common_data.h",
+    "vp9/common/vp9_debugmodes.c",
+    "vp9/common/vp9_entropy.c",
+    "vp9/common/vp9_entropy.h",
+    "vp9/common/vp9_entropymode.c",
+    "vp9/common/vp9_entropymode.h",
+    "vp9/common/vp9_entropymv.c",
+    "vp9/common/vp9_entropymv.h",
+    "vp9/common/vp9_enums.h",
+    "vp9/common/vp9_filter.c",
+    "vp9/common/vp9_filter.h",
+    "vp9/common/vp9_frame_buffers.c",
+    "vp9/common/vp9_frame_buffers.h",
+    "vp9/common/vp9_idct.c",
+    "vp9/common/vp9_idct.h",
+    "vp9/common/vp9_loopfilter.c",
+    "vp9/common/vp9_loopfilter.h",
+    "vp9/common/vp9_mv.h",
+    "vp9/common/vp9_mvref_common.c",
+    "vp9/common/vp9_mvref_common.h",
+    "vp9/common/vp9_onyxc_int.h",
+    "vp9/common/vp9_ppflags.h",
+    "vp9/common/vp9_pred_common.c",
+    "vp9/common/vp9_pred_common.h",
+    "vp9/common/vp9_quant_common.c",
+    "vp9/common/vp9_quant_common.h",
+    "vp9/common/vp9_reconinter.c",
+    "vp9/common/vp9_reconinter.h",
+    "vp9/common/vp9_reconintra.c",
+    "vp9/common/vp9_reconintra.h",
+    "vp9/common/vp9_rtcd.c",
+    "vp9/common/vp9_scale.c",
+    "vp9/common/vp9_scale.h",
+    "vp9/common/vp9_scan.c",
+    "vp9/common/vp9_scan.h",
+    "vp9/common/vp9_seg_common.c",
+    "vp9/common/vp9_seg_common.h",
+    "vp9/common/vp9_textblit.h",
+    "vp9/common/vp9_thread_common.c",
+    "vp9/common/vp9_thread_common.h",
+    "vp9/common/vp9_tile_common.c",
+    "vp9/common/vp9_tile_common.h",
+    "vp9/common/x86/vp9_idct_intrin_sse2.c",
+    "vp9/decoder/vp9_decodeframe.c",
+    "vp9/decoder/vp9_decodeframe.h",
+    "vp9/decoder/vp9_decodemv.c",
+    "vp9/decoder/vp9_decodemv.h",
+    "vp9/decoder/vp9_decoder.c",
+    "vp9/decoder/vp9_decoder.h",
+    "vp9/decoder/vp9_detokenize.c",
+    "vp9/decoder/vp9_detokenize.h",
+    "vp9/decoder/vp9_dsubexp.c",
+    "vp9/decoder/vp9_dsubexp.h",
+    "vp9/decoder/vp9_dthread.c",
+    "vp9/decoder/vp9_dthread.h",
+    "vp9/vp9_dx_iface.c",
+    "vp9/vp9_dx_iface.h",
+    "vp9/vp9_iface_common.h",
+    "vpx/internal/vpx_codec_internal.h",
+    "vpx/internal/vpx_psnr.h",
+    "vpx/src/vpx_codec.c",
+    "vpx/src/vpx_decoder.c",
+    "vpx/src/vpx_encoder.c",
+    "vpx/src/vpx_image.c",
+    "vpx/src/vpx_psnr.c",
+    "vpx/vp8.h",
+    "vpx/vp8dx.h",
+    "vpx/vpx_codec.h",
+    "vpx/vpx_decoder.h",
+    "vpx/vpx_encoder.h",
+    "vpx/vpx_frame_buffer.h",
+    "vpx/vpx_image.h",
+    "vpx/vpx_integer.h",
+    "vpx_dsp/add_noise.c",
+    "vpx_dsp/bitreader.c",
+    "vpx_dsp/bitreader.h",
+    "vpx_dsp/bitreader_buffer.c",
+    "vpx_dsp/bitreader_buffer.h",
+    "vpx_dsp/intrapred.c",
+    "vpx_dsp/inv_txfm.c",
+    "vpx_dsp/inv_txfm.h",
+    "vpx_dsp/loopfilter.c",
+    "vpx_dsp/prob.c",
+    "vpx_dsp/prob.h",
+    "vpx_dsp/txfm_common.h",
+    "vpx_dsp/variance.c",
+    "vpx_dsp/variance.h",
+    "vpx_dsp/vpx_convolve.c",
+    "vpx_dsp/vpx_convolve.h",
+    "vpx_dsp/vpx_dsp_common.h",
+    "vpx_dsp/vpx_dsp_rtcd.c",
+    "vpx_dsp/vpx_filter.h",
+    "vpx_dsp/x86/convolve.h",
+    "vpx_dsp/x86/loopfilter_sse2.c",
+    "vpx_dsp/x86/vpx_asm_stubs.c",
+    "vpx_mem/include/vpx_mem_intrnl.h",
+    "vpx_mem/vpx_mem.c",
+    "vpx_mem/vpx_mem.h",
+    "vpx_ports/bitops.h",
+    "vpx_ports/emmintrin_compat.h",
+    "vpx_ports/mem.h",
+    "vpx_ports/mem_ops.h",
+    "vpx_ports/mem_ops_aligned.h",
+    "vpx_ports/msvc.h",
+    "vpx_ports/system_state.h",
+    "vpx_ports/vpx_once.h",
+    "vpx_ports/vpx_timer.h",
+    "vpx_ports/x86.h",
+    "vpx_scale/generic/gen_scalers.c",
+    "vpx_scale/generic/vpx_scale.c",
+    "vpx_scale/generic/yv12config.c",
+    "vpx_scale/generic/yv12extend.c",
+    "vpx_scale/vpx_scale.h",
+    "vpx_scale/vpx_scale_rtcd.c",
+    "vpx_scale/yv12config.h",
+    "vpx_util/endian_inl.h",
+    "vpx_util/vpx_thread.c",
+    "vpx_util/vpx_thread.h",
+  ]
+
+  include_dirs = [
+    "$libvpx_source",
+    "$libvpx_source/platforms/linux-x64",
+    "$libvpx_source/vpx_mem/memory_manager/include/",
+  ]
+
+  # Always optimize libvpx at max optimization.
+  # Debug performance is too slow.
+  configs -= [ "//starboard/build/config:default_optimizations" ]
+  configs += [ "//starboard/build/config:full_optimizations" ]
+}
diff --git a/src/third_party/libwebp/libwebp.gyp b/src/third_party/libwebp/libwebp.gyp
index 59deb74..9b659e9 100644
--- a/src/third_party/libwebp/libwebp.gyp
+++ b/src/third_party/libwebp/libwebp.gyp
@@ -54,13 +54,6 @@
         ['OS == "android"', {
           'includes': [ '../../build/android/cpufeatures.gypi' ],
         }],
-        ['order_profiling != 0', {
-          'target_conditions' : [
-            ['_toolset=="target"', {
-              'cflags!': [ '-finstrument-functions' ],
-            }],
-          ],
-        }],
       ],
     },
     {
@@ -84,13 +77,6 @@
           # NEON is implicit on ARMv8, and clang doesn't like the redundant flag
           'cflags!': [ '-mfpu=neon' ],
         }],
-        ['order_profiling != 0', {
-          'target_conditions' : [
-            ['_toolset=="target"', {
-              'cflags!': [ '-finstrument-functions' ],
-            }],
-          ],
-        }],
       ],
     },
     {
diff --git a/src/tools/gyp/pylib/gyp/__init__.py b/src/tools/gyp/pylib/gyp/__init__.py
index a43fc80..6c11b0d 100755
--- a/src/tools/gyp/pylib/gyp/__init__.py
+++ b/src/tools/gyp/pylib/gyp/__init__.py
@@ -135,6 +135,8 @@
                 'generator_wants_static_library_dependencies_adjusted', True),
     'generator_wants_sorted_dependencies':
         getattr(generator, 'generator_wants_sorted_dependencies', False),
+    'generator_filelist_paths':
+        getattr(generator, 'generator_filelist_paths', None),
   }
 
   # Process the input specific to this generator.
diff --git a/src/tools/gyp/pylib/gyp/common.py b/src/tools/gyp/pylib/gyp/common.py
index 54b32f2..f4b2db5 100644
--- a/src/tools/gyp/pylib/gyp/common.py
+++ b/src/tools/gyp/pylib/gyp/common.py
@@ -362,6 +362,14 @@
   return Writer()
 
 
+def EnsureDirExists(path):
+  """Make sure the directory for |path| exists."""
+  try:
+    os.makedirs(os.path.dirname(path))
+  except OSError:
+    pass
+
+
 def GetFlavor(params):
   """Returns |params.flavor| if it's set, the system's default flavor else."""
   flavors = {
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 7295c7e..1724d6f 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -77,7 +77,7 @@
 is_linux = platform.system() == 'Linux'
 is_windows = platform.system() == 'Windows'
 
-microsoft_flavors = ['win', 'win-win32', 'win-console', 'win-lib', 'xb1', 'xb1-future']
+microsoft_flavors = ['win', 'win-win32', 'win-win32-lib', 'xb1', 'xb1-future']
 sony_flavors = ['ps3', 'ps4']
 windows_host_flavors = microsoft_flavors + sony_flavors
 
@@ -1835,6 +1835,33 @@
         '$!PRODUCT_DIR', 'obj'))
 
 
+def ComputeOutputDir(params):
+  """Returns the path from the toplevel_dir to the build output directory."""
+  # generator_dir: relative path from pwd to where make puts build files.
+  # Makes migrating from make to ninja easier, ninja doesn't put anything here.
+  generator_dir = os.path.relpath(params['options'].generator_output or '.')
+
+  # output_dir: relative path from generator_dir to the build directory.
+  output_dir = params.get('generator_flags', {}).get('output_dir', 'out')
+
+  # Relative path from source root to our output files.  e.g. "out"
+  return os.path.normpath(os.path.join(generator_dir, output_dir))
+
+
+def CalculateGeneratorInputInfo(params):
+  """Called by __init__ to initialize generator values based on params."""
+  user_config = params.get('generator_flags', {}).get('config', None)
+  toplevel = params['options'].toplevel_dir
+  qualified_out_dir = os.path.normpath(os.path.join(
+      toplevel, ComputeOutputDir(params), user_config, 'gypfiles'))
+
+  global generator_filelist_paths
+  generator_filelist_paths = {
+      'toplevel': toplevel,
+      'qualified_out_dir': qualified_out_dir,
+  }
+
+
 def OpenOutput(path, mode='w'):
   """Open |path| for writing, creating directories if necessary."""
   try:
@@ -1946,17 +1973,10 @@
 
   generator_flags = params.get('generator_flags', {})
 
-  # generator_dir: relative path from pwd to where make puts build files.
-  # Makes migrating from make to ninja easier, ninja doesn't put anything here.
-  generator_dir = os.path.relpath(params['options'].generator_output or '.')
-
-  # output_dir: relative path from generator_dir to the build directory.
-  output_dir = generator_flags.get('output_dir', 'out')
-
   # build_dir: relative path from source root to our output files.
   # e.g. "out/Debug"
   build_dir = os.path.normpath(
-      os.path.join(generator_dir, output_dir, config_name))
+      os.path.join(ComputeOutputDir(params), config_name))
 
   toplevel_build = os.path.join(options.toplevel_dir, build_dir)
 
diff --git a/src/tools/gyp/pylib/gyp/input.py b/src/tools/gyp/pylib/gyp/input.py
index 5697aef..38b75c9 100755
--- a/src/tools/gyp/pylib/gyp/input.py
+++ b/src/tools/gyp/pylib/gyp/input.py
@@ -121,6 +121,11 @@
 # Controls whether or not the generator supports multiple toolsets.
 multiple_toolsets = False
 
+# Paths for converting filelist paths to output paths: {
+#   toplevel,
+#   qualified_output_dir,
+# }
+generator_filelist_paths = None
 
 def GetIncludedBuildFiles(build_file_path, aux_data, included=None):
   """Return a list of all build files included into build_file_path.
@@ -809,14 +814,27 @@
     # This works around actions/rules which have more inputs than will
     # fit on the command line.
     if file_list:
-      if type(contents) == list:
+      if type(contents) is list:
         contents_list = contents
       else:
         contents_list = contents.split(' ')
       replacement = contents_list[0]
-      path = replacement
-      if not os.path.isabs(path):
-        path = os.path.join(build_file_dir, path)
+      if os.path.isabs(replacement):
+        raise GypError('| cannot handle absolute paths, got "%s"' % replacement)
+
+      if not generator_filelist_paths:
+        path = os.path.join(build_file_dir, replacement)
+      else:
+        if os.path.isabs(build_file_dir):
+          toplevel = generator_filelist_paths['toplevel']
+          rel_build_file_dir = gyp.common.RelativePath(build_file_dir, toplevel)
+        else:
+          rel_build_file_dir = build_file_dir
+        qualified_out_dir = generator_filelist_paths['qualified_out_dir']
+        path = os.path.join(qualified_out_dir, rel_build_file_dir, replacement)
+        gyp.common.EnsureDirExists(path)
+
+      replacement = gyp.common.RelativePath(path, build_file_dir)
       f = gyp.common.WriteOnDiff(path)
       for i in contents_list[1:]:
         f.write('%s\n' % i)
@@ -2532,6 +2550,9 @@
 
 def Load(build_files, variables, includes, depth, generator_input_info, check,
          circular_check, parallel):
+  global generator_filelist_paths
+  generator_filelist_paths = generator_input_info['generator_filelist_paths']
+
   # Set up path_sections and non_configuration_keys with the default data plus
   # the generator-specifc data.
   global path_sections