Import Cobalt 10.56829
diff --git a/src/cobalt/accessibility/screen_reader.cc b/src/cobalt/accessibility/screen_reader.cc
index 54a8d8c..9faee21 100644
--- a/src/cobalt/accessibility/screen_reader.cc
+++ b/src/cobalt/accessibility/screen_reader.cc
@@ -34,7 +34,8 @@
 
 ScreenReader::ScreenReader(dom::Document* document, TTSEngine* tts_engine,
                            dom::MutationObserverTaskManager* task_manager)
-    : enabled_(true), document_(document), tts_engine_(tts_engine) {
+    : enabled_(true), document_(document), tts_engine_(tts_engine),
+      focus_changed_(false) {
   document_->AddObserver(this);
   live_region_observer_ = new dom::MutationObserver(
       base::Bind(&ScreenReader::MutationObserverCallback,
@@ -64,6 +65,17 @@
 }
 
 void ScreenReader::OnFocusChanged() {
+  if (focus_changed_) {
+    return;
+  }
+  focus_changed_ = true;
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&ScreenReader::FocusChangedCallback, base::Unretained(this)));
+}
+
+void ScreenReader::FocusChangedCallback() {
+  focus_changed_ = false;
   if (!enabled_) {
     return;
   }
@@ -74,7 +86,7 @@
   }
 }
 
-// MutationObserver callback for tracking the creationg and destruction of
+// MutationObserver callback for tracking the creation and destruction of
 // live regions.
 void ScreenReader::MutationObserverCallback(
     const MutationRecordSequence& sequence,
diff --git a/src/cobalt/accessibility/screen_reader.h b/src/cobalt/accessibility/screen_reader.h
index 846d50f..542ed22 100644
--- a/src/cobalt/accessibility/screen_reader.h
+++ b/src/cobalt/accessibility/screen_reader.h
@@ -47,6 +47,7 @@
  private:
   typedef script::Sequence<scoped_refptr<dom::MutationRecord> >
       MutationRecordSequence;
+  void FocusChangedCallback();
   // MutationObserver callback for tracking live regions.
   void MutationObserverCallback(
       const MutationRecordSequence& sequence,
@@ -56,6 +57,7 @@
   dom::Document* document_;
   TTSEngine* tts_engine_;
   scoped_refptr<dom::MutationObserver> live_region_observer_;
+  bool focus_changed_;
 
   friend class scoped_ptr<ScreenReader>;
   DISALLOW_COPY_AND_ASSIGN(ScreenReader);
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 7ca128e..8929dd1 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -57,6 +57,8 @@
     MacroOpWithNameOnly(ended)                                       \
     MacroOpWithNameOnly(error)                                       \
     MacroOpWithNameOnly(focus)                                       \
+    MacroOpWithNameOnly(focusin)                                     \
+    MacroOpWithNameOnly(focusout)                                    \
     MacroOpWithNameOnly(hashchange)                                  \
     MacroOpWithNameOnly(keydown)                                     \
     MacroOpWithNameOnly(keypress)                                    \
diff --git a/src/cobalt/bindings/IDLExtendedAttributes.txt b/src/cobalt/bindings/IDLExtendedAttributes.txt
index 8b1c115..bd7bd59 100644
--- a/src/cobalt/bindings/IDLExtendedAttributes.txt
+++ b/src/cobalt/bindings/IDLExtendedAttributes.txt
@@ -57,20 +57,6 @@
 # http://heycam.github.io/webidl/#Exposed
 Exposed=*
 
-# The value of this attribute is a list of functions to be called that will
-# return a scoped_refptr<Wrappable>.
-# The raw pointer will be added as an opaque root when an instance of this
-# interface is visited during garbage collection.
-AddOpaqueRoots=|*
-
-# The value of this attribute is a function to be called that will return a
-# scoped_refptr<Wrappable> that represents an opaque root of an object
-# implementing this interface.
-# If opaque root of an object is in the set of opaque roots that have been
-# collected during garbage collection, the object's wrapper will be kept alive
-# as well.
-GetOpaqueRoot=*
-
 # Call the Cobalt function this property is bound to with the corresponding
 # object added to the head of the function's parameters.
 # StackTrace is a custom addition to CallWith for the purpose of testing
diff --git a/src/cobalt/bindings/code_generator_cobalt.py b/src/cobalt/bindings/code_generator_cobalt.py
index f3a7892..043bad1 100644
--- a/src/cobalt/bindings/code_generator_cobalt.py
+++ b/src/cobalt/bindings/code_generator_cobalt.py
@@ -431,11 +431,15 @@
     referenced_interface_names = set(
         get_interface_type_names_from_typed_objects(self.info_provider,
                                                     dictionary.members))
+    if dictionary.parent:
+      referenced_interface_names.add(dictionary.parent)
+      context['parent'] = dictionary.parent
+
     referenced_class_contexts = self.referenced_class_contexts(
         referenced_interface_names, for_conversion)
 
-    context['includes'] = sorted((interface['include']
-                                  for interface in referenced_class_contexts))
+    context['includes'] = sorted(interface['include']
+                                 for interface in referenced_class_contexts)
     context['forward_declarations'] = sorted(
         referenced_class_contexts, key=lambda x: x['fully_qualified_name'])
     context['components'] = self.path_builder.NamespaceComponents(
@@ -467,10 +471,6 @@
             'NoInterfaceObject' not in interface.extended_attributes),
         'conditional':
             interface.extended_attributes.get('Conditional', None),
-        'add_opaque_roots':
-            interface.extended_attributes.get('AddOpaqueRoots', None),
-        'get_opaque_root':
-            interface.extended_attributes.get('GetOpaqueRoot', None),
     }
     interfaces_info = self.info_provider.interfaces_info
     if is_global_interface(interface):
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
index e496560..eb0a945 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -93,13 +93,6 @@
 
 namespace {
 
-Wrappable* GetOpaqueRootFromWrappable(
-    const scoped_refptr<Wrappable>& wrappable) {
-  GarbageCollectionTestInterface* impl =
-      base::polymorphic_downcast<GarbageCollectionTestInterface*>(wrappable.get());
-  return impl->GetHead();
-}
-
 class MozjsGarbageCollectionTestInterfaceHandler : public ProxyHandler {
  public:
   MozjsGarbageCollectionTestInterfaceHandler()
@@ -511,11 +504,7 @@
   JS::RootedObject proxy(context,
       ProxyHandler::NewProxy(context, new_object, prototype, NULL,
                              proxy_handler.Pointer()));
-  WrapperPrivate::GetOpaqueRootFunction get_root;
-  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
-  get_root = base::Bind(&GetOpaqueRootFromWrappable);
-  WrapperPrivate::AddPrivateData(
-      context, proxy, wrappable, get_root, get_reachable_wrappables);
+  WrapperPrivate::AddPrivateData(context, proxy, wrappable);
   return proxy;
 }
 
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
deleted file mode 100644
index 0ef8902..0000000
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
+++ /dev/null
@@ -1,417 +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.
-
-// 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_get_opaque_root_interface.h"
-
-#include "base/debug/trace_event.h"
-#include "cobalt/base/polymorphic_downcast.h"
-#include "cobalt/script/global_environment.h"
-#include "cobalt/script/opaque_handle.h"
-#include "cobalt/script/script_value.h"
-
-#include "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::GetOpaqueRootInterface;
-using cobalt::bindings::testing::MozjsGetOpaqueRootInterface;
-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 {
-
-Wrappable* GetOpaqueRootFromWrappable(
-    const scoped_refptr<Wrappable>& wrappable) {
-  GetOpaqueRootInterface* impl =
-      base::polymorphic_downcast<GetOpaqueRootInterface*>(wrappable.get());
-  return impl->get_opaque_root_function_name();
-}
-
-void GetReachableWrappables(const scoped_refptr<Wrappable>& wrappable,
-    WrapperPrivate::WrappableVector* reachable) {
-  DCHECK(reachable);
-  GetOpaqueRootInterface* impl =
-      base::polymorphic_downcast<GetOpaqueRootInterface*>(wrappable.get());
-  Wrappable* reachable_0 = impl->add_opaque_root_function_name();
-  if (reachable_0) {
-    reachable->push_back(reachable_0);
-  }
-}
-
-class MozjsGetOpaqueRootInterfaceHandler : public ProxyHandler {
- public:
-  MozjsGetOpaqueRootInterfaceHandler()
-      : ProxyHandler(indexed_property_hooks, named_property_hooks) {}
-
- private:
-  static NamedPropertyHooks named_property_hooks;
-  static IndexedPropertyHooks indexed_property_hooks;
-};
-
-ProxyHandler::NamedPropertyHooks
-MozjsGetOpaqueRootInterfaceHandler::named_property_hooks = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-};
-ProxyHandler::IndexedPropertyHooks
-MozjsGetOpaqueRootInterfaceHandler::indexed_property_hooks = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-};
-
-static base::LazyInstance<MozjsGetOpaqueRootInterfaceHandler>
-    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, MozjsGetOpaqueRootInterface::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 = "GetOpaqueRootInterface";
-  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 = "GetOpaqueRootInterfacePrototype";
-  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 = "GetOpaqueRootInterfaceConstructor";
-  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;
-}
-
-
-const JSPropertySpec prototype_properties[] = {
-  JS_PS_END
-};
-
-const JSFunctionSpec prototype_functions[] = {
-  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[] =
-      "GetOpaqueRootInterface";
-  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* MozjsGetOpaqueRootInterface::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::GetOpaqueRootFunction get_root;
-  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
-  get_root = base::Bind(&GetOpaqueRootFromWrappable);
-  get_reachable_wrappables = base::Bind(&GetReachableWrappables);
-  WrapperPrivate::AddPrivateData(
-      context, proxy, wrappable, get_root, get_reachable_wrappables);
-  return proxy;
-}
-
-//static
-const JSClass* MozjsGetOpaqueRootInterface::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* MozjsGetOpaqueRootInterface::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* MozjsGetOpaqueRootInterface::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<GetOpaqueRootInterface> new_object =
-      new GetOpaqueRootInterface();
-  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_get_opaque_root_interface.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.h
deleted file mode 100644
index 7b60f3c..0000000
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.h
+++ /dev/null
@@ -1,52 +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.
-
-// 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 MozjsGetOpaqueRootInterface_h
-#define MozjsGetOpaqueRootInterface_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/get_opaque_root_interface.h"
-
-#include "third_party/mozjs/js/src/jsapi.h"
-
-namespace cobalt {
-namespace bindings {
-namespace testing {
-
-class MozjsGetOpaqueRootInterface {
- 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  // MozjsGetOpaqueRootInterface_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
index 883ae30..bba75f7 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
@@ -46,7 +46,6 @@
 #include "cobalt/bindings/testing/exceptions_interface.h"
 #include "cobalt/bindings/testing/extended_idl_attributes_interface.h"
 #include "cobalt/bindings/testing/garbage_collection_test_interface.h"
-#include "cobalt/bindings/testing/get_opaque_root_interface.h"
 #include "cobalt/bindings/testing/global_interface_parent.h"
 #include "cobalt/bindings/testing/implemented_interface.h"
 #include "cobalt/bindings/testing/indexed_getter_interface.h"
@@ -74,7 +73,6 @@
 #include "cobalt/bindings/testing/mozjs_exceptions_interface.h"
 #include "cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.h"
 #include "cobalt/bindings/testing/mozjs_garbage_collection_test_interface.h"
-#include "cobalt/bindings/testing/mozjs_get_opaque_root_interface.h"
 #include "cobalt/bindings/testing/mozjs_global_interface_parent.h"
 #include "cobalt/bindings/testing/mozjs_implemented_interface.h"
 #include "cobalt/bindings/testing/mozjs_indexed_getter_interface.h"
@@ -175,7 +173,6 @@
 using cobalt::bindings::testing::ExceptionsInterface;
 using cobalt::bindings::testing::ExtendedIDLAttributesInterface;
 using cobalt::bindings::testing::GarbageCollectionTestInterface;
-using cobalt::bindings::testing::GetOpaqueRootInterface;
 using cobalt::bindings::testing::GlobalInterfaceParent;
 using cobalt::bindings::testing::ImplementedInterface;
 using cobalt::bindings::testing::IndexedGetterInterface;
@@ -207,7 +204,6 @@
 using cobalt::bindings::testing::MozjsExceptionsInterface;
 using cobalt::bindings::testing::MozjsExtendedIDLAttributesInterface;
 using cobalt::bindings::testing::MozjsGarbageCollectionTestInterface;
-using cobalt::bindings::testing::MozjsGetOpaqueRootInterface;
 using cobalt::bindings::testing::MozjsGlobalInterfaceParent;
 using cobalt::bindings::testing::MozjsImplementedInterface;
 using cobalt::bindings::testing::MozjsIndexedGetterInterface;
@@ -1156,10 +1152,6 @@
       base::Bind(MozjsGarbageCollectionTestInterface::CreateProxy),
       base::Bind(MozjsGarbageCollectionTestInterface::PrototypeClass));
   wrapper_factory->RegisterWrappableType(
-      GetOpaqueRootInterface::GetOpaqueRootInterfaceWrappableType(),
-      base::Bind(MozjsGetOpaqueRootInterface::CreateProxy),
-      base::Bind(MozjsGetOpaqueRootInterface::PrototypeClass));
-  wrapper_factory->RegisterWrappableType(
       GlobalInterfaceParent::GlobalInterfaceParentWrappableType(),
       base::Bind(MozjsGlobalInterfaceParent::CreateProxy),
       base::Bind(MozjsGlobalInterfaceParent::PrototypeClass));
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
index 0c62a2d..e13c005 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -93,13 +93,6 @@
 
 namespace {
 
-Wrappable* GetOpaqueRootFromWrappable(
-    const scoped_refptr<Wrappable>& wrappable) {
-  GarbageCollectionTestInterface* impl =
-      base::polymorphic_downcast<GarbageCollectionTestInterface*>(wrappable.get());
-  return impl->GetHead();
-}
-
 class MozjsGarbageCollectionTestInterfaceHandler : public ProxyHandler {
  public:
   MozjsGarbageCollectionTestInterfaceHandler()
@@ -515,11 +508,7 @@
   JS::RootedObject proxy(context,
       ProxyHandler::NewProxy(
           context, proxy_handler.Pointer(), new_object, prototype));
-  WrapperPrivate::GetOpaqueRootFunction get_root;
-  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
-  get_root = base::Bind(&GetOpaqueRootFromWrappable);
-  WrapperPrivate::AddPrivateData(
-      context, proxy, wrappable, get_root, get_reachable_wrappables);
+  WrapperPrivate::AddPrivateData(context, proxy, wrappable);
   return proxy;
 }
 
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
deleted file mode 100644
index f2001fc..0000000
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
+++ /dev/null
@@ -1,404 +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.
-
-// 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_get_opaque_root_interface.h"
-
-#include "base/debug/trace_event.h"
-#include "cobalt/base/polymorphic_downcast.h"
-#include "cobalt/script/global_environment.h"
-#include "cobalt/script/opaque_handle.h"
-#include "cobalt/script/script_value.h"
-
-#include "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::GetOpaqueRootInterface;
-using cobalt::bindings::testing::MozjsGetOpaqueRootInterface;
-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 {
-
-Wrappable* GetOpaqueRootFromWrappable(
-    const scoped_refptr<Wrappable>& wrappable) {
-  GetOpaqueRootInterface* impl =
-      base::polymorphic_downcast<GetOpaqueRootInterface*>(wrappable.get());
-  return impl->get_opaque_root_function_name();
-}
-
-void GetReachableWrappables(const scoped_refptr<Wrappable>& wrappable,
-    WrapperPrivate::WrappableVector* reachable) {
-  DCHECK(reachable);
-  GetOpaqueRootInterface* impl =
-      base::polymorphic_downcast<GetOpaqueRootInterface*>(wrappable.get());
-  Wrappable* reachable_0 = impl->add_opaque_root_function_name();
-  if (reachable_0) {
-    reachable->push_back(reachable_0);
-  }
-}
-
-class MozjsGetOpaqueRootInterfaceHandler : public ProxyHandler {
- public:
-  MozjsGetOpaqueRootInterfaceHandler()
-      : ProxyHandler(indexed_property_hooks, named_property_hooks) {}
-
- private:
-  static NamedPropertyHooks named_property_hooks;
-  static IndexedPropertyHooks indexed_property_hooks;
-};
-
-ProxyHandler::NamedPropertyHooks
-MozjsGetOpaqueRootInterfaceHandler::named_property_hooks = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-};
-ProxyHandler::IndexedPropertyHooks
-MozjsGetOpaqueRootInterfaceHandler::indexed_property_hooks = {
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-};
-
-static base::LazyInstance<MozjsGetOpaqueRootInterfaceHandler>
-    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, MozjsGetOpaqueRootInterface::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 = {
-    "GetOpaqueRootInterface",
-    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 = {
-    "GetOpaqueRootInterfacePrototype",
-};
-
-const JSClass interface_object_class_definition = {
-    "GetOpaqueRootInterfaceConstructor",
-    0,
-    NULL,  // addProperty
-    NULL,  // delProperty
-    NULL,  // getProperty
-    NULL,  // setProperty
-    NULL,  // enumerate
-    NULL,  // resolve
-    NULL,  // mayResolve
-    NULL,  // finalize
-    NULL,  // call
-    &HasInstance,
-    Constructor,
-};
-
-
-
-const JSPropertySpec prototype_properties[] = {
-  JS_PS_END
-};
-
-const JSFunctionSpec prototype_functions[] = {
-  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[] =
-      "GetOpaqueRootInterface";
-  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);
-}
-
-InterfaceData* GetInterfaceData(JSContext* context) {
-  MozjsGlobalEnvironment* global_environment =
-      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
-  // Use the address of the properties definition for this interface as a
-  // unique key for looking up the InterfaceData for this interface.
-  intptr_t key = reinterpret_cast<intptr_t>(&own_properties);
-  InterfaceData* interface_data = global_environment->GetInterfaceData(key);
-  if (!interface_data) {
-    interface_data = new InterfaceData();
-    DCHECK(interface_data);
-    global_environment->CacheInterfaceData(key, interface_data);
-    DCHECK_EQ(interface_data, global_environment->GetInterfaceData(key));
-  }
-  return interface_data;
-}
-
-}  // namespace
-
-// static
-JSObject* MozjsGetOpaqueRootInterface::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::GetOpaqueRootFunction get_root;
-  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
-  get_root = base::Bind(&GetOpaqueRootFromWrappable);
-  get_reachable_wrappables = base::Bind(&GetReachableWrappables);
-  WrapperPrivate::AddPrivateData(
-      context, proxy, wrappable, get_root, get_reachable_wrappables);
-  return proxy;
-}
-
-// static
-const JSClass* MozjsGetOpaqueRootInterface::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* MozjsGetOpaqueRootInterface::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* MozjsGetOpaqueRootInterface::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<GetOpaqueRootInterface> new_object =
-      new GetOpaqueRootInterface();
-  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_get_opaque_root_interface.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.h
deleted file mode 100644
index b5fd99c..0000000
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.h
+++ /dev/null
@@ -1,52 +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.
-
-// 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 MozjsGetOpaqueRootInterface_h
-#define MozjsGetOpaqueRootInterface_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/get_opaque_root_interface.h"
-
-#include "third_party/mozjs-45/js/src/jsapi.h"
-
-namespace cobalt {
-namespace bindings {
-namespace testing {
-
-class MozjsGetOpaqueRootInterface {
- 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  // MozjsGetOpaqueRootInterface_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
index c26f450..1414c2c 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
@@ -46,7 +46,6 @@
 #include "cobalt/bindings/testing/exceptions_interface.h"
 #include "cobalt/bindings/testing/extended_idl_attributes_interface.h"
 #include "cobalt/bindings/testing/garbage_collection_test_interface.h"
-#include "cobalt/bindings/testing/get_opaque_root_interface.h"
 #include "cobalt/bindings/testing/global_interface_parent.h"
 #include "cobalt/bindings/testing/implemented_interface.h"
 #include "cobalt/bindings/testing/indexed_getter_interface.h"
@@ -74,7 +73,6 @@
 #include "cobalt/bindings/testing/mozjs_exceptions_interface.h"
 #include "cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.h"
 #include "cobalt/bindings/testing/mozjs_garbage_collection_test_interface.h"
-#include "cobalt/bindings/testing/mozjs_get_opaque_root_interface.h"
 #include "cobalt/bindings/testing/mozjs_global_interface_parent.h"
 #include "cobalt/bindings/testing/mozjs_implemented_interface.h"
 #include "cobalt/bindings/testing/mozjs_indexed_getter_interface.h"
@@ -175,7 +173,6 @@
 using cobalt::bindings::testing::ExceptionsInterface;
 using cobalt::bindings::testing::ExtendedIDLAttributesInterface;
 using cobalt::bindings::testing::GarbageCollectionTestInterface;
-using cobalt::bindings::testing::GetOpaqueRootInterface;
 using cobalt::bindings::testing::GlobalInterfaceParent;
 using cobalt::bindings::testing::ImplementedInterface;
 using cobalt::bindings::testing::IndexedGetterInterface;
@@ -207,7 +204,6 @@
 using cobalt::bindings::testing::MozjsExceptionsInterface;
 using cobalt::bindings::testing::MozjsExtendedIDLAttributesInterface;
 using cobalt::bindings::testing::MozjsGarbageCollectionTestInterface;
-using cobalt::bindings::testing::MozjsGetOpaqueRootInterface;
 using cobalt::bindings::testing::MozjsGlobalInterfaceParent;
 using cobalt::bindings::testing::MozjsImplementedInterface;
 using cobalt::bindings::testing::MozjsIndexedGetterInterface;
@@ -1157,10 +1153,6 @@
       base::Bind(MozjsGarbageCollectionTestInterface::CreateProxy),
       base::Bind(MozjsGarbageCollectionTestInterface::PrototypeClass));
   wrapper_factory->RegisterWrappableType(
-      GetOpaqueRootInterface::GetOpaqueRootInterfaceWrappableType(),
-      base::Bind(MozjsGetOpaqueRootInterface::CreateProxy),
-      base::Bind(MozjsGetOpaqueRootInterface::PrototypeClass));
-  wrapper_factory->RegisterWrappableType(
       GlobalInterfaceParent::GlobalInterfaceParentWrappableType(),
       base::Bind(MozjsGlobalInterfaceParent::CreateProxy),
       base::Bind(MozjsGlobalInterfaceParent::PrototypeClass));
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template
index 12ad4c0..99ca683 100644
--- a/src/cobalt/bindings/mozjs/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -90,30 +90,6 @@
 {% block implementation %}
 namespace {
 
-{% if get_opaque_root %}
-Wrappable* GetOpaqueRootFromWrappable(
-    const scoped_refptr<Wrappable>& wrappable) {
-  {{impl_class}}* impl =
-      base::polymorphic_downcast<{{impl_class}}*>(wrappable.get());
-  return impl->{{get_opaque_root}}();
-}
-
-{% endif %}
-{% if add_opaque_roots %}
-void GetReachableWrappables(const scoped_refptr<Wrappable>& wrappable,
-    WrapperPrivate::WrappableVector* reachable) {
-  DCHECK(reachable);
-  {{impl_class}}* impl =
-      base::polymorphic_downcast<{{impl_class}}*>(wrappable.get());
-{% for reachable_object in add_opaque_roots %}
-  Wrappable* reachable_{{loop.index0}} = impl->{{reachable_object}}();
-  if (reachable_{{loop.index0}}) {
-    reachable->push_back(reachable_{{loop.index0}});
-  }
-{% endfor %}
-}
-
-{% endif %}
 {% if named_property_getter %}
 bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object,
                               const std::string& property_name) {
@@ -843,20 +819,7 @@
   JS::RootedObject proxy(context,
       ProxyHandler::NewProxy(context, new_object, prototype, NULL,
                              proxy_handler.Pointer()));
-{% if add_opaque_roots or get_opaque_root %}
-  WrapperPrivate::GetOpaqueRootFunction get_root;
-  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
-{% if get_opaque_root %}
-  get_root = base::Bind(&GetOpaqueRootFromWrappable);
-{% endif %}
-{% if add_opaque_roots %}
-  get_reachable_wrappables = base::Bind(&GetReachableWrappables);
-{% endif %}
-  WrapperPrivate::AddPrivateData(
-      context, proxy, wrappable, get_root, get_reachable_wrappables);
-{% else %}
   WrapperPrivate::AddPrivateData(context, proxy, wrappable);
-{% endif %}
   return proxy;
 }
 
diff --git a/src/cobalt/bindings/mozjs45/templates/interface.cc.template b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
index 6296ec9..abe82f9 100644
--- a/src/cobalt/bindings/mozjs45/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
@@ -105,30 +105,6 @@
 {% block implementation %}
 namespace {
 
-{% if get_opaque_root %}
-Wrappable* GetOpaqueRootFromWrappable(
-    const scoped_refptr<Wrappable>& wrappable) {
-  {{impl_class}}* impl =
-      base::polymorphic_downcast<{{impl_class}}*>(wrappable.get());
-  return impl->{{get_opaque_root}}();
-}
-
-{% endif %}
-{% if add_opaque_roots %}
-void GetReachableWrappables(const scoped_refptr<Wrappable>& wrappable,
-    WrapperPrivate::WrappableVector* reachable) {
-  DCHECK(reachable);
-  {{impl_class}}* impl =
-      base::polymorphic_downcast<{{impl_class}}*>(wrappable.get());
-{% for reachable_object in add_opaque_roots %}
-  Wrappable* reachable_{{loop.index0}} = impl->{{reachable_object}}();
-  if (reachable_{{loop.index0}}) {
-    reachable->push_back(reachable_{{loop.index0}});
-  }
-{% endfor %}
-}
-
-{% endif %}
 {% if named_property_getter %}
 bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object,
                               const std::string& property_name) {
@@ -861,20 +837,7 @@
   JS::RootedObject proxy(context,
       ProxyHandler::NewProxy(
           context, proxy_handler.Pointer(), new_object, prototype));
-{% if add_opaque_roots or get_opaque_root %}
-  WrapperPrivate::GetOpaqueRootFunction get_root;
-  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
-{% if get_opaque_root %}
-  get_root = base::Bind(&GetOpaqueRootFromWrappable);
-{% endif %}
-{% if add_opaque_roots %}
-  get_reachable_wrappables = base::Bind(&GetReachableWrappables);
-{% endif %}
-  WrapperPrivate::AddPrivateData(
-      context, proxy, wrappable, get_root, get_reachable_wrappables);
-{% else %}
   WrapperPrivate::AddPrivateData(context, proxy, wrappable);
-{% endif %}
   return proxy;
 }
 
diff --git a/src/cobalt/bindings/templates/dictionary.h.template b/src/cobalt/bindings/templates/dictionary.h.template
index 2999e8b..686ed73 100644
--- a/src/cobalt/bindings/templates/dictionary.h.template
+++ b/src/cobalt/bindings/templates/dictionary.h.template
@@ -62,7 +62,11 @@
 namespace {{component}} {
 {% endfor %}
 
+{% if parent %}
+class {{class_name}} : public {{parent}} {
+{% else %}
 class {{class_name}} {
+{% endif %}
  public:
   {{class_name}}() {
 {% for member in members %}
diff --git a/src/cobalt/bindings/testing/garbage_collection_test_interface.cc b/src/cobalt/bindings/testing/garbage_collection_test_interface.cc
index 2bc0177..75b7da9 100644
--- a/src/cobalt/bindings/testing/garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/testing/garbage_collection_test_interface.cc
@@ -58,15 +58,6 @@
   Join(this, next.get());
 }
 
-// The value of |GetOpaqueRoot| in the .idl. This ensures that nodes in the
-// same list have the same "root".
-script::Wrappable* GarbageCollectionTestInterface::GetHead() {
-  if (previous_) {
-    return previous_->GetHead();
-  }
-  return this;
-}
-
 void GarbageCollectionTestInterface::MakeHead() {
   if (previous_) {
     DCHECK(previous_->next_ == this);
@@ -99,6 +90,11 @@
   return ::cobalt::bindings::testing::instances.Get();
 }
 
+void GarbageCollectionTestInterface::TraceMembers(script::Tracer* tracer) {
+  tracer->Trace(previous_);
+  tracer->Trace(next_);
+}
+
 }  // namespace testing
 }  // namespace bindings
 }  // namespace cobalt
diff --git a/src/cobalt/bindings/testing/garbage_collection_test_interface.h b/src/cobalt/bindings/testing/garbage_collection_test_interface.h
index b976041..337d290 100644
--- a/src/cobalt/bindings/testing/garbage_collection_test_interface.h
+++ b/src/cobalt/bindings/testing/garbage_collection_test_interface.h
@@ -45,12 +45,10 @@
   void set_next(const scoped_refptr<GarbageCollectionTestInterface>& next);
   scoped_refptr<GarbageCollectionTestInterface> next() { return next_; }
 
-  // The value of |GetOpaqueRoot| in the .idl. This ensures that nodes in the
-  // same list have the same "root".
-  script::Wrappable* GetHead();
-
   static GarbageCollectionTestInterfaceVector& instances();
 
+  void TraceMembers(script::Tracer* tracer) OVERRIDE;
+
   DEFINE_WRAPPABLE_TYPE(GarbageCollectionTestInterface);
 
  private:
diff --git a/src/cobalt/bindings/testing/garbage_collection_test_interface.idl b/src/cobalt/bindings/testing/garbage_collection_test_interface.idl
index 3806100..bace8ed 100644
--- a/src/cobalt/bindings/testing/garbage_collection_test_interface.idl
+++ b/src/cobalt/bindings/testing/garbage_collection_test_interface.idl
@@ -13,12 +13,10 @@
 // limitations under the License.
 [
   Constructor,
-  // The head of the list is the "opaque root" of all nodes in the list.
-  GetOpaqueRoot=GetHead,
 ]
 interface GarbageCollectionTestInterface {
   // This interface represents a simple linked-list implementation used for
-  // tests around reachability of objects from opaque roots.
+  // tests around reachability of objects via |TraceMembers|.
   attribute GarbageCollectionTestInterface? previous;
   attribute GarbageCollectionTestInterface? next;
 };
diff --git a/src/cobalt/bindings/testing/get_opaque_root_interface.h b/src/cobalt/bindings/testing/get_opaque_root_interface.h
deleted file mode 100644
index 4c74c3d..0000000
--- a/src/cobalt/bindings/testing/get_opaque_root_interface.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef COBALT_BINDINGS_TESTING_GET_OPAQUE_ROOT_INTERFACE_H_
-#define COBALT_BINDINGS_TESTING_GET_OPAQUE_ROOT_INTERFACE_H_
-
-#include "cobalt/script/wrappable.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace cobalt {
-namespace bindings {
-namespace testing {
-
-class GetOpaqueRootInterface : public script::Wrappable {
- public:
-  MOCK_METHOD0(get_opaque_root_function_name, script::Wrappable*());
-  MOCK_METHOD0(add_opaque_root_function_name, script::Wrappable*());
-
-  DEFINE_WRAPPABLE_TYPE(GetOpaqueRootInterface);
-};
-
-}  // namespace testing
-}  // namespace bindings
-}  // namespace cobalt
-
-#endif  // COBALT_BINDINGS_TESTING_GET_OPAQUE_ROOT_INTERFACE_H_
diff --git a/src/cobalt/bindings/testing/get_opaque_root_interface.idl b/src/cobalt/bindings/testing/get_opaque_root_interface.idl
deleted file mode 100644
index 2dca51b..0000000
--- a/src/cobalt/bindings/testing/get_opaque_root_interface.idl
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Constructor attribute is added for test purposes.
-[
-  Constructor,
-  AddOpaqueRoots=(add_opaque_root_function_name),
-  GetOpaqueRoot=get_opaque_root_function_name
-]
-interface GetOpaqueRootInterface {};
diff --git a/src/cobalt/bindings/testing/get_opaque_root_test.cc b/src/cobalt/bindings/testing/get_opaque_root_test.cc
deleted file mode 100644
index e46a11e..0000000
--- a/src/cobalt/bindings/testing/get_opaque_root_test.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/bindings/testing/bindings_test_base.h"
-#include "cobalt/bindings/testing/get_opaque_root_interface.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cobalt {
-namespace bindings {
-namespace testing {
-
-namespace {
-class GetOpaqueRootInterfaceTest : public BindingsTestBase {
- public:
-  GetOpaqueRootInterfaceTest()
-      : test_mock_(new ::testing::NiceMock<GetOpaqueRootInterface>()) {
-    global_environment_->Bind(
-        "test", make_scoped_refptr<GetOpaqueRootInterface>((test_mock_)));
-  }
-
-  GetOpaqueRootInterface& test_mock() { return *test_mock_.get(); }
-
- private:
-  const scoped_refptr<GetOpaqueRootInterface> test_mock_;
-};
-}  // namespace
-
-TEST_F(GetOpaqueRootInterfaceTest, CallsFunction) {
-  EXPECT_CALL(test_mock(), add_opaque_root_function_name())
-      .Times(::testing::AtLeast(1));
-  CollectGarbage();
-}
-
-}  // namespace testing
-}  // namespace bindings
-}  // namespace cobalt
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp
index c32311a..bc92e7c 100644
--- a/src/cobalt/bindings/testing/testing.gyp
+++ b/src/cobalt/bindings/testing/testing.gyp
@@ -47,7 +47,6 @@
         'exceptions_interface.idl',
         'extended_idl_attributes_interface.idl',
         'garbage_collection_test_interface.idl',
-        'get_opaque_root_interface.idl',
         'global_interface_parent.idl',
         'indexed_getter_interface.idl',
         'interface_with_any.idl',
@@ -157,7 +156,6 @@
         'exceptions_bindings_test.cc',
         'extended_attributes_test.cc',
         'garbage_collection_test.cc',
-        'get_opaque_root_test.cc',
         'get_own_property_descriptor.cc',
         'getter_setter_test.cc',
         'global_interface_bindings_test.cc',
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 3e5aad6..61b714e 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -49,10 +49,13 @@
         'memory_tracker/tool/buffered_file_writer.h',
         'memory_tracker/tool/compressed_time_series_tool.cc',
         'memory_tracker/tool/compressed_time_series_tool.h',
+        'memory_tracker/tool/histogram_table_csv_base.h',
         'memory_tracker/tool/leak_finder_tool.cc',
         'memory_tracker/tool/leak_finder_tool.h',
         'memory_tracker/tool/log_writer_tool.cc',
         'memory_tracker/tool/log_writer_tool.h',
+        'memory_tracker/tool/malloc_stats_tool.cc',
+        'memory_tracker/tool/malloc_stats_tool.h',
         'memory_tracker/tool/memory_size_binner_tool.cc',
         'memory_tracker/tool/memory_size_binner_tool.h',
         'memory_tracker/tool/params.cc',
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index 685296f..294a22c 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -211,6 +211,7 @@
         '../audio/audio_node_channel_interpretation.idl',
         '../dom/blob_property_bag.idl',
         '../dom/dom_parser_supported_type.idl',
+        '../dom/event_init.idl',
         '../dom/media_source_end_of_stream_error.idl',
         '../dom/media_source_ready_state.idl',
         '../dom/mutation_observer_init.idl',
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc
index 71a4570..5743645 100644
--- a/src/cobalt/browser/memory_settings/auto_mem.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -114,12 +114,18 @@
 }
 
 void EnsureValuePositive(IntSetting* setting) {
+  if (!setting->valid()) {
+    return;
+  }
   if (setting->value() < 0) {
     setting->set_value(setting->source_type(), 0);
   }
 }
 
 void EnsureValuePositive(DimensionSetting* setting) {
+  if (!setting->valid()) {
+    return;
+  }
   const TextureDimensions value = setting->value();
   if (value.width() < 0 || value.height() < 0 || value.bytes_per_pixel() < 0) {
     setting->set_value(setting->source_type(), TextureDimensions());
@@ -127,6 +133,9 @@
 }
 
 void EnsureTwoBytesPerPixel(DimensionSetting* setting) {
+  if (!setting->valid()) {
+    return;
+  }
   TextureDimensions value = setting->value();
   if (value.bytes_per_pixel() != 2) {
     LOG(ERROR) << "Only two bytes per pixel are allowed for setting: "
@@ -151,7 +160,6 @@
   return sum;
 }
 
-
 // Creates the GPU setting.
 // This setting is unique because it may not be defined by command line, or
 // build. In this was, it can be unset.
diff --git a/src/cobalt/browser/memory_tracker/tool.cc b/src/cobalt/browser/memory_tracker/tool.cc
index e37356a..5083f99 100644
--- a/src/cobalt/browser/memory_tracker/tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool.cc
@@ -23,11 +23,13 @@
 #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_stats_tool.h"
 #include "cobalt/browser/memory_tracker/tool/memory_size_binner_tool.h"
 #include "cobalt/browser/memory_tracker/tool/print_csv_tool.h"
 #include "cobalt/browser/memory_tracker/tool/print_tool.h"
 #include "cobalt/browser/memory_tracker/tool/tool_impl.h"
 #include "cobalt/browser/memory_tracker/tool/tool_thread.h"
+
 #include "nb/analytics/memory_tracker_helpers.h"
 #include "nb/lexical_cast.h"
 #include "starboard/log.h"
@@ -62,6 +64,7 @@
   kAllocationLogger,
   kLeakTracer,
   kJavascriptLeakTracer,
+  kMallocStats,
 };
 
 struct SwitchVal {
@@ -185,6 +188,12 @@
       "Automatically detects Javascript leaks and reports them in CSV format.",
       kJavascriptLeakTracer);
 
+  SwitchVal malloc_stats_tool(
+      "malloc_stats",
+      "Queries the allocation system for memory usage. This is the most "
+      "lightweight tool. Output is CSV format.",
+      kMallocStats);
+
   SwitchMap switch_map;
   switch_map[ParseToolName(startup_tool.tool_name)] = startup_tool;
   switch_map[ParseToolName(continuous_printer_tool.tool_name)] =
@@ -198,6 +207,8 @@
   switch_map[ParseToolName(js_leak_tracing_tool.tool_name)] =
       js_leak_tracing_tool;
 
+  switch_map[ParseToolName(malloc_stats_tool.tool_name)] = malloc_stats_tool;
+
   std::string tool_name = ParseToolName(command_arg);
   std::string tool_arg = ParseToolArg(command_arg);
 
@@ -346,6 +357,10 @@
       tool_ptr.reset(leak_finder.release());
       break;
     }
+    case kMallocStats: {
+      tool_ptr.reset(new MallocStatsTool);
+      break;
+    }
     default: {
       SB_NOTREACHED() << "Unhandled case.";
       break;
diff --git a/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h b/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h
new file mode 100644
index 0000000..0c8ef3e
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h
@@ -0,0 +1,178 @@
+// 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_HISTOGRAM_TABLE_CSV_BASE_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_HISTOGRAM_TABLE_CSV_BASE_H_
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// This HistogramTableCSV provides most of the functionality for generating a
+// csv table from a histogram of data.
+//
+// Subclasses need to override ValueToString(...) so that the
+// data-type can be stringified during the call to ToString().
+//
+// Example:
+//  class MyHistogram : public HistogramTableCSV<int64_t>() {...}
+//  MyHistogram my_histogram;
+//  my_histogram.set_title("Memory Values");
+//
+//  // Add first row.
+//  my_histogram.BeginRow(time_delta);
+//  my_histogram.AddRowValue("ColumnA", 0);
+//  my_histogram.AddRowValue("ColumnB", 0);
+//  my_histogram.FinilizeRow();
+//
+//  // Add second row.
+//  my_histogram.BeginRow(time_delta);
+//  my_histogram.AddRowValue("ColumnA", 1);
+//  my_histogram.AddRowValue("ColumnB", 2);
+//  my_histogram.AddRowValue("ColumnC", 1);  // Ok, ColumnC will be autofilled.
+//  my_histogram.FinilizeRow();
+//
+//  Print(my_histogram.ToString());
+template <typename ValueType>
+class HistogramTableCSVBase {
+ public:
+  typedef std::map<std::string, std::vector<ValueType> > TableData;
+
+  // default_value is used to auto-fill values, such as when a new column
+  // is introduced.
+  explicit HistogramTableCSVBase(const ValueType& default_value)
+      : default_value_(default_value) {}
+
+  virtual std::string ValueToString(const ValueType& value) const = 0;
+
+  void set_title(const std::string& title) {
+    title_ = title;
+  }
+
+  void BeginRow(const base::TimeDelta time_value) {
+    time_values_.push_back(time_value);
+  }
+
+  void AddRowValue(const std::string& column_key, const ValueType& value) {
+    if (time_values_.empty()) {
+      NOTREACHED() << "table_data_ was empty.";
+      time_values_.push_back(base::TimeDelta());
+    }
+    const size_t n = time_values_.size();
+    std::vector<ValueType>& column = table_data_[column_key];
+    while (column.size() < n-1) {
+      column.push_back(default_value_);
+    }
+    column.push_back(value);
+  }
+
+  void FinalizeRow() {
+    const size_t n = time_values_.size();
+    for (typename TableData::iterator it = table_data_.begin();
+         it != table_data_.end(); ++it) {
+      std::vector<ValueType>& column = it->second;
+      while (column.size() < n) {
+        column.push_back(default_value_);
+      }
+    }
+  }
+
+  std::string ToString() const {
+    const char kSeperator[] = "//////////////////////////////////////////////";
+    std::stringstream ss;
+    ss << kSeperator << kNewLine;
+    if (title_.size()) {
+      ss << "// CSV of " << title_ << kNewLine;
+    }
+    for (size_t i = 0; i < NumberOfRows(); ++i) {
+      ss << StringifyRow(i);
+    }
+    ss << kSeperator;
+    return ss.str();
+  }
+
+  // All odd elements are removed. Effectively compressing the table in half.
+  void RemoveOddElements() {
+    memory_tracker::RemoveOddElements(&time_values_);
+    for (typename TableData::iterator it = table_data_.begin();
+         it != table_data_.end(); ++it) {
+      memory_tracker::RemoveOddElements(&it->second);
+    }
+  }
+
+  size_t NumberOfRows() const {
+    return time_values_.size();
+  }
+
+ protected:
+  static std::string JoinValues(const std::vector<std::string>& row_values) {
+    std::stringstream ss;
+    for (size_t i = 0; i < row_values.size(); ++i) {
+      ss << kQuote << row_values[i] << kQuote << kDelimiter;
+    }
+    ss << kNewLine;
+    return ss.str();
+  }
+
+  static std::string TimeToMinutesString(base::TimeDelta dt) {
+    double value_minutes = dt.InSecondsF() / 60.;
+    char buff[128];
+    SbStringFormatF(buff, sizeof(buff), "%.2f", value_minutes);
+    return std::string(buff);
+  }
+
+  std::string StringifyRow(size_t index) const {
+    if (index == 0) {
+      // Create header row.
+      std::vector<std::string> column_keys;
+      column_keys.push_back("Time(min)");
+      for (typename TableData::const_iterator it = table_data_.begin();
+           it != table_data_.end(); ++it) {
+        column_keys.push_back(SanitizeCSVKey(it->first));
+      }
+      return JoinValues(column_keys);
+    } else {
+      // Create data row.
+      std::vector<std::string> row_values;
+      row_values.push_back(TimeToMinutesString(time_values_[index]));
+      for (typename TableData::const_iterator it = table_data_.begin();
+           it != table_data_.end(); ++it) {
+        const std::vector<ValueType>& column = it->second;
+        const std::string value_str = ValueToString(column[index]);
+        row_values.push_back(value_str);
+      }
+      return JoinValues(row_values);
+    }
+  }
+
+  std::string title_;
+  ValueType default_value_;
+  std::vector<base::TimeDelta> time_values_;
+  TableData table_data_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_HISTOGRAM_TABLE_CSV_BASE_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.cc b/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.cc
new file mode 100644
index 0000000..5c77791
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.cc
@@ -0,0 +1,112 @@
+// 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_stats_tool.h"
+
+#include <string>
+#include <vector>
+
+#include "cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/analytics/memory_tracker.h"
+#include "starboard/string.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+namespace {
+
+// Takes in bytes -> outputs as megabytes.
+class MemoryBytesHistogramCSV : public HistogramTableCSVBase<int64_t> {
+ public:
+  MemoryBytesHistogramCSV() : HistogramTableCSVBase<int64_t>(0) {}
+  std::string ValueToString(const int64_t& bytes) const OVERRIDE {
+    return ToMegabyteString(bytes);
+  }
+
+  static std::string ToMegabyteString(int64_t bytes) {
+    double mega_bytes = static_cast<double>(bytes) / (1024.0 * 1024.0);
+    char buff[128];
+    SbStringFormatF(buff, sizeof(buff), "%.1f", mega_bytes);
+    return std::string(buff);
+  }
+};
+
+}  // namespace.
+
+MallocStatsTool::MallocStatsTool() {
+}
+
+std::string MallocStatsTool::tool_name() const  {
+  return "MallocStatsTool";
+}
+
+void MallocStatsTool::Run(Params* params) {
+  // Run function does almost nothing.
+  params->logger()->Output("MallocStatsTool running...");
+
+  Timer output_timer(base::TimeDelta::FromSeconds(30));
+  Timer sample_timer(base::TimeDelta::FromMilliseconds(50));
+
+  MemoryBytesHistogramCSV histogram_table;
+  histogram_table.set_title("Malloc Stats");
+
+  // If we get a finish signal then this will break out of the loop.
+  while (!params->wait_for_finish_signal(250 * kSbTimeMillisecond)) {
+    // LOG CSV.
+    if (output_timer.UpdateAndIsExpired()) {
+      std::stringstream ss;
+      ss << kNewLine << histogram_table.ToString() << kNewLine << kNewLine;
+      params->logger()->Output(ss.str());
+    }
+
+    // ADD A HISTOGRAM SAMPLE.
+    if (sample_timer.UpdateAndIsExpired()) {
+      // Take a sample.
+      nb::analytics::MemoryStats memory_stats =
+          nb::analytics::GetProcessMemoryStats();
+
+      histogram_table.BeginRow(params->time_since_start());
+      histogram_table.AddRowValue(
+          "TotalCpuMemory(MB)", memory_stats.total_cpu_memory);
+      histogram_table.AddRowValue(
+          "UsedCpuMemory(MB)", memory_stats.used_cpu_memory);
+      histogram_table.AddRowValue(
+          "TotalGpuMemory(MB)", memory_stats.total_gpu_memory);
+      histogram_table.AddRowValue(
+          "UsedGpuMemory(MB)", memory_stats.used_gpu_memory);
+      histogram_table.FinalizeRow();
+    }
+
+    // COMPRESS TABLE WHEN FULL.
+    //
+    // Table is full, therefore eliminate half of the elements.
+    // Reduce sample frequency to match.
+    if (histogram_table.NumberOfRows() >= 100) {
+      // Compression step.
+      histogram_table.RemoveOddElements();
+
+      // By double the sampling time this keeps the table linear with
+      // respect to time. If sampling time was not doubled then there
+      // would be time distortion in the graph.
+      sample_timer.ScaleTimerAndReset(2.0);
+    }
+  }
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.h b/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.h
new file mode 100644
index 0000000..85381f9
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/malloc_stats_tool.h
@@ -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.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_MALLOC_STATS_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_MALLOC_STATS_TOOL_H_
+
+#include <string>
+
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Generates a CSV table of the memory stats. This tool does not use
+// per-allocation tracking and therefore has a lower memory footprint than
+// most other tools.
+class MallocStatsTool : public AbstractTool {
+ public:
+  MallocStatsTool();
+  std::string tool_name() const OVERRIDE;
+  void Run(Params* params) OVERRIDE;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_MALLOC_STATS_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.h b/src/cobalt/browser/memory_tracker/tool/tool_impl.h
index 9edef18..b8d4ad3 100644
--- a/src/cobalt/browser/memory_tracker/tool/tool_impl.h
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.h
@@ -58,6 +58,7 @@
  public:
   virtual ~AbstractLogger() {}
   virtual void Output(const char* str) = 0;
+  void Output(const std::string& str) { Output(str.c_str()); }
   virtual void Flush() = 0;
 };
 
diff --git a/src/cobalt/browser/memory_tracker/tool/util.cc b/src/cobalt/browser/memory_tracker/tool/util.cc
index 09068cb..b25c481 100644
--- a/src/cobalt/browser/memory_tracker/tool/util.cc
+++ b/src/cobalt/browser/memory_tracker/tool/util.cc
@@ -93,15 +93,23 @@
   return ss.str();
 }
 
-Timer::Timer(base::TimeDelta dt)
-    : start_time_(base::TimeTicks::Now()), time_before_expiration_(dt) {}
+Timer::Timer(base::TimeDelta delta_time) {
+  start_time_ = Now();
+  time_before_expiration_ = delta_time;
+}
 
-void Timer::Restart() { start_time_ = base::TimeTicks::Now(); }
+Timer::Timer(base::TimeDelta delta_time, Timer::TimeFunctor time_functor) {
+  time_function_override_ = time_functor;
+  start_time_ = Now();
+  time_before_expiration_ = delta_time;
+}
+
+void Timer::Restart() { start_time_ = Now(); }
 
 bool Timer::UpdateAndIsExpired() {
-  base::TimeTicks now_time = base::TimeTicks::Now();
-  base::TimeDelta dt = now_time - start_time_;
-  if (dt > time_before_expiration_) {
+  base::TimeTicks now_time = Now();
+  base::TimeDelta delta_time = now_time - start_time_;
+  if (delta_time >= time_before_expiration_) {
     start_time_ = now_time;
     return true;
   } else {
@@ -109,6 +117,22 @@
   }
 }
 
+base::TimeTicks Timer::Now() {
+  if (time_function_override_.is_null()) {
+    return base::TimeTicks::HighResNow();
+  } else {
+    return time_function_override_.Run();
+  }
+}
+
+void Timer::ScaleTimerAndReset(double scale) {
+  int64_t old_dt = time_before_expiration_.InMicroseconds();
+  int64_t new_dt =
+      static_cast<int64_t>(static_cast<double>(old_dt) * scale);
+  time_before_expiration_ = base::TimeDelta::FromMicroseconds(new_dt);
+  Restart();
+}
+
 const char* BaseNameFast(const char* file_name) {
   // Case: Linux.
   const char* end_pos = file_name + SbStringGetLength(file_name);
diff --git a/src/cobalt/browser/memory_tracker/tool/util.h b/src/cobalt/browser/memory_tracker/tool/util.h
index aa34660..02f2ca8 100644
--- a/src/cobalt/browser/memory_tracker/tool/util.h
+++ b/src/cobalt/browser/memory_tracker/tool/util.h
@@ -20,7 +20,11 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
+#include "base/logging.h"
 #include "base/time.h"
+#include "starboard/string.h"
+#include "starboard/types.h"
 
 namespace cobalt {
 namespace browser {
@@ -71,13 +75,20 @@
 // Simple timer class that fires periodically after dt time has elapsed.
 class Timer {
  public:
-  explicit Timer(base::TimeDelta dt);
+  typedef base::Callback<base::TimeTicks(void)> TimeFunctor;
+
+  explicit Timer(base::TimeDelta delta_time);
+  Timer(base::TimeDelta delta_time, TimeFunctor f);
+
   void Restart();
   bool UpdateAndIsExpired();  // Returns true if the expiration was triggered.
+  void ScaleTimerAndReset(double scale);
 
  private:
+  base::TimeTicks Now();
   base::TimeTicks start_time_;
   base::TimeDelta time_before_expiration_;
+  TimeFunctor time_function_override_;
 };
 
 struct AllocationSamples {
diff --git a/src/cobalt/browser/memory_tracker/tool/util_test.cc b/src/cobalt/browser/memory_tracker/tool/util_test.cc
index 07e9d63..988414d 100644
--- a/src/cobalt/browser/memory_tracker/tool/util_test.cc
+++ b/src/cobalt/browser/memory_tracker/tool/util_test.cc
@@ -18,16 +18,33 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace browser {
 namespace memory_tracker {
+namespace {
+
+class FakeTimeSource {
+ public:
+  explicit FakeTimeSource(base::TimeTicks value) : static_time_(value) {}
+  void set_static_time(base::TimeTicks value) {
+    static_time_ = value;
+  }
+  base::TimeTicks GetTime() {
+    return static_time_;
+  }
+ private:
+  base::TimeTicks static_time_;
+};
+
+}  // namespace.
 
 // Tests the expectation that AllocationSizeBinner will correctly bin
 // allocations.
-TEST(MemoryTrackerUtilTest, RemoveString) {
+TEST(UtilTest, RemoveString) {
   std::string value = "abba";
   value = RemoveString(value, "bb");
   EXPECT_STREQ("aa", value.c_str());
@@ -35,14 +52,14 @@
 
 // Tests the expectation that AllocationSizeBinner will correctly bin
 // allocations.
-TEST(MemoryTrackerUtilTest, InsertCommasIntoNumberString) {
+TEST(UtilTest, InsertCommasIntoNumberString) {
   std::string value = "2345.54";
   std::string value_with_commas = InsertCommasIntoNumberString(value);
 
   EXPECT_STREQ("2,345.54", value_with_commas.c_str());
 }
 
-TEST(MemoryTrackerUtilTest, NumberFormatWithCommas) {
+TEST(UtilTest, NumberFormatWithCommas) {
   int value = 1000;
   std::string value_with_commas = NumberFormatWithCommas<int>(value);
 
@@ -51,7 +68,7 @@
 
 // Tests the expectation that RemoveOddElements() removes the odd elements of
 // a vector and resizes it.
-TEST(MemoryTrackerUtilTest, RemoveOddElements) {
+TEST(UtilTest, RemoveOddElements) {
   std::vector<int> values;
 
   // EVEN TEST.
@@ -84,7 +101,7 @@
 
 // Tests the expectation that GetLinearFit() generates the expected linear
 // regression values.
-TEST(MemoryTrackerUtilTest, GetLinearFit) {
+TEST(UtilTest, GetLinearFit) {
   std::vector<std::pair<int, int> > data;
   for (int i = 0; i < 10; ++i) {
     data.push_back(std::pair<int, int>(i+1, 2*i));
@@ -99,7 +116,7 @@
 
 // Test the expectation that BaseNameFast() works correctly for both windows
 // and linux path types.
-TEST(MemoryTrackerUtilTest, BaseNameFast) {
+TEST(UtilTest, BaseNameFast) {
   const char* linux_path = "directory/filename.cc";
   const char* win_path = "directory\\filename.cc";
 
@@ -107,6 +124,32 @@
   EXPECT_STREQ("filename.cc", BaseNameFast(win_path));
 }
 
+TEST(UtilTest, TimerUse) {
+  base::TimeTicks initial_time = base::TimeTicks::Now();
+  FakeTimeSource time_source(initial_time);
+
+  Timer::TimeFunctor time_functor =
+      base::Bind(&FakeTimeSource::GetTime, base::Unretained(&time_source));
+
+  Timer test_timer(base::TimeDelta::FromSeconds(30), time_functor);
+  EXPECT_FALSE(test_timer.UpdateAndIsExpired());  // 0 time has elapsed.
+
+  time_source.set_static_time(initial_time + base::TimeDelta::FromSeconds(29));
+  // 29 seconds has elapsed, which is less than the 30 seconds required for
+  // the timer to fire.
+  EXPECT_FALSE(test_timer.UpdateAndIsExpired());
+  time_source.set_static_time(initial_time + base::TimeDelta::FromSeconds(30));
+  // 31 seconds has elapsed, which means that the next call to
+  // UpdateAndIsExpired() should succeed.
+  EXPECT_TRUE(test_timer.UpdateAndIsExpired());
+  // Now that the value fired, expect that it won't fire again (until the next
+  // 30 seconds has passed).
+  EXPECT_FALSE(test_timer.UpdateAndIsExpired());
+  time_source.set_static_time(initial_time + base::TimeDelta::FromSeconds(60));
+  EXPECT_TRUE(test_timer.UpdateAndIsExpired());
+  EXPECT_FALSE(test_timer.UpdateAndIsExpired());
+}
+
 }  // namespace memory_tracker
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index c2eb52e..fcddd62 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-55341
\ No newline at end of file
+56829
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index 0c93ea2..80b9306 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -438,7 +438,7 @@
     # during system startup.  To allocate a large chunk at startup helps with
     # reducing frafmentation and can avoid failures to allocate incrementally.
     # This can be set to 0.
-    'cobalt_media_buffer_initial_capacity%': 26 * 1024 * 1024,
+    'cobalt_media_buffer_initial_capacity%': 0 * 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
diff --git a/src/cobalt/cssom/cssom.gyp b/src/cobalt/cssom/cssom.gyp
index ecb8dad..08156e5 100644
--- a/src/cobalt/cssom/cssom.gyp
+++ b/src/cobalt/cssom/cssom.gyp
@@ -234,8 +234,15 @@
         'url_value.cc',
         'url_value.h',
       ],
+      'export_dependent_settings': [
+        # Additionally, ensure that the include directories for generated
+        # headers are put on the include directories for targets that depend
+        # on this one.
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+      ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
         '<(DEPTH)/cobalt/dom/dom_exception.gyp:dom_exception',
         '<(DEPTH)/cobalt/math/math.gyp:math',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index 09a671c..32d5f9e 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -75,7 +75,9 @@
 #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
       partial_layout_is_enabled_(true),
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
-      navigation_start_clock_(options.navigation_start_clock),
+      navigation_start_clock_(options.navigation_start_clock
+                                  ? options.navigation_start_clock
+                                  : new base::SystemMonotonicClock()),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           default_timeline_(new DocumentTimeline(this, 0))),
       user_agent_style_sheet_(options.user_agent_style_sheet),
@@ -329,7 +331,11 @@
   return NULL;
 }
 
+// https://www.w3.org/TR/html5/editing.html#dom-document-activeelement
 scoped_refptr<Element> Document::active_element() const {
+  // The activeElement attribute on Document objects must return the element in
+  // the document that is focused. If no element in the Document is focused,
+  // this must return the body element.
   if (!active_element_) {
     return body();
   } else {
@@ -385,27 +391,11 @@
 }
 
 void Document::SetActiveElement(Element* active_element) {
-  // Invalidate matching rules on old and new active element.
-  if (active_element_) {
-    HTMLElement* html_element = active_element_->AsHTMLElement();
-    if (html_element) {
-      html_element->InvalidateMatchingRulesRecursively();
-    }
-  }
   if (active_element) {
-    HTMLElement* html_element = active_element->AsHTMLElement();
-    if (html_element) {
-      html_element->InvalidateMatchingRulesRecursively();
-    }
     active_element_ = base::AsWeakPtr(active_element);
   } else {
     active_element_.reset();
   }
-
-  // Record mutation and trigger layout.
-  is_computed_style_dirty_ = true;
-  RecordMutation();
-  FOR_EACH_OBSERVER(DocumentObserver, observers_, OnFocusChanged());
 }
 
 void Document::IncreaseLoadingCounter() { ++loading_counter_; }
@@ -461,6 +451,12 @@
   csp_delegate_->NotifyUrlChanged(url);
 }
 
+void Document::OnFocusChange() {
+  is_computed_style_dirty_ = true;
+  RecordMutation();
+  FOR_EACH_OBSERVER(DocumentObserver, observers_, OnFocusChanged());
+}
+
 void Document::OnCSSMutation() {
   // Something in the document's CSS rules has been modified, but we don't know
   // what, so set the flag indicating that rule matching needs to be done.
@@ -594,6 +590,58 @@
   }
 }
 
+void Document::UpdateComputedStyleOnElementAndAncestor(HTMLElement* element) {
+  if (!element || element->node_document() != this ||
+      !is_computed_style_dirty_) {
+    return;
+  }
+
+  UpdateSelectorTree();
+  UpdateKeyframes();
+  UpdateFontFaces();
+
+  base::TimeDelta style_change_event_time =
+      base::TimeDelta::FromMillisecondsD(*default_timeline_->current_time());
+
+  // Find all ancestors of the element until the document.
+  std::vector<HTMLElement*> ancestors;
+  while (true) {
+    ancestors.push_back(element);
+    if (element->parent_node() == dynamic_cast<Node*>(this)) {
+      break;
+    }
+    Element* parent_element = element->parent_element();
+    if (!parent_element) {
+      return;
+    }
+    element = parent_element->AsHTMLElement();
+    if (!element) {
+      return;
+    }
+  }
+
+  // Update computed styles on the ancestors and the element.
+  HTMLElement* previous_element = NULL;
+  bool ancestors_were_valid = true;
+  for (std::vector<HTMLElement*>::reverse_iterator it = ancestors.rbegin();
+       it != ancestors.rend(); ++it) {
+    HTMLElement* current_element = *it;
+    bool is_valid = ancestors_were_valid &&
+                    current_element->matching_rules_valid() &&
+                    current_element->computed_style_valid();
+    if (!is_valid) {
+      DCHECK(initial_computed_style_declaration_);
+      DCHECK(initial_computed_style_data_);
+      current_element->UpdateComputedStyle(
+          previous_element ? previous_element->css_computed_style_declaration()
+                           : initial_computed_style_declaration_,
+          initial_computed_style_data_, style_change_event_time);
+    }
+    previous_element = current_element;
+    ancestors_were_valid = is_valid;
+  }
+}
+
 void Document::SampleTimelineTime() { default_timeline_->Sample(); }
 
 #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index 4e63114..fd9602c 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -246,6 +246,13 @@
     return keyframes_map_;
   }
 
+  // Returns whether the document has browsing context. Having the browsing
+  // context means the document is shown on the screen.
+  //   https://www.w3.org/TR/html5/browsers.html#browsing-context
+  bool HasBrowsingContext() { return !!window_; }
+
+  void set_window(Window* window) { window_ = window; }
+
   // Sets the active element of the document.
   void SetActiveElement(Element* active_element);
 
@@ -265,6 +272,10 @@
   //       (see https://www.w3.org/TR/dom/#mutation-observers).
   void RecordMutation();
 
+  // Called when the focus changes. This should be called only once when the
+  // focus is shifted from one element to another.
+  void OnFocusChange();
+
   // From cssom::MutationObserver.
   void OnCSSMutation() OVERRIDE;
 
@@ -281,6 +292,10 @@
   // Matching rules, media rules, font faces and key frames are also updated.
   void UpdateComputedStyles();
 
+  // Updates the computed styles of the element and all its ancestors.
+  // Matching rules, media rules, font faces and key frames are also updated.
+  void UpdateComputedStyleOnElementAndAncestor(HTMLElement* element);
+
   // Manages the clock used by Web Animations.
   //     https://www.w3.org/TR/web-animations
   // This clock is also used for requestAnimationFrame() callbacks, according
@@ -351,11 +366,6 @@
   // Animations, using all the style sheets in the document.
   void UpdateKeyframes();
 
-  // Returns whether the document has browsing context. Having the browsing
-  // context means the document is shown on the screen.
-  //   https://www.w3.org/TR/html5/browsers.html#browsing-context
-  bool HasBrowsingContext() { return !!window_; }
-
   // Reference to HTML element context.
   HTMLElementContext* const html_element_context_;
   // Reference to the associated window object.
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 6738764..9e4437d 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -347,6 +347,7 @@
         'testing/stub_css_parser.h',
         'testing/stub_script_runner.cc',
         'testing/stub_script_runner.h',
+        'testing/stub_window.h',
       ],
       'dependencies': [
         # TODO: Remove the dependency below, it works around the fact that
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 7ec5d03..9954088 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -69,6 +69,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/browser/browser.gyp:browser',
         '<(DEPTH)/cobalt/css_parser/css_parser.gyp:css_parser',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
diff --git a/src/cobalt/dom/dom_token_list.cc b/src/cobalt/dom/dom_token_list.cc
index 0bc8b8e..31ab686 100644
--- a/src/cobalt/dom/dom_token_list.cc
+++ b/src/cobalt/dom/dom_token_list.cc
@@ -188,6 +188,10 @@
   return tokens_;
 }
 
+void DOMTokenList::TraceMembers(script::Tracer* tracer) {
+  tracer->Trace(element());
+}
+
 DOMTokenList::~DOMTokenList() { GlobalStats::GetInstance()->Remove(this); }
 
 // Algorithm for RunUpdateSteps:
diff --git a/src/cobalt/dom/dom_token_list.h b/src/cobalt/dom/dom_token_list.h
index 40e8a31..fb29a8c 100644
--- a/src/cobalt/dom/dom_token_list.h
+++ b/src/cobalt/dom/dom_token_list.h
@@ -55,6 +55,8 @@
   // The associated element.
   Element* element() { return element_; }
 
+  void TraceMembers(script::Tracer* tracer) OVERRIDE;
+
   DEFINE_WRAPPABLE_TYPE(DOMTokenList);
 
  private:
diff --git a/src/cobalt/dom/dom_token_list.idl b/src/cobalt/dom/dom_token_list.idl
index 721ba4e..66b16f6 100644
--- a/src/cobalt/dom/dom_token_list.idl
+++ b/src/cobalt/dom/dom_token_list.idl
@@ -14,7 +14,6 @@
 
 // https://www.w3.org/TR/dom/#domtokenlist
 
-[AddOpaqueRoots=(element)]
 interface DOMTokenList {
   readonly attribute unsigned long length;
   getter DOMString? item(unsigned long index);
diff --git a/src/cobalt/dom/eme/media_key_session.cc b/src/cobalt/dom/eme/media_key_session.cc
index f66bf48..5c62383 100644
--- a/src/cobalt/dom/eme/media_key_session.cc
+++ b/src/cobalt/dom/eme/media_key_session.cc
@@ -29,10 +29,11 @@
 // See step 3.1 of
 // https://www.w3.org/TR/encrypted-media/#dom-mediakeys-createsession.
 MediaKeySession::MediaKeySession(
-    scoped_ptr<media::DrmSystem::Session> drm_system_session,
+    const scoped_refptr<media::DrmSystem>& drm_system,
     script::ScriptValueFactory* script_value_factory,
     const ClosedCallback& closed_callback)
-    : drm_system_session_(drm_system_session.Pass()),
+    : drm_system_(drm_system),
+      drm_system_session_(drm_system->CreateSession()),
       script_value_factory_(script_value_factory),
       uninitialized_(true),
       callable_(false),
@@ -41,8 +42,9 @@
           this, script_value_factory->CreateBasicPromise<void>())),
       initiated_by_generate_request_(false) {}
 
-// Session ID should be empty for uninitialized sessions according to step 3.1
-// of https://www.w3.org/TR/encrypted-media/#dom-mediakeys-createsession.
+// According to the step 3.1 of
+// https://www.w3.org/TR/encrypted-media/#dom-mediakeys-createsession,
+// session ID should be empty until the first request is generated successfully.
 std::string MediaKeySession::session_id() const {
   return drm_system_session_->id().value_or("");
 }
diff --git a/src/cobalt/dom/eme/media_key_session.h b/src/cobalt/dom/eme/media_key_session.h
index 4419362..cad6671 100644
--- a/src/cobalt/dom/eme/media_key_session.h
+++ b/src/cobalt/dom/eme/media_key_session.h
@@ -40,7 +40,7 @@
   typedef base::Callback<void(MediaKeySession* session)> ClosedCallback;
 
   // Custom, not in any spec.
-  MediaKeySession(scoped_ptr<media::DrmSystem::Session> drm_system_session,
+  MediaKeySession(const scoped_refptr<media::DrmSystem>& drm_system,
                   script::ScriptValueFactory* script_value_factory,
                   const ClosedCallback& closed_callback);
 
@@ -68,6 +68,11 @@
   void OnSessionDidNotUpdate(VoidPromiseValue::Reference* promise_reference);
   void OnClosed();
 
+  // 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_|
+  // from each session.
+  const scoped_refptr<media::DrmSystem> drm_system_;
   scoped_ptr<media::DrmSystem::Session> drm_system_session_;
   script::ScriptValueFactory* const script_value_factory_;
   bool uninitialized_;
diff --git a/src/cobalt/dom/eme/media_keys.cc b/src/cobalt/dom/eme/media_keys.cc
index 861d44d..5a06fc4 100644
--- a/src/cobalt/dom/eme/media_keys.cc
+++ b/src/cobalt/dom/eme/media_keys.cc
@@ -38,9 +38,12 @@
   }
 
   // 3. Let session be a new MediaKeySession object.
+  //
+  // |MediaKeys| are passed to |MediaKeySession| as weak pointer because the
+  // order of destruction is not guaranteed due to JavaScript memory management.
   scoped_refptr<MediaKeySession> session(new MediaKeySession(
-      drm_system_->CreateSession(), script_value_factory_,
-      base::Bind(&MediaKeys::OnSessionClosed, base::Unretained(this))));
+      drm_system_, script_value_factory_,
+      base::Bind(&MediaKeys::OnSessionClosed, AsWeakPtr())));
   open_sessions_.push_back(session);
   return session;
 }
diff --git a/src/cobalt/dom/eme/media_keys.h b/src/cobalt/dom/eme/media_keys.h
index 52013df..5531904 100644
--- a/src/cobalt/dom/eme/media_keys.h
+++ b/src/cobalt/dom/eme/media_keys.h
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "cobalt/dom/buffer_source.h"
 #include "cobalt/dom/eme/media_key_session.h"
 #include "cobalt/dom/eme/media_key_session_type.h"
@@ -34,7 +34,8 @@
 // Represents a set of keys that an associated HTMLMediaElement can use
 // for decryption of media data during playback.
 //   https://www.w3.org/TR/encrypted-media/#mediakeys-interface
-class MediaKeys : public script::Wrappable {
+class MediaKeys : public script::Wrappable,
+                  public base::SupportsWeakPtr<MediaKeys> {
  public:
   // Custom, not in any spec.
 
@@ -57,7 +58,7 @@
   void OnSessionClosed(MediaKeySession* session);
 
   script::ScriptValueFactory* script_value_factory_;
-  scoped_ptr<media::DrmSystem> drm_system_;
+  scoped_refptr<media::DrmSystem> drm_system_;
 
   // A MediaKeySession object shall not be destroyed and shall continue
   // to receive events if it is not closed and the MediaKeys object that created
diff --git a/src/cobalt/dom/event.cc b/src/cobalt/dom/event.cc
index 3aac0af..2484c59 100644
--- a/src/cobalt/dom/event.cc
+++ b/src/cobalt/dom/event.cc
@@ -40,6 +40,23 @@
   InitEventInternal(base::Token(type), false, false);
 }
 
+Event::Event(base::Token type, const EventInit& eventInitDict)
+    : event_phase_(kNone),
+      time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+  SB_DCHECK(eventInitDict.has_bubbles());
+  SB_DCHECK(eventInitDict.has_cancelable());
+  InitEventInternal(type, eventInitDict.bubbles(), eventInitDict.cancelable());
+}
+
+Event::Event(const std::string& type, const EventInit& eventInitDict)
+    : event_phase_(kNone),
+      time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+  SB_DCHECK(eventInitDict.has_bubbles());
+  SB_DCHECK(eventInitDict.has_cancelable());
+  InitEventInternal(base::Token(type), eventInitDict.bubbles(),
+                    eventInitDict.cancelable());
+}
+
 Event::Event(base::Token type, Bubbles bubbles, Cancelable cancelable)
     : event_phase_(kNone),
       time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
@@ -59,6 +76,15 @@
 }
 
 void Event::InitEvent(const std::string& type, bool bubbles, bool cancelable) {
+  // Our event is for single use only.
+  DCHECK(!IsBeingDispatched());
+  DCHECK(!target());
+  DCHECK(!current_target());
+
+  if (IsBeingDispatched() || target() || current_target()) {
+    return;
+  }
+
   InitEventInternal(base::Token(type), bubbles, cancelable);
 }
 
@@ -71,15 +97,6 @@
 }
 
 void Event::InitEventInternal(base::Token type, bool bubbles, bool cancelable) {
-  // Our event is for single use only.
-  DCHECK(!IsBeingDispatched());
-  DCHECK(!target());
-  DCHECK(!current_target());
-
-  if (IsBeingDispatched() || target() || current_target()) {
-    return;
-  }
-
   type_ = type;
   bubbles_ = bubbles;
   cancelable_ = cancelable;
diff --git a/src/cobalt/dom/event.h b/src/cobalt/dom/event.h
index 97daeaa..090066d 100644
--- a/src/cobalt/dom/event.h
+++ b/src/cobalt/dom/event.h
@@ -19,6 +19,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "cobalt/base/token.h"
+#include "cobalt/dom/event_init.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -55,7 +56,10 @@
   // Creates an event that cannot be bubbled and cancelled.
   explicit Event(base::Token type);
   explicit Event(const std::string& type);
+  Event(base::Token type, const EventInit& eventInitDict);
+  Event(const std::string& type, const EventInit& eventInitDict);
   Event(base::Token type, Bubbles bubbles, Cancelable cancelable);
+
   ~Event() OVERRIDE;
 
   // Web API: Event
diff --git a/src/cobalt/dom/event.idl b/src/cobalt/dom/event.idl
index a27e3e5..cd863e1 100644
--- a/src/cobalt/dom/event.idl
+++ b/src/cobalt/dom/event.idl
@@ -14,7 +14,7 @@
 
 // https://www.w3.org/TR/dom/#event
 
-[Constructor(DOMString type)]
+[Constructor(DOMString type, optional EventInit eventInitDict)]
 interface Event {
   readonly attribute DOMString type;
   readonly attribute EventTarget? target;
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/cobalt/dom/event_init.idl
similarity index 73%
rename from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
rename to src/cobalt/dom/event_init.idl
index fff4bd9..f0974f5 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/cobalt/dom/event_init.idl
@@ -12,10 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+// https://www.w3.org/TR/dom/#eventinit
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
-}
+dictionary EventInit {
+  boolean bubbles = false;
+  boolean cancelable = false;
+};
diff --git a/src/cobalt/dom/focus_event.cc b/src/cobalt/dom/focus_event.cc
index 69170ac..cacd086 100644
--- a/src/cobalt/dom/focus_event.cc
+++ b/src/cobalt/dom/focus_event.cc
@@ -24,5 +24,9 @@
                        const scoped_refptr<EventTarget>& related_target)
     : UIEvent(type), related_target_(related_target) {}
 
+FocusEvent::FocusEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+                       const scoped_refptr<EventTarget>& related_target)
+    : UIEvent(type, bubbles, cancelable), related_target_(related_target) {}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/focus_event.h b/src/cobalt/dom/focus_event.h
index a6727c1..57ba8a3 100644
--- a/src/cobalt/dom/focus_event.h
+++ b/src/cobalt/dom/focus_event.h
@@ -34,6 +34,9 @@
   FocusEvent(base::Token type,
              const scoped_refptr<EventTarget>& related_target);
 
+  FocusEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+             const scoped_refptr<EventTarget>& related_target);
+
   // Web API: FocusEvent
   //
   const scoped_refptr<EventTarget>& related_target() const {
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index e1e9485..af05c42 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -191,33 +191,42 @@
   SetAttribute("tabindex", base::Int32ToString(tab_index));
 }
 
+// Algorithm for Focus:
+//   https://www.w3.org/TR/html5/editing.html#dom-focus
 void HTMLElement::Focus() {
-  if (!IsFocusable()) {
+  // 1. If the element is marked as locked for focus, then abort these steps.
+  if (locked_for_focus_) {
     return;
   }
 
+  // 2. Mark the element as locked for focus.
+  locked_for_focus_ = true;
+
+  // 3. Run the focusing steps for the element.
+  RunFocusingSteps();
+
+  // 4. Unmark the element as locked for focus.
+  locked_for_focus_ = false;
+
+  // Custom, not in any spec.
   Document* document = node_document();
-  Element* old_active_element = document->active_element();
-  if (old_active_element == this->AsElement()) {
-    return;
+  if (document) {
+    document->OnFocusChange();
   }
-
-  document->SetActiveElement(this);
-
-  if (old_active_element) {
-    old_active_element->DispatchEvent(
-        new FocusEvent(base::Tokens::blur(), this));
-  }
-
-  DispatchEvent(new FocusEvent(base::Tokens::focus(), old_active_element));
 }
 
+// Algorithm for Blur:
+//   https://www.w3.org/TR/html5/editing.html#dom-blur
 void HTMLElement::Blur() {
-  Document* document = node_document();
-  if (document->active_element() == this->AsElement()) {
-    document->SetActiveElement(NULL);
+  // The blur() method, when invoked, should run the unfocusing steps for the
+  // element on which the method was called instead. User agents may selectively
+  // or uniformly ignore calls to this method for usability reasons.
+  RunUnFocusingSteps();
 
-    DispatchEvent(new FocusEvent(base::Tokens::blur(), NULL));
+  // Custom, not in any spec.
+  Document* document = node_document();
+  if (document) {
+    document->OnFocusChange();
   }
 }
 
@@ -519,6 +528,7 @@
 void HTMLElement::OnCSSMutation() {
   // Invalidate the computed style of this node.
   computed_style_valid_ = false;
+  descendant_computed_styles_valid_ = false;
 
   // Remove the style attribute value from the Element.
   Element::RemoveStyleAttribute();
@@ -650,16 +660,23 @@
     return;
   }
 
-  // Update computed style for this element's descendants.
+  // Update computed style for this element's descendants. Note that if
+  // descendant_computed_styles_valid_ flag is not set, the ancestors should
+  // still be considered invalid, which forces the computes styles to be updated
+  // on all children.
   for (Element* element = first_element_child(); element;
        element = element->next_element_sibling()) {
     HTMLElement* html_element = element->AsHTMLElement();
     if (html_element) {
       html_element->UpdateComputedStyleRecursively(
           css_computed_style_declaration(), root_computed_style,
-          style_change_event_time, is_valid, current_element_depth + 1);
+          style_change_event_time,
+          is_valid && descendant_computed_styles_valid_,
+          current_element_depth + 1);
     }
   }
+
+  descendant_computed_styles_valid_ = true;
 }
 
 void HTMLElement::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
@@ -667,12 +684,14 @@
   if (!cached_background_images_.empty()) {
     cached_background_images_.clear();
     computed_style_valid_ = false;
+    descendant_computed_styles_valid_ = false;
   }
   PurgeCachedBackgroundImagesOfDescendants();
 }
 
 void HTMLElement::InvalidateComputedStylesOfNodeAndDescendants() {
   computed_style_valid_ = false;
+  descendant_computed_styles_valid_ = false;
   InvalidateComputedStylesOfDescendants();
 }
 
@@ -707,10 +726,12 @@
 HTMLElement::HTMLElement(Document* document, base::Token local_name)
     : Element(document, local_name),
       dom_stat_tracker_(document->html_element_context()->dom_stat_tracker()),
+      locked_for_focus_(false),
       directionality_(kNoExplicitDirectionality),
       style_(new cssom::CSSDeclaredStyleDeclaration(
           document->html_element_context()->css_parser())),
       computed_style_valid_(false),
+      descendant_computed_styles_valid_(false),
       css_computed_style_declaration_(new cssom::CSSComputedStyleDeclaration()),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           transitions_adapter_(new DOMAnimatable(this))),
@@ -739,6 +760,25 @@
 
 void HTMLElement::OnMutation() { InvalidateMatchingRulesRecursively(); }
 
+void HTMLElement::OnRemovedFromDocument() {
+  Node::OnRemovedFromDocument();
+
+  // When an element that is focused stops being a focusable element, or stops
+  // being focused without another element being explicitly focused in its
+  // stead, the user agent should synchronously run the unfocusing steps for the
+  // affected element only.
+  // For example, this might happen because the element is removed from its
+  // Document, or has a hidden attribute added. It would also happen to an input
+  // element when the element gets disabled.
+  //   https://www.w3.org/TR/html5/editing.html#unfocusing-steps
+  Document* document = node_document();
+  DCHECK(document);
+  if (document->active_element() == this->AsElement()) {
+    RunUnFocusingSteps();
+    document->OnFocusChange();
+  }
+}
+
 void HTMLElement::OnSetAttribute(const std::string& name,
                                  const std::string& value) {
   if (name == "class" || name == "id") {
@@ -756,6 +796,117 @@
   }
 }
 
+// Algorithm for IsFocusable:
+//   https://www.w3.org/TR/html5/editing.html#focusable
+bool HTMLElement::IsFocusable() {
+  return HasTabindexFocusFlag() && IsBeingRendered();
+}
+
+// Algorithm for HasTabindexFocusFlag:
+//  https://www.w3.org/TR/html5/editing.html#specially-focusable
+bool HTMLElement::HasTabindexFocusFlag() const {
+  int32 tabindex;
+  return base::StringToInt32(GetAttribute("tabindex").value_or(""), &tabindex);
+}
+
+// An element is being rendered if it has any associated CSS layout boxes, SVG
+// layout boxes, or some equivalent in other styling languages.
+//   https://www.w3.org/TR/html5/rendering.html#being-rendered
+bool HTMLElement::IsBeingRendered() {
+  Document* document = node_document();
+  if (!document) {
+    return false;
+  }
+
+  document->UpdateComputedStyleOnElementAndAncestor(this);
+
+  return computed_style()->display() != cssom::KeywordValue::GetNone() &&
+         computed_style()->visibility() == cssom::KeywordValue::GetVisible();
+}
+
+// Algorithm for RunFocusingSteps:
+//   https://www.w3.org/TR/html5/editing.html#focusing-steps
+void HTMLElement::RunFocusingSteps() {
+  // 1. If the element is not in a Document, or if the element's Document has
+  // no browsing context, or if the element's Document's browsing context has no
+  // top-level browsing context, or if the element is not focusable, or if the
+  // element is already focused, then abort these steps.
+  Document* document = node_document();
+  if (!document || !document->HasBrowsingContext() || !IsFocusable()) {
+    return;
+  }
+  Element* old_active_element = document->active_element();
+  if (old_active_element == this) {
+    return;
+  }
+
+  // 2. If focusing the element will remove the focus from another element,
+  // then run the unfocusing steps for that element.
+  if (old_active_element && old_active_element->AsHTMLElement()) {
+    old_active_element->AsHTMLElement()->RunUnFocusingSteps();
+  }
+
+  // focusin: A user agent MUST dispatch this event when an event target is
+  // about to receive focus. This event type MUST be dispatched before the
+  // element is given focus. The event target MUST be the element which is about
+  // to receive focus. This event type is similar to focus, but is dispatched
+  // before focus is shifted, and does bubble.
+  //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-focusin
+  DispatchEvent(new FocusEvent(base::Tokens::focusin(), Event::kBubbles,
+                               Event::kNotCancelable, this));
+
+  // 3. Make the element the currently focused element in its top-level browsing
+  // context.
+  document->SetActiveElement(this);
+
+  // 4. Not needed by Cobalt.
+
+  // 5. Fire a simple event named focus at the element.
+  // focus: A user agent MUST dispatch this event when an event target receives
+  // focus. The focus MUST be given to the element before the dispatch of this
+  // event type. This event type is similar to focusin, but is dispatched after
+  // focus is shifted, and does not bubble.
+  //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-focus
+  DispatchEvent(new FocusEvent(base::Tokens::focus(), Event::kNotBubbles,
+                               Event::kNotCancelable, this));
+
+  // Custom, not in any sepc.
+  InvalidateMatchingRulesRecursively();
+}
+
+// Algorithm for RunUnFocusingSteps:
+//   https://www.w3.org/TR/html5/editing.html#unfocusing-steps
+void HTMLElement::RunUnFocusingSteps() {
+  // 1. Not needed by Cobalt.
+
+  // focusout: A user agent MUST dispatch this event when an event target is
+  // about to lose focus. This event type MUST be dispatched before the element
+  // loses focus. The event target MUST be the element which is about to lose
+  // focus. This event type is similar to blur, but is dispatched before focus
+  // is shifted, and does bubble.
+  //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-focusout
+  DispatchEvent(new FocusEvent(base::Tokens::focusout(), Event::kBubbles,
+                               Event::kNotCancelable, this));
+
+  // 2. Unfocus the element.
+  Document* document = node_document();
+  if (document && document->active_element() == this->AsElement()) {
+    document->SetActiveElement(NULL);
+  }
+
+  // 3. Fire a simple event named blur at the element.
+  // blur: A user agent MUST dispatch this event when an event target loses
+  // focus. The focus MUST be taken from the element before the dispatch of this
+  // event type. This event type is similar to focusout, but is dispatched after
+  // focus is shifted, and does not bubble.
+  //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-blur
+  DispatchEvent(new FocusEvent(base::Tokens::blur(), Event::kNotBubbles,
+                               Event::kNotCancelable, this));
+
+  // Custom, not in any sepc.
+  InvalidateMatchingRulesRecursively();
+}
+
 void HTMLElement::SetDirectionality(const std::string& value) {
   // NOTE: Value "auto" is not supported.
   Directionality previous_directionality = directionality_;
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 88bbccd..42c0146 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -219,6 +219,15 @@
           root_computed_style,
       const base::TimeDelta& style_change_event_time, bool ancestors_were_valid,
       int current_element_depth);
+
+  // Updates the cached computed style of this element and its anecstors.
+  void UpdateComputedStyleAlongAncestors(
+      const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
+          parent_computed_style,
+      const scoped_refptr<const cssom::CSSComputedStyleData>&
+          root_computed_style,
+      const base::TimeDelta& style_change_event_time);
+
   // Updates the cached computed style of this element.
   void UpdateComputedStyle(
       const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
@@ -238,9 +247,6 @@
 
   LayoutBoxes* layout_boxes() const { return layout_boxes_.get(); }
 
-  // Determines whether this element is focusable.
-  bool IsFocusable() const { return HasAttribute("tabindex"); }
-
   PseudoElement* pseudo_element(PseudoElementType type) const {
     DCHECK(type < kMaxPseudoElementType);
     return pseudo_elements_[type].get();
@@ -253,6 +259,10 @@
     pseudo_elements_[type] = pseudo_element.Pass();
   }
 
+  bool computed_style_valid() const { return computed_style_valid_; }
+
+  bool matching_rules_valid() const { return matching_rules_valid_; }
+
   DEFINE_WRAPPABLE_TYPE(HTMLElement);
 
  protected:
@@ -269,12 +279,20 @@
  private:
   // From Node.
   void OnMutation() OVERRIDE;
+  void OnRemovedFromDocument() OVERRIDE;
 
   // From Element.
   void OnSetAttribute(const std::string& name,
                       const std::string& value) OVERRIDE;
   void OnRemoveAttribute(const std::string& name) OVERRIDE;
 
+  bool IsFocusable();
+  bool HasTabindexFocusFlag() const;
+  bool IsBeingRendered();
+
+  void RunFocusingSteps();
+  void RunUnFocusingSteps();
+
   // This both updates the directionality based upon the string value and
   // invalidates layout box caching if the value has changed.
   // NOTE1: Value "auto" is not supported.
@@ -298,6 +316,8 @@
   // https://www.w3.org/TR/html5/semantics.html#the-root-element.
   bool IsRootElement();
 
+  bool locked_for_focus_;
+
   // The directionality of the html element is determined by the 'dir'
   // attribute.
   // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
@@ -315,6 +335,9 @@
   // Keeps track of whether the HTML element's current computed style is out
   // of date or not.
   bool computed_style_valid_;
+  // Keeps track of whether the HTML element's descendants' computed styles are
+  // out of date or not.
+  bool descendant_computed_styles_valid_;
 
   scoped_refptr<cssom::CSSComputedStyleDeclaration>
       css_computed_style_declaration_;
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index 685ccab..239d247 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -30,6 +30,11 @@
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/dom/layout_boxes.h"
 #include "cobalt/dom/named_node_map.h"
+#include "cobalt/dom/testing/stub_window.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/media_session/media_session.h"
+#include "cobalt/network_bridge/net_poster.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::Return;
@@ -59,10 +64,10 @@
 const char kFooBarDeclarationString[] = "foo: bar;";
 const char kDisplayInlineDeclarationString[] = "display: inline;";
 
-class MockLayoutBoxes : public dom::LayoutBoxes {
+class MockLayoutBoxes : public LayoutBoxes {
  public:
   MOCK_CONST_METHOD0(type, Type());
-  MOCK_CONST_METHOD0(GetClientRects, scoped_refptr<dom::DOMRectList>());
+  MOCK_CONST_METHOD0(GetClientRects, scoped_refptr<DOMRectList>());
 
   MOCK_CONST_METHOD0(IsInlineLevel, bool());
 
@@ -122,7 +127,6 @@
   scoped_ptr<DomStatTracker> dom_stat_tracker_;
   HTMLElementContext html_element_context_;
   scoped_refptr<Document> document_;
-  MessageLoop message_loop_;
 };
 
 scoped_refptr<HTMLElement>
@@ -142,8 +146,7 @@
     if (parent_html_element) {
       // Set layout boxes for all elements that have a child.
       scoped_ptr<MockLayoutBoxes> layout_boxes(new MockLayoutBoxes);
-      parent_html_element->set_layout_boxes(
-          layout_boxes.PassAs<dom::LayoutBoxes>());
+      parent_html_element->set_layout_boxes(layout_boxes.PassAs<LayoutBoxes>());
 
       parent_html_element->AppendChild(child_html_element);
     }
@@ -162,7 +165,7 @@
     if (child_element) {
       scoped_ptr<MockLayoutBoxes> layout_boxes(new MockLayoutBoxes);
       parent_element->AsHTMLElement()->set_layout_boxes(
-          layout_boxes.PassAs<dom::LayoutBoxes>());
+          layout_boxes.PassAs<LayoutBoxes>());
     }
     parent_element = child_element;
   }
@@ -199,26 +202,72 @@
   EXPECT_EQ(-1, html_element->tab_index());
 }
 
-TEST_F(HTMLElementTest, FocusBlur) {
+TEST_F(HTMLElementTest, Focus) {
+  // Give the document browsing context which is needed for focus to work.
+  testing::StubWindow window;
+  document_->set_window(window.window());
+  // Give the document initial computed style.
+  document_->SetViewport(math::Size(320, 240));
+
+  scoped_refptr<HTMLElement> html_element_1 =
+      document_->CreateElement("div")->AsHTMLElement();
+  scoped_refptr<HTMLElement> html_element_2 =
+      document_->CreateElement("div")->AsHTMLElement();
+  document_->AppendChild(html_element_1);
+  document_->AppendChild(html_element_2);
+  EXPECT_FALSE(document_->active_element());
+
+  html_element_1->set_tab_index(-1);
+  html_element_1->Focus();
+  ASSERT_TRUE(document_->active_element());
+  EXPECT_EQ(html_element_1, document_->active_element()->AsHTMLElement());
+
+  html_element_2->set_tab_index(-1);
+  html_element_2->Focus();
+  ASSERT_TRUE(document_->active_element());
+  EXPECT_EQ(html_element_2, document_->active_element()->AsHTMLElement());
+}
+
+TEST_F(HTMLElementTest, Blur) {
+  // Give the document browsing context which is needed for focus to work.
+  testing::StubWindow window;
+  document_->set_window(window.window());
+  // Give the document initial computed style.
+  document_->SetViewport(math::Size(320, 240));
+
   scoped_refptr<HTMLElement> html_element =
       document_->CreateElement("div")->AsHTMLElement();
+  document_->AppendChild(html_element);
   EXPECT_FALSE(document_->active_element());
 
   html_element->set_tab_index(-1);
   html_element->Focus();
+  ASSERT_TRUE(document_->active_element());
   EXPECT_EQ(html_element, document_->active_element()->AsHTMLElement());
 
   html_element->Blur();
   EXPECT_FALSE(document_->active_element());
 }
 
-TEST_F(HTMLElementTest, IsFocusable) {
+TEST_F(HTMLElementTest, RemoveActiveElementShouldRunBlur) {
+  // Give the document browsing context which is needed for focus to work.
+  testing::StubWindow window;
+  document_->set_window(window.window());
+  // Give the document initial computed style.
+  document_->SetViewport(math::Size(320, 240));
+
   scoped_refptr<HTMLElement> html_element =
       document_->CreateElement("div")->AsHTMLElement();
-  EXPECT_FALSE(html_element->IsFocusable());
+  document_->AppendChild(html_element);
+  EXPECT_FALSE(document_->active_element());
 
   html_element->set_tab_index(-1);
-  EXPECT_TRUE(html_element->IsFocusable());
+  html_element->Focus();
+  ASSERT_TRUE(document_->active_element());
+  EXPECT_EQ(html_element, document_->active_element()->AsHTMLElement());
+
+  document_->RemoveChild(html_element);
+  EXPECT_FALSE(document_->active_element());
 }
 
 TEST_F(HTMLElementTest, LayoutBoxesGetter) {
@@ -227,16 +276,16 @@
 
   scoped_ptr<MockLayoutBoxes> mock_layout_boxes(new MockLayoutBoxes);
   MockLayoutBoxes* saved_mock_layout_boxes_ptr = mock_layout_boxes.get();
-  html_element->set_layout_boxes(mock_layout_boxes.PassAs<dom::LayoutBoxes>());
+  html_element->set_layout_boxes(mock_layout_boxes.PassAs<LayoutBoxes>());
   DCHECK(mock_layout_boxes.get() == NULL);
 
   EXPECT_CALL(*base::polymorphic_downcast<MockLayoutBoxes*>(
                   html_element->layout_boxes()),
               type())
-      .WillOnce(Return(dom::LayoutBoxes::kLayoutLayoutBoxes));
-  dom::LayoutBoxes* layout_boxes = html_element->layout_boxes();
+      .WillOnce(Return(LayoutBoxes::kLayoutLayoutBoxes));
+  LayoutBoxes* layout_boxes = html_element->layout_boxes();
   EXPECT_EQ(layout_boxes, saved_mock_layout_boxes_ptr);
-  EXPECT_EQ(layout_boxes->type(), dom::LayoutBoxes::kLayoutLayoutBoxes);
+  EXPECT_EQ(layout_boxes->type(), LayoutBoxes::kLayoutLayoutBoxes);
 }
 
 TEST_F(HTMLElementTest, GetBoundingClientRectWithoutLayoutBox) {
@@ -264,7 +313,7 @@
   EXPECT_FLOAT_EQ(html_element->client_top(), 0.0f);
 
   scoped_ptr<MockLayoutBoxes> layout_boxes(new MockLayoutBoxes);
-  html_element->set_layout_boxes(layout_boxes.PassAs<dom::LayoutBoxes>());
+  html_element->set_layout_boxes(layout_boxes.PassAs<LayoutBoxes>());
 
   // 1. If the CSS layout box is inline, return zero.
   EXPECT_CALL(*base::polymorphic_downcast<MockLayoutBoxes*>(
@@ -298,7 +347,7 @@
   EXPECT_FLOAT_EQ(html_element->client_left(), 0.0f);
 
   scoped_ptr<MockLayoutBoxes> layout_boxes(new MockLayoutBoxes);
-  html_element->set_layout_boxes(layout_boxes.PassAs<dom::LayoutBoxes>());
+  html_element->set_layout_boxes(layout_boxes.PassAs<LayoutBoxes>());
 
   // 1. If the CSS layout box is inline, return zero.
   EXPECT_CALL(*base::polymorphic_downcast<MockLayoutBoxes*>(
@@ -582,7 +631,7 @@
   EXPECT_FLOAT_EQ(html_element->offset_width(), 0.0f);
 
   scoped_ptr<MockLayoutBoxes> layout_boxes(new MockLayoutBoxes);
-  html_element->set_layout_boxes(layout_boxes.PassAs<dom::LayoutBoxes>());
+  html_element->set_layout_boxes(layout_boxes.PassAs<LayoutBoxes>());
 
   // 2. Return the border edge width of the first CSS layout box associated with
   // the element, ignoring any transforms that apply to the element and its
@@ -605,7 +654,7 @@
   EXPECT_FLOAT_EQ(html_element->offset_height(), 0.0f);
 
   scoped_ptr<MockLayoutBoxes> layout_boxes(new MockLayoutBoxes);
-  html_element->set_layout_boxes(layout_boxes.PassAs<dom::LayoutBoxes>());
+  html_element->set_layout_boxes(layout_boxes.PassAs<LayoutBoxes>());
 
   // 2. Return the border edge height of the first CSS layout box associated
   // with the element, ignoring any transforms that apply to the element and its
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 869d4c2..51edf3e 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -776,8 +776,7 @@
     ScheduleOwnEvent(base::Tokens::abort());
   }
 
-  ClearMediaSource();
-
+  ClearMediaPlayer();
   CreateMediaPlayer();
 
   // 4 - If the media element's networkState is not set to kNetworkEmpty, then
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index b567262..ebbb42d 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -14,12 +14,14 @@
 
 #include "cobalt/dom/navigator.h"
 
+#include "base/optional.h"
 #include "cobalt/dom/dom_exception.h"
 #if defined(COBALT_MEDIA_SOURCE_2016)
 #include "cobalt/dom/eme/media_key_system_access.h"
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 #include "cobalt/media_session/media_session_client.h"
 #include "cobalt/script/script_value_factory.h"
+#include "starboard/media.h"
 
 using cobalt::media_session::MediaSession;
 
@@ -61,15 +63,137 @@
 
 namespace {
 
-// TODO: Implement "3.1.1.1 Get Supported Configuration" using
-//       SbMediaCanPlayMimeAndKeySystem().
-//       https://www.w3.org/TR/encrypted-media/#get-supported-configuration
-bool MaybeGetSupportedConfiguration(
-    const std::string& /*key_system*/,
-    const eme::MediaKeySystemConfiguration& candidate_configuration,
-    eme::MediaKeySystemConfiguration* supported_configuration) {
-  *supported_configuration = candidate_configuration;
-  return true;
+// See
+// https://www.w3.org/TR/encrypted-media/#get-supported-capabilities-for-audio-video-type.
+base::optional<script::Sequence<MediaKeySystemMediaCapability> >
+TryGetSupportedCapabilities(
+    const std::string& key_system,
+    const script::Sequence<MediaKeySystemMediaCapability>&
+        requested_media_capabilities) {
+  // 2. Let supported media capabilities be an empty sequence of
+  //    MediaKeySystemMediaCapability dictionaries.
+  script::Sequence<MediaKeySystemMediaCapability> supported_media_capabilities;
+  // 3. For each requested media capability in requested media capabilities:
+  for (std::size_t media_capability_index = 0;
+       media_capability_index < requested_media_capabilities.size();
+       ++media_capability_index) {
+    const MediaKeySystemMediaCapability& requested_media_capability =
+        requested_media_capabilities.at(media_capability_index);
+    // 3.1. Let content type be requested media capability's contentType member.
+    const std::string& content_type = requested_media_capability.content_type();
+    // 3.3. If content type is the empty string, return null.
+    if (content_type.empty()) {
+      return base::nullopt;
+    }
+    // 3.13. If the user agent and [CDM] implementation definitely support
+    //       playback of encrypted media data for the combination of container,
+    //       media types [...]:
+    if (SbMediaCanPlayMimeAndKeySystem(content_type.c_str(),
+                                       key_system.c_str()) ==
+        kSbMediaSupportTypeProbably) {
+      // 3.13.1. Add requested media capability to supported media capabilities.
+      supported_media_capabilities.push_back(requested_media_capability);
+    }
+  }
+  // 4. If supported media capabilities is empty, return null.
+  if (supported_media_capabilities.empty()) {
+    return base::nullopt;
+  }
+  // 5. Return supported media capabilities.
+  return supported_media_capabilities;
+}
+
+// Technically, a user agent is supposed to implement "3.1.1.1 Get Supported
+// Configuration" which requests the user consent until it's given. But since
+// Cobalt never interacts with the user directly, we'll assume that the consent
+// is always given and go straight to "3.1.1.2 Get Supported Configuration and
+// Consent". See
+// https://www.w3.org/TR/encrypted-media/#get-supported-configuration-and-consent.
+base::optional<eme::MediaKeySystemConfiguration> TryGetSupportedConfiguration(
+    const std::string& key_system,
+    const eme::MediaKeySystemConfiguration& candidate_configuration) {
+  // 1. Let accumulated configuration be a new MediaKeySystemConfiguration
+  //    dictionary.
+  eme::MediaKeySystemConfiguration accumulated_configuration;
+
+  // 2. Set the label member of accumulated configuration to equal the label
+  //    member of candidate configuration.
+  accumulated_configuration.set_label(candidate_configuration.label());
+
+  // For now, copy initialization data types into accumulated configuration.
+  //
+  // TODO: Implement step 3 after introducing a Starboard API for detecting
+  //       supported initialization data type.
+  // TODO: Checking has_init_data_types() won't be needed once Cobalt supports
+  //       default values for IDL sequences.
+  accumulated_configuration.set_init_data_types(
+      candidate_configuration.has_init_data_types()
+          ? candidate_configuration.init_data_types()
+          : script::Sequence<std::string>());
+
+  // TODO: Reject distinctive identifiers, persistent state, and persistent
+  //       sessions.
+
+  // 15. If the videoCapabilities and audioCapabilities members in candidate
+  //     configuration are both empty, return NotSupported.
+  //
+  // TODO: Checking has_video_capabilities() and has_audio_capabilities() won't
+  //       be needed once Cobalt supports default values for IDL sequences.
+  if ((!candidate_configuration.has_video_capabilities() ||
+       candidate_configuration.video_capabilities().empty()) &&
+      (!candidate_configuration.has_audio_capabilities() ||
+       candidate_configuration.audio_capabilities().empty())) {
+    return base::nullopt;
+  }
+
+  // 16. If the videoCapabilities member in candidate configuration is
+  //     non-empty:
+  if (candidate_configuration.has_video_capabilities() &&
+      !candidate_configuration.video_capabilities().empty()) {
+    // 16.1. Let video capabilities be the result of executing the "Get
+    //       Supported Capabilities for Audio/Video Type" algorithm.
+    base::optional<script::Sequence<MediaKeySystemMediaCapability> >
+        maybe_video_capabilities = TryGetSupportedCapabilities(
+            key_system, candidate_configuration.video_capabilities());
+    // 16.2. If video capabilities is null, return NotSupported.
+    if (!maybe_video_capabilities) {
+      return base::nullopt;
+    }
+    // 16.3. Set the videoCapabilities member of accumulated configuration to
+    //       video capabilities.
+    accumulated_configuration.set_video_capabilities(*maybe_video_capabilities);
+  } else {
+    // Otherwise: set the videoCapabilities member of accumulated configuration
+    // to an empty sequence.
+    accumulated_configuration.set_video_capabilities(
+        script::Sequence<MediaKeySystemMediaCapability>());
+  }
+
+  // 17. If the audioCapabilities member in candidate configuration is
+  //     non-empty:
+  if (candidate_configuration.has_audio_capabilities() &&
+      !candidate_configuration.audio_capabilities().empty()) {
+    // 17.1. Let audio capabilities be the result of executing the "Get
+    //       Supported Capabilities for Audio/Video Type" algorithm.
+    base::optional<script::Sequence<MediaKeySystemMediaCapability> >
+        maybe_audio_capabilities = TryGetSupportedCapabilities(
+            key_system, candidate_configuration.audio_capabilities());
+    // 17.2. If audio capabilities is null, return NotSupported.
+    if (!maybe_audio_capabilities) {
+      return base::nullopt;
+    }
+    // 17.3. Set the audioCapabilities member of accumulated configuration to
+    //       audio capabilities.
+    accumulated_configuration.set_audio_capabilities(*maybe_audio_capabilities);
+  } else {
+    // Otherwise: set the audioCapabilities member of accumulated configuration
+    // to an empty sequence.
+    accumulated_configuration.set_audio_capabilities(
+        script::Sequence<MediaKeySystemMediaCapability>());
+  }
+
+  // 23. Return accumulated configuration.
+  return accumulated_configuration;
 }
 
 }  // namespace
@@ -96,17 +220,18 @@
   }
 
   // 6.3. For each value in |supportedConfigurations|:
-  for (size_t configuration_index = 0;
+  for (std::size_t configuration_index = 0;
        configuration_index < supported_configurations.size();
        ++configuration_index) {
     // 6.3.3. If supported configuration is not NotSupported:
-    eme::MediaKeySystemConfiguration supported_configuration;
-    if (MaybeGetSupportedConfiguration(
-            key_system, supported_configurations.at(configuration_index),
-            &supported_configuration)) {
+    base::optional<eme::MediaKeySystemConfiguration>
+        maybe_supported_configuration = TryGetSupportedConfiguration(
+            key_system, supported_configurations.at(configuration_index));
+    if (maybe_supported_configuration) {
       // 6.3.3.1. Let access be a new MediaKeySystemAccess object.
       scoped_refptr<eme::MediaKeySystemAccess> media_key_system_access(
-          new eme::MediaKeySystemAccess(key_system, supported_configuration,
+          new eme::MediaKeySystemAccess(key_system,
+                                        *maybe_supported_configuration,
                                         script_value_factory_));
       // 6.3.3.2. Resolve promise.
       promise_reference.value().Resolve(media_key_system_access);
diff --git a/src/cobalt/dom/node.idl b/src/cobalt/dom/node.idl
index 7649487..e1d2ec1 100644
--- a/src/cobalt/dom/node.idl
+++ b/src/cobalt/dom/node.idl
@@ -14,10 +14,6 @@
 
 // https://www.w3.org/TR/dom/#node
 
-[
-  GetOpaqueRoot=GetRootNode,
-  AddOpaqueRoots=(GetRootNode),
-]
 interface Node : EventTarget {
   const unsigned short ELEMENT_NODE = 1;
   const unsigned short TEXT_NODE = 3;
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index e7ef524..5323f3d 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -30,6 +30,7 @@
 #include "cobalt/dom/node.h"
 #include "cobalt/dom/node_descendants_iterator.h"
 #include "cobalt/dom/node_list.h"
+#include "cobalt/dom/testing/stub_window.h"
 #include "cobalt/dom_parser/parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -229,9 +230,15 @@
 
 // div:focus should match focused div.
 TEST_F(RuleMatchingTest, FocusPseudoClassMatch) {
+  // Give the document browsing context which is needed for focus to work.
+  testing::StubWindow window;
+  document_->set_window(window.window());
+  // Give the document initial computed style.
+  document_->SetViewport(math::Size(320, 240));
+
   css_style_sheet_ = css_parser_->ParseStyleSheet(
       ":focus {}", base::SourceLocation("[object RuleMatchingTest]", 1, 1));
-  root_->set_inner_html("<div tabIndex=-1/>");
+  root_->set_inner_html("<div tabIndex=\"-1\"/>");
   root_->first_element_child()->AsHTMLElement()->Focus();
   UpdateAllMatchingRules();
 
@@ -243,9 +250,15 @@
 
 // div:focus shouldn't match unfocused div.
 TEST_F(RuleMatchingTest, FocusPseudoClassNoMatch) {
+  // Give the document browsing context which is needed for focus to work.
+  testing::StubWindow window;
+  document_->set_window(window.window());
+  // Give the document initial computed style.
+  document_->SetViewport(math::Size(320, 240));
+
   css_style_sheet_ = css_parser_->ParseStyleSheet(
       ":focus {}", base::SourceLocation("[object RuleMatchingTest]", 1, 1));
-  root_->set_inner_html("<div tabIndex=-1/>");
+  root_->set_inner_html("<div tabIndex=\"-1\"/>");
   UpdateAllMatchingRules();
 
   cssom::RulesWithCascadePrecedence* matching_rules =
diff --git a/src/cobalt/webdriver/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
similarity index 88%
rename from src/cobalt/webdriver/testing/stub_window.h
rename to src/cobalt/dom/testing/stub_window.h
index e2751dc..ffe7834 100644
--- a/src/cobalt/webdriver/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_WEBDRIVER_TESTING_STUB_WINDOW_H_
-#define COBALT_WEBDRIVER_TESTING_STUB_WINDOW_H_
+#ifndef COBALT_DOM_TESTING_STUB_WINDOW_H_
+#define COBALT_DOM_TESTING_STUB_WINDOW_H_
 
 #include <string>
 
@@ -28,14 +28,16 @@
 #include "cobalt/media/media_module_stub.h"
 #include "cobalt/media_session/media_session.h"
 #include "cobalt/network/network_module.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
 #include "googleurl/src/gurl.h"
 
 namespace cobalt {
-namespace webdriver {
+namespace dom {
 namespace testing {
 
-// A helper class for WebDriver tests that brings up a dom::Window with a number
-// of parts stubbed out.
+// A helper class for tests that brings up a dom::Window with a number of parts
+// stubbed out.
 class StubWindow {
  public:
   StubWindow()
@@ -70,6 +72,7 @@
   scoped_refptr<script::GlobalEnvironment> global_environment() {
     return global_environment_;
   }
+  css_parser::Parser* css_parser() { return css_parser_.get(); }
 
  private:
   static void StubErrorCallback(const std::string& /*error*/) {}
@@ -90,7 +93,7 @@
 };
 
 }  // namespace testing
-}  // namespace webdriver
+}  // namespace dom
 }  // namespace cobalt
 
-#endif  // COBALT_WEBDRIVER_TESTING_STUB_WINDOW_H_
+#endif  // COBALT_DOM_TESTING_STUB_WINDOW_H_
diff --git a/src/cobalt/dom/ui_event.cc b/src/cobalt/dom/ui_event.cc
index a9ebec2..2b4d274 100644
--- a/src/cobalt/dom/ui_event.cc
+++ b/src/cobalt/dom/ui_event.cc
@@ -25,8 +25,6 @@
 UIEvent::UIEvent(UninitializedFlag uninitialized_flag)
     : Event(uninitialized_flag) {}
 
-UIEvent::UIEvent(base::Token type) : Event(type) {}
-
 UIEvent::UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable)
     : Event(type, bubbles, cancelable) {}
 
@@ -38,5 +36,7 @@
   view_ = view;
 }
 
+UIEvent::UIEvent(base::Token type) : Event(type) {}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/ui_event.h b/src/cobalt/dom/ui_event.h
index a1e669f..6659231 100644
--- a/src/cobalt/dom/ui_event.h
+++ b/src/cobalt/dom/ui_event.h
@@ -36,6 +36,8 @@
   // Creates an event with its "initialized flag" unset.
   explicit UIEvent(UninitializedFlag uninitialized_flag);
 
+  UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable);
+
   // Web API: UIEvent
   //
   void InitUIEvent(const std::string& type, bool bubbles, bool cancelable,
@@ -52,7 +54,6 @@
 
  protected:
   explicit UIEvent(base::Token type);
-  UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable);
 
   ~UIEvent() OVERRIDE {}
 
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
index 8596241..7a400c8 100644
--- a/src/cobalt/media/base/drm_system.h
+++ b/src/cobalt/media/base/drm_system.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop.h"
@@ -35,7 +36,7 @@
 //
 // Ensures that callbacks are always asynchronous and performed
 // from the same thread where |DrmSystem| was instantiated.
-class DrmSystem {
+class DrmSystem : public base::RefCounted<DrmSystem> {
  public:
   typedef base::Callback<void(scoped_array<uint8> message, int message_size)>
       SessionUpdateRequestGeneratedCallback;
@@ -44,7 +45,8 @@
   typedef base::Callback<void()> SessionDidNotUpdateCallback;
 
   // Flyweight that provides RAII semantics for sessions.
-  // Most of logic is implemented by |DrmSystem|.
+  // Most of logic is implemented by |DrmSystem| and thus sessions must be
+  // destroyed before |DrmSystem|.
   class Session {
    public:
     ~Session();
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 40763c6..b773589 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -330,6 +330,7 @@
 
   if (!player_) {
     seek_cb.Run(PIPELINE_ERROR_INVALID_STATE);
+    return;
   }
 
   player_->PrepareForSeek();
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 01d4a3c..e4ce5be 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -124,7 +124,9 @@
       ->ResetGetCurrentSbDecodeTargetFunction();
 #endif  // SB_API_VERSION >= 4
 
-  SbPlayerDestroy(player_);
+  if (SbPlayerIsValid(player_)) {
+    SbPlayerDestroy(player_);
+  }
 }
 
 void StarboardPlayer::UpdateVideoResolution(int frame_width, int frame_height) {
@@ -203,8 +205,13 @@
 }
 
 void StarboardPlayer::SetBounds(const gfx::Rect& rect) {
-  DCHECK(SbPlayerIsValid(player_));
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+    pending_set_bounds_rect_ = rect;
+    return;
+  }
 
+  DCHECK(SbPlayerIsValid(player_));
 #if SB_API_VERSION >= 4
   const int kZIndex = 0;
   SbPlayerSetBounds(player_, kZIndex, rect.x(), rect.y(), rect.width(),
@@ -216,13 +223,20 @@
 
 void StarboardPlayer::PrepareForSeek() {
   DCHECK(message_loop_->BelongsToCurrentThread());
+
+  seek_pending_ = true;
+
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+    return;
+  }
+
   ++ticket_;
 #if SB_API_VERSION < 4
   SbPlayerSetPause(player_, true);
 #else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, 0.f);
 #endif  // SB_API_VERSION < 4
-  seek_pending_ = true;
 }
 
 void StarboardPlayer::Seek(base::TimeDelta time) {
@@ -270,9 +284,14 @@
 
 void StarboardPlayer::SetPlaybackRate(double playback_rate) {
   DCHECK(message_loop_->BelongsToCurrentThread());
-  DCHECK(SbPlayerIsValid(player_));
 
   playback_rate_ = playback_rate;
+
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+    return;
+  }
+
 #if SB_API_VERSION < 4
   SbPlayerSetPause(player_, playback_rate == 0.0);
 #else   // SB_API_VERSION < 4
@@ -451,6 +470,11 @@
 #endif  // SB_API_VERSION >= 4
 
   set_bounds_helper_->SetPlayer(this);
+
+  if (pending_set_bounds_rect_) {
+    SetBounds(*pending_set_bounds_rect_);
+    pending_set_bounds_rect_ = base::nullopt_t();
+  }
 }
 
 #if SB_API_VERSION >= 4
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index 5acfecd..d2c02f1 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -157,6 +157,10 @@
   bool paused_;
   bool seek_pending_;
   DecoderBufferCache decoder_buffer_cache_;
+  // If |SetBounds| is called while we are in a suspended state, then the
+  // |Rect| that we are passed will be saved to here, and then immediately set
+  // on the new player that we construct when we are resumed.
+  base::optional<gfx::Rect> pending_set_bounds_rect_;
 
   // The following variables can be accessed from GetInfo(), which can be called
   // from any threads.  So some of their usages have to be guarded by |lock_|.
diff --git a/src/cobalt/media/decoder_buffer_allocator.cc b/src/cobalt/media/decoder_buffer_allocator.cc
index f60a1c7..70858e5 100644
--- a/src/cobalt/media/decoder_buffer_allocator.cc
+++ b/src/cobalt/media/decoder_buffer_allocator.cc
@@ -22,32 +22,52 @@
 namespace media {
 
 namespace {
-bool kPreAllocateAllMemory = true;
+const bool kPreAllocateAllMemory = true;
 }  // namespace
 
-DecoderBufferAllocator::DecoderBufferAllocator()
-    : memory_block_(
-          SbMemoryAllocateAligned(DecoderBuffer::kAlignmentSize,
-                                  COBALT_MEDIA_BUFFER_INITIAL_CAPACITY)) {
-  memory_pool_.set(starboard::make_scoped_ptr(
-      new nb::MemoryPool(memory_block_, COBALT_MEDIA_BUFFER_INITIAL_CAPACITY,
-                         kPreAllocateAllMemory)));
+DecoderBufferAllocator::DecoderBufferAllocator() : memory_block_(NULL) {
+  if (COBALT_MEDIA_BUFFER_INITIAL_CAPACITY > 0) {
+    memory_block_ = SbMemoryAllocateAligned(
+        DecoderBuffer::kAlignmentSize, COBALT_MEDIA_BUFFER_INITIAL_CAPACITY);
+  }
+
+  if (COBALT_MEDIA_BUFFER_INITIAL_CAPACITY > 0 ||
+      COBALT_MEDIA_BUFFER_ALLOCATION_UNIT > 0) {
+    // TODO: Support COBALT_MEDIA_BUFFER_ALLOCATION_UNIT > 0.
+    memory_pool_.set(starboard::make_scoped_ptr(
+        new nb::MemoryPool(memory_block_, COBALT_MEDIA_BUFFER_INITIAL_CAPACITY,
+                           kPreAllocateAllMemory)));
+  }
 }
 
 DecoderBufferAllocator::~DecoderBufferAllocator() {
-  DCHECK_EQ(memory_pool_->GetAllocated(), 0);
-  SbMemoryDeallocateAligned(memory_block_);
+  if (memory_pool_.is_valid()) {
+    DCHECK_EQ(memory_pool_->GetAllocated(), 0);
+  }
+
+  if (memory_block_) {
+    SbMemoryDeallocateAligned(memory_block_);
+  }
 }
 
 void* DecoderBufferAllocator::Allocate(Type type, size_t size,
                                        size_t alignment) {
   UNREFERENCED_PARAMETER(type);
-  return memory_pool_->Allocate(size, alignment);
+  if (memory_pool_.is_valid()) {
+    return memory_pool_->Allocate(size, alignment);
+  }
+
+  return SbMemoryAllocateAligned(alignment, size);
 }
 
 void DecoderBufferAllocator::Free(Type type, void* ptr) {
   UNREFERENCED_PARAMETER(type);
-  memory_pool_->Free(ptr);
+  if (memory_pool_.is_valid()) {
+    memory_pool_->Free(ptr);
+    return;
+  }
+
+  SbMemoryDeallocateAligned(ptr);
 }
 
 }  // namespace media
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index c8477bd..9741314 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -32,6 +32,9 @@
 
 namespace {
 
+// Used to ensure that there is no more than one instance of WebMediaPlayerImpl.
+WebMediaPlayerImpl* s_instance;
+
 // Limits the range of playback rate.
 //
 // TODO(kylep): Revisit these.
@@ -124,6 +127,9 @@
       drm_system_(NULL) {
   TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::WebMediaPlayerImpl");
 
+  DCHECK(!s_instance);
+  s_instance = this;
+
   DCHECK(buffer_allocator_);
   media_log_->AddEvent(
       media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
@@ -145,6 +151,9 @@
 
   DCHECK(!main_loop_ || main_loop_ == MessageLoop::current());
 
+  DCHECK_EQ(s_instance, this);
+  s_instance = NULL;
+
   if (delegate_) {
     delegate_->UnregisterPlayer(this);
   }
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp
index 6959bcd..2a38204 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45.gyp
+++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -27,7 +27,6 @@
         'mozjs_property_enumerator.cc',
         'mozjs_script_value_factory.cc',
         'mozjs_source_code.cc',
-        'opaque_root_tracker.cc',
         'promise_wrapper.cc',
         'proxy_handler.cc',
         'referenced_object_map.cc',
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 2bd7c41..32cf999 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -140,8 +140,6 @@
   wrapper_factory_.reset(new WrapperFactory(context_));
   script_value_factory_.reset(new MozjsScriptValueFactory(this));
   referenced_objects_.reset(new ReferencedObjectMap(context_));
-  opaque_root_tracker_.reset(new OpaqueRootTracker(
-      context_, referenced_objects_.get(), wrapper_factory_.get()));
 
   JS_AddExtraGCRootsTracer(runtime, TraceFunction, this);
 }
@@ -400,37 +398,20 @@
 void MozjsGlobalEnvironment::BeginGarbageCollection() {
   TRACK_MEMORY_SCOPE("Javascript");
   // It's possible that a GC could be triggered from within the
-  // BeginGarbageCollection callback. Only create the OpaqueRootState the
-  // first time we enter. Also, only verify that |visisted_wrappables_| is
-  // empty in this case.
-  // TODO: Opaque root logic is a special case of tracing wrappables, and
-  // should be removed.
+  // BeginGarbageCollection callback. Only verify that |visisted_wrappables_|
+  // is empty the first time we enter.
   garbage_collection_count_++;
 
   if (garbage_collection_count_ == 1) {
-    if (global_object_proxy_) {
-      DCHECK(!opaque_root_state_);
-      JSAutoRequest auto_request(context_);
-      JSAutoCompartment auto_compartment(context_, global_object_proxy_);
-      // Get the current state of opaque root relationships. Keep this object
-      // alive for the duration of the GC phase to ensure that reachability
-      // between roots and reachable objects is maintained.
-      opaque_root_state_ = opaque_root_tracker_->GetCurrentOpaqueRootState();
-    }
-
     DCHECK_EQ(visited_wrappables_.size(), 0);
   }
 }
 
 void MozjsGlobalEnvironment::EndGarbageCollection() {
-  // Reset opaque root reachability relationships. Also reset
-  // |visisted_wrappables_|.
-  // TODO: Opaque root logic is a special case of tracing wrappables, and
-  // should be removed.
+  // Reset |visisted_wrappables_|.
   garbage_collection_count_--;
   DCHECK_GE(garbage_collection_count_, 0);
   if (garbage_collection_count_ == 0) {
-    opaque_root_state_.reset(NULL);
     visited_wrappables_.clear();
   }
 }
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index 0a5a573..bcb0a54 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -25,7 +25,6 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/mozjs-45/interface_data.h"
-#include "cobalt/script/mozjs-45/opaque_root_tracker.h"
 #include "cobalt/script/mozjs-45/util/exception_helpers.h"
 #include "cobalt/script/mozjs-45/weak_heap_object_manager.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -99,10 +98,6 @@
 
   WeakHeapObjectManager* weak_object_manager() { return &weak_object_manager_; }
 
-  OpaqueRootTracker* opaque_root_tracker() {
-    return opaque_root_tracker_.get();
-  }
-
   base::hash_set<Wrappable*>* visited_wrappables() {
     return &visited_wrappables_;
   }
@@ -172,13 +167,11 @@
   WeakHeapObjectManager weak_object_manager_;
   CachedWrapperMultiMap kept_alive_objects_;
   scoped_ptr<ReferencedObjectMap> referenced_objects_;
-  scoped_ptr<OpaqueRootTracker> opaque_root_tracker_;
   CachedInterfaceData cached_interface_data_;
   STLValueDeleter<CachedInterfaceData> cached_interface_data_deleter_;
   ContextDestructor context_destructor_;
   scoped_ptr<WrapperFactory> wrapper_factory_;
   scoped_ptr<MozjsScriptValueFactory> script_value_factory_;
-  scoped_ptr<OpaqueRootTracker::OpaqueRootState> opaque_root_state_;
   JS::Heap<JSObject*> global_object_proxy_;
   EnvironmentSettings* environment_settings_;
   // TODO: Should be |std::unordered_set| once C++11 is enabled.
diff --git a/src/cobalt/script/mozjs-45/opaque_root_tracker.cc b/src/cobalt/script/mozjs-45/opaque_root_tracker.cc
deleted file mode 100644
index fdce7cf..0000000
--- a/src/cobalt/script/mozjs-45/opaque_root_tracker.cc
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/script/mozjs-45/opaque_root_tracker.h"
-
-#include <utility>
-#include <vector>
-
-#include "cobalt/script/mozjs-45/weak_heap_object.h"
-#include "third_party/mozjs-45/js/src/jsapi.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-namespace {
-// Implementation of OpaqueRootTracker::OpaqueRootState.
-// On creation, this class will register reachability between objects and their
-// roots in |ReferencedObjectMap|. On destruction, the reachability
-// relationship will be removed.
-class OpaqueRootStateImpl : public OpaqueRootTracker::OpaqueRootState {
- public:
-  OpaqueRootStateImpl(JSContext* context,
-                      ReferencedObjectMap* referenced_object_map)
-      : context_(context), referenced_object_map_(referenced_object_map) {}
-
-  void TrackReachability(WrapperPrivate* from, WrapperPrivate* to) {
-    intptr_t from_key = ReferencedObjectMap::GetKeyForWrappable(
-        from->wrappable<Wrappable>().get());
-    JSObject* to_proxy = to->js_object_proxy();
-    DCHECK(to_proxy);
-    JS::RootedValue to_value(context_, JS::ObjectValue(*to_proxy));
-    referenced_objects_.push_back(
-        std::make_pair(from_key, WeakHeapObject(context_, to_value)));
-    referenced_object_map_->AddReferencedObject(from_key, to_value);
-  }
-
-  ~OpaqueRootStateImpl() {
-    JSAutoRequest auto_request(context_);
-    for (ReferencedObjectPairVector::iterator it = referenced_objects_.begin();
-         it != referenced_objects_.end(); ++it) {
-      WeakHeapObject &value = it->second;
-      if (value.IsGcThing() && !value.WasCollected()) {
-        JS::RootedValue reachable_value(context_, it->second.GetValue());
-        referenced_object_map_->RemoveReferencedObject(it->first,
-                                                       reachable_value);
-      }
-    }
-  }
-
- private:
-  typedef std::vector<std::pair<intptr_t, WeakHeapObject> >
-      ReferencedObjectPairVector;
-
-  JSContext* context_;
-  ReferencedObjectMap* referenced_object_map_;
-  ReferencedObjectPairVector referenced_objects_;
-};
-}  // namespace
-
-OpaqueRootTracker::OpaqueRootTracker(JSContext* context,
-                                     ReferencedObjectMap* referenced_object_map,
-                                     WrapperFactory* wrapper_factory)
-    : context_(context),
-      referenced_object_map_(referenced_object_map),
-      wrapper_factory_(wrapper_factory) {}
-
-void OpaqueRootTracker::AddObjectWithOpaqueRoot(
-    WrapperPrivate* wrapper_private) {
-  all_objects_.insert(wrapper_private);
-}
-
-void OpaqueRootTracker::RemoveObjectWithOpaqueRoot(
-    WrapperPrivate* wrapper_private) {
-  all_objects_.erase(wrapper_private);
-}
-
-scoped_ptr<OpaqueRootTracker::OpaqueRootState>
-OpaqueRootTracker::GetCurrentOpaqueRootState() {
-  scoped_ptr<OpaqueRootStateImpl> state(
-      new OpaqueRootStateImpl(context_, referenced_object_map_));
-  // Get the current opaque root for all objects that are being tracked.
-  for (WrapperPrivateSet::iterator it = all_objects_.begin();
-       it != all_objects_.end(); ++it) {
-    WrapperPrivate* wrapper_private = *it;
-    TrackReachabilityToOpaqueRoot(state.get(), wrapper_private);
-    TrackReachableWrappables(state.get(), wrapper_private);
-  }
-  return state.PassAs<OpaqueRootState>();
-}
-
-void OpaqueRootTracker::TrackReachabilityToOpaqueRoot(
-    OpaqueRootState* state, WrapperPrivate* wrapper_private) {
-  OpaqueRootStateImpl* state_impl =
-      base::polymorphic_downcast<OpaqueRootStateImpl*>(state);
-  // If this wrappable has an opaque root, track reachability between this
-  // wrappable and its root.
-  Wrappable* opaque_root = wrapper_private->GetOpaqueRoot();
-  if (opaque_root) {
-    WrapperPrivate* opaque_root_private = WrapperPrivate::GetFromWrappable(
-        opaque_root, context_, wrapper_factory_);
-    // Always mark the root as reachable from the non-root object.
-    state_impl->TrackReachability(wrapper_private, opaque_root_private);
-
-    // Only mark the non-root object as reachable if we need to keep the
-    // wrapper alive for some reason. In general it's okay for a wrapper to
-    // get GC'd because the Cobalt object will still be kept alive, and a new
-    // JS object can be created if needed again.
-    if (wrapper_private->ShouldKeepWrapperAliveIfReachable()) {
-      state_impl->TrackReachability(opaque_root_private, wrapper_private);
-    }
-  }
-}
-
-void OpaqueRootTracker::TrackReachableWrappables(
-    OpaqueRootState* state, WrapperPrivate* wrapper_private) {
-  OpaqueRootStateImpl* state_impl =
-      base::polymorphic_downcast<OpaqueRootStateImpl*>(state);
-  // Track any wrappables that are explicitly marked as reachable from
-  // this wrappable.
-  typedef std::vector<Wrappable*> WrappableVector;
-  WrappableVector reachable_objects;
-  wrapper_private->GetReachableWrappables(&reachable_objects);
-  for (size_t i = 0; i < reachable_objects.size(); ++i) {
-    WrapperPrivate* reachable_object_private = WrapperPrivate::GetFromWrappable(
-        reachable_objects[i], context_, wrapper_factory_);
-    state_impl->TrackReachability(wrapper_private, reachable_object_private);
-  }
-}
-
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/opaque_root_tracker.h b/src/cobalt/script/mozjs-45/opaque_root_tracker.h
deleted file mode 100644
index f09ed04..0000000
--- a/src/cobalt/script/mozjs-45/opaque_root_tracker.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#ifndef COBALT_SCRIPT_MOZJS_45_OPAQUE_ROOT_TRACKER_H_
-#define COBALT_SCRIPT_MOZJS_45_OPAQUE_ROOT_TRACKER_H_
-
-#include "base/hash_tables.h"
-#include "cobalt/script/mozjs-45/referenced_object_map.h"
-#include "cobalt/script/mozjs-45/wrapper_factory.h"
-#include "cobalt/script/mozjs-45/wrapper_private.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-// This class manages lifetime of structures containing objects that are
-// reachable from through the interface implementation, but not through
-// JavaScript, by ensuring that the root of the structure is not garbage
-// collected if any of the members of the structure are reachable.
-//
-// The implementation of the structure in Cobalt is typically reference counted.
-// The |root| of that structure is a single node that transitively holds a
-// reference to every other node in the structure. It may be the case that the
-// only reference to that root node is from a JS object's WrapperPrivate. The
-// OpaqueRootTracker class ensures that if some node in the structure is
-// reachable from JavaScript, the JS object holding the reference to the root
-// of the structure is marked as reachable as well, which preserves the final
-// reference to the root of the structure, keeping the entire structure alive.
-//
-// For example, any DOM Node can be reached from any other DOM Node object in
-// the same tree. If an arbitary internal Node is reachable from JavaScript,
-// this class will ensure that the root of the tree will also be kept alive,
-// preserving the entire tree until no nodes in the tree are reachable from
-// JavaScript.
-//
-// An object's opaque root can change throughout the object's lifetime, so the
-// root needs to be recalculated every garbage collection phase.
-class OpaqueRootTracker {
- public:
-  // Callers do not need to operate on this class. They just need to manage its
-  // lifetime appropriately as described below.
-  class OpaqueRootState {
-   protected:
-    OpaqueRootState() {}
-    virtual ~OpaqueRootState() {}
-    friend class scoped_ptr<OpaqueRootState>;
-  };
-
-  OpaqueRootTracker(JSContext* context,
-                    ReferencedObjectMap* referenced_object_map,
-                    WrapperFactory* wrapper_factory);
-
-  // All objects that implement this functionality must be registered to this
-  // class.
-  void AddObjectWithOpaqueRoot(WrapperPrivate* wrapper_private);
-  void RemoveObjectWithOpaqueRoot(WrapperPrivate* wrapper_private);
-
-  // Get the current state of opaque roots. This should be called when garbage
-  // collection begins before marking has begun. Once garbage collection is
-  // complete, this should be released.
-  scoped_ptr<OpaqueRootState> GetCurrentOpaqueRootState();
-
- private:
-  void TrackReachabilityToOpaqueRoot(OpaqueRootState* state,
-                                     WrapperPrivate* wrapper_private);
-  void TrackReachableWrappables(OpaqueRootState* state,
-                                WrapperPrivate* wrapper_private);
-  typedef base::hash_set<WrapperPrivate*> WrapperPrivateSet;
-
-  JSContext* context_;
-  ReferencedObjectMap* referenced_object_map_;
-  WrapperFactory* wrapper_factory_;
-  // list of objects that are potentially reachable from an opaque root
-  WrapperPrivateSet all_objects_;
-};
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
-#endif  // COBALT_SCRIPT_MOZJS_45_OPAQUE_ROOT_TRACKER_H_
diff --git a/src/cobalt/script/mozjs-45/wrapper_private.cc b/src/cobalt/script/mozjs-45/wrapper_private.cc
index 44a0930..e344ef3 100644
--- a/src/cobalt/script/mozjs-45/wrapper_private.cc
+++ b/src/cobalt/script/mozjs-45/wrapper_private.cc
@@ -89,40 +89,13 @@
   }
 }
 
-Wrappable* WrapperPrivate::GetOpaqueRoot() const {
-  if (!get_opaque_root_function_.is_null()) {
-    return get_opaque_root_function_.Run(wrappable_);
-  }
-  return NULL;
-}
-
-void WrapperPrivate::GetReachableWrappables(
-    std::vector<Wrappable*>* reachable) {
-  if (!get_reachable_wrappables_function_.is_null()) {
-    return get_reachable_wrappables_function_.Run(wrappable_, reachable);
-  }
-}
-
-bool WrapperPrivate::ShouldKeepWrapperAliveIfReachable() {
-  const ProxyHandler* proxy_handler =
-      base::polymorphic_downcast<const ProxyHandler*>(
-          js::GetProxyHandler(wrapper_proxy_));
-
-  DCHECK(proxy_handler);
-  return proxy_handler->has_custom_property() ||
-         wrappable_->ShouldKeepWrapperAlive();
-}
-
 // static
-void WrapperPrivate::AddPrivateData(
-    JSContext* context, JS::HandleObject wrapper_proxy,
-    const scoped_refptr<Wrappable>& wrappable,
-    const GetOpaqueRootFunction& get_opaque_root_function,
-    const GetReachableWrappablesFunction& get_reachable_wrappables_function) {
+void WrapperPrivate::AddPrivateData(JSContext* context,
+                                    JS::HandleObject wrapper_proxy,
+                                    const scoped_refptr<Wrappable>& wrappable) {
   DCHECK(js::IsProxy(wrapper_proxy));
-  WrapperPrivate* private_data = new WrapperPrivate(
-      context, wrappable, wrapper_proxy, get_opaque_root_function,
-      get_reachable_wrappables_function);
+  WrapperPrivate* private_data =
+      new WrapperPrivate(context, wrappable, wrapper_proxy);
   JS::RootedObject target_object(context,
                                  js::GetProxyTargetObject(wrapper_proxy));
   JS_SetPrivate(target_object, private_data);
@@ -220,32 +193,14 @@
   }
 }
 
-WrapperPrivate::WrapperPrivate(
-    JSContext* context, const scoped_refptr<Wrappable>& wrappable,
-    JS::HandleObject wrapper_proxy,
-    const GetOpaqueRootFunction& get_opaque_root_function,
-    const GetReachableWrappablesFunction& get_reachable_wrappables_function)
-    : context_(context),
-      wrappable_(wrappable),
-      wrapper_proxy_(wrapper_proxy),
-      get_opaque_root_function_(get_opaque_root_function),
-      get_reachable_wrappables_function_(get_reachable_wrappables_function) {
+WrapperPrivate::WrapperPrivate(JSContext* context,
+                               const scoped_refptr<Wrappable>& wrappable,
+                               JS::HandleObject wrapper_proxy)
+    : context_(context), wrappable_(wrappable), wrapper_proxy_(wrapper_proxy) {
   DCHECK(js::IsProxy(wrapper_proxy));
-  if (!get_opaque_root_function_.is_null() ||
-      !get_reachable_wrappables_function_.is_null()) {
-    MozjsGlobalEnvironment* global_environment =
-        MozjsGlobalEnvironment::GetFromContext(context_);
-    global_environment->opaque_root_tracker()->AddObjectWithOpaqueRoot(this);
-  }
 }
 
 WrapperPrivate::~WrapperPrivate() {
-  if (!get_opaque_root_function_.is_null() ||
-      !get_reachable_wrappables_function_.is_null()) {
-    MozjsGlobalEnvironment* global_environment =
-        MozjsGlobalEnvironment::GetFromContext(context_);
-    global_environment->opaque_root_tracker()->RemoveObjectWithOpaqueRoot(this);
-  }
   wrapper_proxy_ = NULL;
 }
 
diff --git a/src/cobalt/script/mozjs-45/wrapper_private.h b/src/cobalt/script/mozjs-45/wrapper_private.h
index 7e4b917..0483cea 100644
--- a/src/cobalt/script/mozjs-45/wrapper_private.h
+++ b/src/cobalt/script/mozjs-45/wrapper_private.h
@@ -58,10 +58,6 @@
 class WrapperPrivate : public base::SupportsWeakPtr<WrapperPrivate> {
  public:
   typedef std::vector<Wrappable*> WrappableVector;
-  typedef base::Callback<Wrappable*(const scoped_refptr<Wrappable>&)>
-      GetOpaqueRootFunction;
-  typedef base::Callback<void(const scoped_refptr<Wrappable>&,
-                              WrappableVector*)> GetReachableWrappablesFunction;
 
   template <typename T>
   scoped_refptr<T> wrappable() const {
@@ -70,25 +66,9 @@
 
   JSObject* js_object_proxy() const { return wrapper_proxy_; }
 
-  Wrappable* GetOpaqueRoot() const;
-  void GetReachableWrappables(std::vector<Wrappable*>* reachable);
-
-  // Return true if the GC should avoid collecting this wrapper. Note that if
-  // the wrapper is unreachable, it may still be collected.
-  bool ShouldKeepWrapperAliveIfReachable();
-
   // Create a new WrapperPrivate instance and associate it with the wrapper.
-  static void AddPrivateData(
-      JSContext* context, JS::HandleObject wrapper_proxy,
-      const scoped_refptr<Wrappable>& wrappable,
-      const GetOpaqueRootFunction& get_opaque_root_function,
-      const GetReachableWrappablesFunction& get_reachable_wrappables_function);
-
   static void AddPrivateData(JSContext* context, JS::HandleObject wrapper_proxy,
-                             const scoped_refptr<Wrappable>& wrappable) {
-    AddPrivateData(context, wrapper_proxy, wrappable, GetOpaqueRootFunction(),
-                   GetReachableWrappablesFunction());
-  }
+                             const scoped_refptr<Wrappable>& wrappable);
 
   // Return true if the object has wrapper private.
   static bool HasWrapperPrivate(JSContext* context, JS::HandleObject object);
@@ -118,18 +98,13 @@
   static void Trace(JSTracer* trace, JSObject* object);
 
  private:
-  WrapperPrivate(
-      JSContext* context, const scoped_refptr<Wrappable>& wrappable,
-      JS::HandleObject wrapper_proxy,
-      const GetOpaqueRootFunction& get_opaque_root_function,
-      const GetReachableWrappablesFunction& get_reachable_wrappables_function);
+  WrapperPrivate(JSContext* context, const scoped_refptr<Wrappable>& wrappable,
+                 JS::HandleObject wrapper_proxy);
   ~WrapperPrivate();
 
   JSContext* context_;
   scoped_refptr<Wrappable> wrappable_;
   JS::Heap<JSObject*> wrapper_proxy_;
-  GetOpaqueRootFunction get_opaque_root_function_;
-  GetReachableWrappablesFunction get_reachable_wrappables_function_;
 
   friend Tracer;
 };
diff --git a/src/cobalt/script/mozjs/mozjs.gyp b/src/cobalt/script/mozjs/mozjs.gyp
index ce2763c..1a2a752 100644
--- a/src/cobalt/script/mozjs/mozjs.gyp
+++ b/src/cobalt/script/mozjs/mozjs.gyp
@@ -28,7 +28,6 @@
         'mozjs_script_value_factory.cc',
         'mozjs_source_code.cc',
         'mozjs_trace_logging.cc',
-        'opaque_root_tracker.cc',
         'promise_wrapper.cc',
         'proxy_handler.cc',
         'referenced_object_map.cc',
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc
index e306a50..e7d64a1 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -162,8 +162,6 @@
   wrapper_factory_.reset(new WrapperFactory(context_));
   script_value_factory_.reset(new MozjsScriptValueFactory(this));
   referenced_objects_.reset(new ReferencedObjectMap(context_));
-  opaque_root_tracker_.reset(new OpaqueRootTracker(
-      context_, referenced_objects_.get(), wrapper_factory_.get()));
 
   JS_AddExtraGCRootsTracer(runtime, TraceFunction, this);
 }
@@ -394,37 +392,20 @@
 void MozjsGlobalEnvironment::BeginGarbageCollection() {
   TRACK_MEMORY_SCOPE("Javascript");
   // It's possible that a GC could be triggered from within the
-  // BeginGarbageCollection callback. Only create the OpaqueRootState the
-  // first time we enter. Also, only verify that |visisted_wrappables_| is
-  // empty in this case.
-  // TODO: Opaque root logic is a special case of tracing wrappables, and
-  // should be removed.
+  // BeginGarbageCollection callback. Only verify that |visisted_wrappables_|
+  // is empty the first time we enter.
   garbage_collection_count_++;
 
   if (garbage_collection_count_ == 1) {
-    if (global_object_proxy_) {
-      DCHECK(!opaque_root_state_);
-      JSAutoRequest auto_request(context_);
-      JSAutoCompartment auto_compartment(context_, global_object_proxy_);
-      // Get the current state of opaque root relationships. Keep this object
-      // alive for the duration of the GC phase to ensure that reachability
-      // between roots and reachable objects is maintained.
-      opaque_root_state_ = opaque_root_tracker_->GetCurrentOpaqueRootState();
-    }
-
     DCHECK_EQ(visited_wrappables_.size(), 0);
   }
 }
 
 void MozjsGlobalEnvironment::EndGarbageCollection() {
-  // Reset opaque root reachability relationships. Also reset
-  // |visisted_wrappables_|.
-  // TODO: Opaque root logic is a special case of tracing wrappables, and
-  // should be removed.
+  // Reset |visisted_wrappables_|.
   garbage_collection_count_--;
   DCHECK_GE(garbage_collection_count_, 0);
   if (garbage_collection_count_ == 0) {
-    opaque_root_state_.reset(NULL);
     visited_wrappables_.clear();
   }
 }
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.h b/src/cobalt/script/mozjs/mozjs_global_environment.h
index 6c79872..4914e18 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.h
@@ -25,7 +25,6 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/mozjs/interface_data.h"
-#include "cobalt/script/mozjs/opaque_root_tracker.h"
 #include "cobalt/script/mozjs/util/exception_helpers.h"
 #include "cobalt/script/mozjs/weak_heap_object_manager.h"
 #include "cobalt/script/mozjs/wrapper_factory.h"
@@ -98,10 +97,6 @@
 
   WeakHeapObjectManager* weak_object_manager() { return &weak_object_manager_; }
 
-  OpaqueRootTracker* opaque_root_tracker() {
-    return opaque_root_tracker_.get();
-  }
-
   base::hash_set<Wrappable*>* visited_wrappables() {
     return &visited_wrappables_;
   }
@@ -169,13 +164,11 @@
   WeakHeapObjectManager weak_object_manager_;
   CachedWrapperMultiMap kept_alive_objects_;
   scoped_ptr<ReferencedObjectMap> referenced_objects_;
-  scoped_ptr<OpaqueRootTracker> opaque_root_tracker_;
   CachedInterfaceData cached_interface_data_;
   STLValueDeleter<CachedInterfaceData> cached_interface_data_deleter_;
   ContextDestructor context_destructor_;
   scoped_ptr<WrapperFactory> wrapper_factory_;
   scoped_ptr<MozjsScriptValueFactory> script_value_factory_;
-  scoped_ptr<OpaqueRootTracker::OpaqueRootState> opaque_root_state_;
   JS::Heap<JSObject*> global_object_proxy_;
   EnvironmentSettings* environment_settings_;
   // TODO: Should be |std::unordered_set| once C++11 is enabled.
diff --git a/src/cobalt/script/mozjs/opaque_root_tracker.cc b/src/cobalt/script/mozjs/opaque_root_tracker.cc
deleted file mode 100644
index 2663e0b..0000000
--- a/src/cobalt/script/mozjs/opaque_root_tracker.cc
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/script/mozjs/opaque_root_tracker.h"
-
-#include <utility>
-#include <vector>
-
-#include "cobalt/script/mozjs/weak_heap_object.h"
-#include "third_party/mozjs/js/src/jsapi.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-namespace {
-// Implementation of OpaqueRootTracker::OpaqueRootState.
-// On creation, this class will register reachability between objects and their
-// roots in |ReferencedObjectMap|. On destruction, the reachability
-// relationship will be removed.
-class OpaqueRootStateImpl : public OpaqueRootTracker::OpaqueRootState {
- public:
-  OpaqueRootStateImpl(JSContext* context,
-                      ReferencedObjectMap* referenced_object_map)
-      : context_(context), referenced_object_map_(referenced_object_map) {}
-
-  void TrackReachability(WrapperPrivate* from, WrapperPrivate* to) {
-    intptr_t from_key = ReferencedObjectMap::GetKeyForWrappable(
-        from->wrappable<Wrappable>().get());
-    JSObject* to_proxy = to->js_object_proxy();
-    DCHECK(to_proxy);
-    JS::RootedValue to_value(context_, JS::ObjectValue(*to_proxy));
-    referenced_objects_.push_back(
-        std::make_pair(from_key, WeakHeapObject(context_, to_value)));
-    referenced_object_map_->AddReferencedObject(from_key, to_value);
-  }
-
-  ~OpaqueRootStateImpl() {
-    JSAutoRequest auto_request(context_);
-    for (ReferencedObjectPairVector::iterator it = referenced_objects_.begin();
-         it != referenced_objects_.end(); ++it) {
-      WeakHeapObject &value = it->second;
-      if (value.IsGcThing() && !value.WasCollected()) {
-        JS::RootedValue reachable_value(context_, value.GetValue());
-        referenced_object_map_->RemoveReferencedObject(it->first,
-                                                       reachable_value);
-      }
-    }
-  }
-
- private:
-  typedef std::vector<std::pair<intptr_t, WeakHeapObject> >
-      ReferencedObjectPairVector;
-
-  JSContext* context_;
-  ReferencedObjectMap* referenced_object_map_;
-  ReferencedObjectPairVector referenced_objects_;
-};
-}  // namespace
-
-OpaqueRootTracker::OpaqueRootTracker(JSContext* context,
-                                     ReferencedObjectMap* referenced_object_map,
-                                     WrapperFactory* wrapper_factory)
-    : context_(context),
-      referenced_object_map_(referenced_object_map),
-      wrapper_factory_(wrapper_factory) {}
-
-void OpaqueRootTracker::AddObjectWithOpaqueRoot(
-    WrapperPrivate* wrapper_private) {
-  all_objects_.insert(wrapper_private);
-}
-
-void OpaqueRootTracker::RemoveObjectWithOpaqueRoot(
-    WrapperPrivate* wrapper_private) {
-  all_objects_.erase(wrapper_private);
-}
-
-scoped_ptr<OpaqueRootTracker::OpaqueRootState>
-OpaqueRootTracker::GetCurrentOpaqueRootState() {
-  scoped_ptr<OpaqueRootStateImpl> state(
-      new OpaqueRootStateImpl(context_, referenced_object_map_));
-  // Get the current opaque root for all objects that are being tracked.
-  for (WrapperPrivateSet::iterator it = all_objects_.begin();
-       it != all_objects_.end(); ++it) {
-    WrapperPrivate* wrapper_private = *it;
-    TrackReachabilityToOpaqueRoot(state.get(), wrapper_private);
-    TrackReachableWrappables(state.get(), wrapper_private);
-  }
-  return state.PassAs<OpaqueRootState>();
-}
-
-void OpaqueRootTracker::TrackReachabilityToOpaqueRoot(
-    OpaqueRootState* state, WrapperPrivate* wrapper_private) {
-  OpaqueRootStateImpl* state_impl =
-      base::polymorphic_downcast<OpaqueRootStateImpl*>(state);
-  // If this wrappable has an opaque root, track reachability between this
-  // wrappable and its root.
-  Wrappable* opaque_root = wrapper_private->GetOpaqueRoot();
-  if (opaque_root) {
-    WrapperPrivate* opaque_root_private = WrapperPrivate::GetFromWrappable(
-        opaque_root, context_, wrapper_factory_);
-    // Always mark the root as reachable from the non-root object.
-    state_impl->TrackReachability(wrapper_private, opaque_root_private);
-
-    // Only mark the non-root object as reachable if we need to keep the
-    // wrapper alive for some reason. In general it's okay for a wrapper to
-    // get GC'd because the Cobalt object will still be kept alive, and a new
-    // JS object can be created if needed again.
-    if (wrapper_private->ShouldKeepWrapperAliveIfReachable()) {
-      state_impl->TrackReachability(opaque_root_private, wrapper_private);
-    }
-  }
-}
-
-void OpaqueRootTracker::TrackReachableWrappables(
-    OpaqueRootState* state, WrapperPrivate* wrapper_private) {
-  OpaqueRootStateImpl* state_impl =
-      base::polymorphic_downcast<OpaqueRootStateImpl*>(state);
-  // Track any wrappables that are explicitly marked as reachable from
-  // this wrappable.
-  typedef std::vector<Wrappable*> WrappableVector;
-  WrappableVector reachable_objects;
-  wrapper_private->GetReachableWrappables(&reachable_objects);
-  for (size_t i = 0; i < reachable_objects.size(); ++i) {
-    WrapperPrivate* reachable_object_private = WrapperPrivate::GetFromWrappable(
-        reachable_objects[i], context_, wrapper_factory_);
-    state_impl->TrackReachability(wrapper_private, reachable_object_private);
-  }
-}
-
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs/opaque_root_tracker.h b/src/cobalt/script/mozjs/opaque_root_tracker.h
deleted file mode 100644
index 2b3aade..0000000
--- a/src/cobalt/script/mozjs/opaque_root_tracker.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#ifndef COBALT_SCRIPT_MOZJS_OPAQUE_ROOT_TRACKER_H_
-#define COBALT_SCRIPT_MOZJS_OPAQUE_ROOT_TRACKER_H_
-
-#include "base/hash_tables.h"
-#include "cobalt/script/mozjs/referenced_object_map.h"
-#include "cobalt/script/mozjs/wrapper_factory.h"
-#include "cobalt/script/mozjs/wrapper_private.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-// This class manages lifetime of structures containing objects that are
-// reachable from through the interface implementation, but not through
-// JavaScript, by ensuring that the root of the structure is not garbage
-// collected if any of the members of the structure are reachable.
-//
-// The implementation of the structure in Cobalt is typically reference counted.
-// The |root| of that structure is a single node that transitively holds a
-// reference to every other node in the structure. It may be the case that the
-// only reference to that root node is from a JS object's WrapperPrivate. The
-// OpaqueRootTracker class ensures that if some node in the structure is
-// reachable from JavaScript, the JS object holding the reference to the root
-// of the structure is marked as reachable as well, which preserves the final
-// reference to the root of the structure, keeping the entire structure alive.
-//
-// For example, any DOM Node can be reached from any other DOM Node object in
-// the same tree. If an arbitary internal Node is reachable from JavaScript,
-// this class will ensure that the root of the tree will also be kept alive,
-// preserving the entire tree until no nodes in the tree are reachable from
-// JavaScript.
-//
-// An object's opaque root can change throughout the object's lifetime, so the
-// root needs to be recalculated every garbage collection phase.
-class OpaqueRootTracker {
- public:
-  // Callers do not need to operate on this class. They just need to manage its
-  // lifetime appropriately as described below.
-  class OpaqueRootState {
-   protected:
-    OpaqueRootState() {}
-    virtual ~OpaqueRootState() {}
-    friend class scoped_ptr<OpaqueRootState>;
-  };
-
-  OpaqueRootTracker(JSContext* context,
-                    ReferencedObjectMap* referenced_object_map,
-                    WrapperFactory* wrapper_factory);
-
-  // All objects that implement this functionality must be registered to this
-  // class.
-  void AddObjectWithOpaqueRoot(WrapperPrivate* wrapper_private);
-  void RemoveObjectWithOpaqueRoot(WrapperPrivate* wrapper_private);
-
-  // Get the current state of opaque roots. This should be called when garbage
-  // collection begins before marking has begun. Once garbage collection is
-  // complete, this should be released.
-  scoped_ptr<OpaqueRootState> GetCurrentOpaqueRootState();
-
- private:
-  void TrackReachabilityToOpaqueRoot(OpaqueRootState* state,
-                                     WrapperPrivate* wrapper_private);
-  void TrackReachableWrappables(OpaqueRootState* state,
-                                WrapperPrivate* wrapper_private);
-  typedef base::hash_set<WrapperPrivate*> WrapperPrivateSet;
-
-  JSContext* context_;
-  ReferencedObjectMap* referenced_object_map_;
-  WrapperFactory* wrapper_factory_;
-  // list of objects that are potentially reachable from an opaque root
-  WrapperPrivateSet all_objects_;
-};
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
-#endif  // COBALT_SCRIPT_MOZJS_OPAQUE_ROOT_TRACKER_H_
diff --git a/src/cobalt/script/mozjs/wrapper_private.cc b/src/cobalt/script/mozjs/wrapper_private.cc
index 7b8d983..3814020 100644
--- a/src/cobalt/script/mozjs/wrapper_private.cc
+++ b/src/cobalt/script/mozjs/wrapper_private.cc
@@ -88,38 +88,13 @@
   }
 }
 
-Wrappable* WrapperPrivate::GetOpaqueRoot() const {
-  if (!get_opaque_root_function_.is_null()) {
-    return get_opaque_root_function_.Run(wrappable_);
-  }
-  return NULL;
-}
-
-void WrapperPrivate::GetReachableWrappables(
-    std::vector<Wrappable*>* reachable) {
-  if (!get_reachable_wrappables_function_.is_null()) {
-    return get_reachable_wrappables_function_.Run(wrappable_, reachable);
-  }
-}
-
-bool WrapperPrivate::ShouldKeepWrapperAliveIfReachable() {
-  ProxyHandler* proxy_handler = base::polymorphic_downcast<ProxyHandler*>(
-      js::GetProxyHandler(wrapper_proxy_));
-  DCHECK(proxy_handler);
-  return proxy_handler->has_custom_property() ||
-         wrappable_->ShouldKeepWrapperAlive();
-}
-
 // static
-void WrapperPrivate::AddPrivateData(
-    JSContext* context, JS::HandleObject wrapper_proxy,
-    const scoped_refptr<Wrappable>& wrappable,
-    const GetOpaqueRootFunction& get_opaque_root_function,
-    const GetReachableWrappablesFunction& get_reachable_wrappables_function) {
+void WrapperPrivate::AddPrivateData(JSContext* context,
+                                    JS::HandleObject wrapper_proxy,
+                                    const scoped_refptr<Wrappable>& wrappable) {
   DCHECK(js::IsProxy(wrapper_proxy));
-  WrapperPrivate* private_data = new WrapperPrivate(
-      context, wrappable, wrapper_proxy, get_opaque_root_function,
-      get_reachable_wrappables_function);
+  WrapperPrivate* private_data =
+      new WrapperPrivate(context, wrappable, wrapper_proxy);
   JS::RootedObject target_object(context,
                                  js::GetProxyTargetObject(wrapper_proxy));
   JS_SetPrivate(target_object, private_data);
@@ -216,32 +191,14 @@
   }
 }
 
-WrapperPrivate::WrapperPrivate(
-    JSContext* context, const scoped_refptr<Wrappable>& wrappable,
-    JS::HandleObject wrapper_proxy,
-    const GetOpaqueRootFunction& get_opaque_root_function,
-    const GetReachableWrappablesFunction& get_reachable_wrappables_function)
-    : context_(context),
-      wrappable_(wrappable),
-      wrapper_proxy_(wrapper_proxy),
-      get_opaque_root_function_(get_opaque_root_function),
-      get_reachable_wrappables_function_(get_reachable_wrappables_function) {
+WrapperPrivate::WrapperPrivate(JSContext* context,
+                               const scoped_refptr<Wrappable>& wrappable,
+                               JS::HandleObject wrapper_proxy)
+    : context_(context), wrappable_(wrappable), wrapper_proxy_(wrapper_proxy) {
   DCHECK(js::IsProxy(wrapper_proxy));
-  if (!get_opaque_root_function_.is_null() ||
-      !get_reachable_wrappables_function_.is_null()) {
-    MozjsGlobalEnvironment* global_environment =
-        MozjsGlobalEnvironment::GetFromContext(context_);
-    global_environment->opaque_root_tracker()->AddObjectWithOpaqueRoot(this);
-  }
 }
 
 WrapperPrivate::~WrapperPrivate() {
-  if (!get_opaque_root_function_.is_null() ||
-      !get_reachable_wrappables_function_.is_null()) {
-    MozjsGlobalEnvironment* global_environment =
-        MozjsGlobalEnvironment::GetFromContext(context_);
-    global_environment->opaque_root_tracker()->RemoveObjectWithOpaqueRoot(this);
-  }
   wrapper_proxy_ = NULL;
 }
 
diff --git a/src/cobalt/script/mozjs/wrapper_private.h b/src/cobalt/script/mozjs/wrapper_private.h
index 1ac464f..5f15731 100644
--- a/src/cobalt/script/mozjs/wrapper_private.h
+++ b/src/cobalt/script/mozjs/wrapper_private.h
@@ -58,10 +58,6 @@
 class WrapperPrivate : public base::SupportsWeakPtr<WrapperPrivate> {
  public:
   typedef std::vector<Wrappable*> WrappableVector;
-  typedef base::Callback<Wrappable*(const scoped_refptr<Wrappable>&)>
-      GetOpaqueRootFunction;
-  typedef base::Callback<void(const scoped_refptr<Wrappable>&,
-                              WrappableVector*)> GetReachableWrappablesFunction;
 
   template <typename T>
   scoped_refptr<T> wrappable() const {
@@ -70,25 +66,9 @@
 
   JSObject* js_object_proxy() const { return wrapper_proxy_; }
 
-  Wrappable* GetOpaqueRoot() const;
-  void GetReachableWrappables(std::vector<Wrappable*>* reachable);
-
-  // Return true if the GC should avoid collecting this wrapper. Note that if
-  // the wrapper is unreachable, it may still be collected.
-  bool ShouldKeepWrapperAliveIfReachable();
-
   // Create a new WrapperPrivate instance and associate it with the wrapper.
-  static void AddPrivateData(
-      JSContext* context, JS::HandleObject wrapper_proxy,
-      const scoped_refptr<Wrappable>& wrappable,
-      const GetOpaqueRootFunction& get_opaque_root_function,
-      const GetReachableWrappablesFunction& get_reachable_wrappables_function);
-
   static void AddPrivateData(JSContext* context, JS::HandleObject wrapper_proxy,
-                             const scoped_refptr<Wrappable>& wrappable) {
-    AddPrivateData(context, wrapper_proxy, wrappable, GetOpaqueRootFunction(),
-                   GetReachableWrappablesFunction());
-  }
+                             const scoped_refptr<Wrappable>& wrappable);
 
   // Return true if the object has wrapper private.
   static bool HasWrapperPrivate(JSContext* context, JS::HandleObject object);
@@ -118,18 +98,13 @@
   static void Trace(JSTracer* trace, JSObject* object);
 
  private:
-  WrapperPrivate(
-      JSContext* context, const scoped_refptr<Wrappable>& wrappable,
-      JS::HandleObject wrapper_proxy,
-      const GetOpaqueRootFunction& get_opaque_root_function,
-      const GetReachableWrappablesFunction& get_reachable_wrappables_function);
+  WrapperPrivate(JSContext* context, const scoped_refptr<Wrappable>& wrappable,
+                 JS::HandleObject wrapper_proxy);
   ~WrapperPrivate();
 
   JSContext* context_;
   scoped_refptr<Wrappable> wrappable_;
   JS::Heap<JSObject*> wrapper_proxy_;
-  GetOpaqueRootFunction get_opaque_root_function_;
-  GetReachableWrappablesFunction get_reachable_wrappables_function_;
 
   friend Tracer;
 };
diff --git a/src/cobalt/webdriver/execute_test.cc b/src/cobalt/webdriver/execute_test.cc
index 50018d8..2a2e852 100644
--- a/src/cobalt/webdriver/execute_test.cc
+++ b/src/cobalt/webdriver/execute_test.cc
@@ -18,10 +18,10 @@
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "cobalt/dom/document.h"
+#include "cobalt/dom/testing/stub_window.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/webdriver/script_executor.h"
-#include "cobalt/webdriver/testing/stub_window.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -62,7 +62,7 @@
 class ScriptExecutorTest : public ::testing::Test {
  protected:
   void SetUp() OVERRIDE {
-    stub_window_.reset(new testing::StubWindow());
+    stub_window_.reset(new dom::testing::StubWindow());
     script_executor_ =
         ScriptExecutor::Create(&element_mapping_, global_environment());
 
@@ -78,7 +78,7 @@
   }
 
  protected:
-  scoped_ptr<testing::StubWindow> stub_window_;
+  scoped_ptr<dom::testing::StubWindow> stub_window_;
   MockElementMapping element_mapping_;
   scoped_refptr<ScriptExecutor> script_executor_;
 };
diff --git a/src/cobalt/webdriver/webdriver.gyp b/src/cobalt/webdriver/webdriver.gyp
index e6b431b..64e7481 100644
--- a/src/cobalt/webdriver/webdriver.gyp
+++ b/src/cobalt/webdriver/webdriver.gyp
@@ -89,6 +89,7 @@
       'dependencies': [
         '<(DEPTH)/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/net/net.gyp:http_server',
         'copy_webdriver_data',
diff --git a/src/cobalt/websocket/close_event.h b/src/cobalt/websocket/close_event.h
index 2a23cad..43b7a79 100644
--- a/src/cobalt/websocket/close_event.h
+++ b/src/cobalt/websocket/close_event.h
@@ -30,13 +30,11 @@
       : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
   explicit CloseEvent(const std::string& type)
       : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
-  CloseEvent(const base::Token type,
-             const cobalt::websocket::CloseEventInit& eventInitDict)
+  CloseEvent(const base::Token type, const CloseEventInit& eventInitDict)
       : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
     InitializeFromCloseEventInit(eventInitDict);
   }
-  CloseEvent(const std::string& type,
-             const cobalt::websocket::CloseEventInit& eventInitDict)
+  CloseEvent(const std::string& type, const CloseEventInit& eventInitDict)
       : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
     InitializeFromCloseEventInit(eventInitDict);
   }
diff --git a/src/cobalt/websocket/close_event.idl b/src/cobalt/websocket/close_event.idl
index 28023c1..425e0d2 100644
--- a/src/cobalt/websocket/close_event.idl
+++ b/src/cobalt/websocket/close_event.idl
@@ -12,9 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).
-// This software or document includes material copied from or derived from
-// The WebSocket API https://www.w3.org/TR/websockets/#event-definitions
+// https://www.w3.org/TR/websockets/#closeevent
 
 [Constructor(DOMString type, optional CloseEventInit eventInitDict)]
 interface CloseEvent : Event {
diff --git a/src/cobalt/websocket/close_event_init.idl b/src/cobalt/websocket/close_event_init.idl
index ccd1237..29805c0 100644
--- a/src/cobalt/websocket/close_event_init.idl
+++ b/src/cobalt/websocket/close_event_init.idl
@@ -12,12 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).
-// This software or document includes material copied from or derived from
-// The WebSocket API https://www.w3.org/TR/websockets/#event-definitions
-
-// From:
 // https://html.spec.whatwg.org/multipage/comms.html#the-closeevent-interfaces
+
 dictionary CloseEventInit : EventInit {
   boolean wasClean = false;
   unsigned short code = 0;
diff --git a/src/cobalt/websocket/web_socket.idl b/src/cobalt/websocket/web_socket.idl
index b0d2127..f4efaab 100644
--- a/src/cobalt/websocket/web_socket.idl
+++ b/src/cobalt/websocket/web_socket.idl
@@ -12,11 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-/*
- * Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).
- * This software or document includes material copied from or derived from
- * The WebSocket API https://www.w3.org/TR/websockets/
- */
+// https://www.w3.org/TR/websockets/#the-websocket-interface
 
 [
   Constructor(DOMString url, optional DOMString protocols),
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index d13c46d..1c377ef 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -58,6 +58,7 @@
       ],
       'dependencies': [
         'websocket',
+        '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
         '<(DEPTH)/testing/gmock.gyp:gmock',
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index 806c7c5..cd119d6 100644
--- a/src/cobalt/xhr/xml_http_request.cc
+++ b/src/cobalt/xhr/xml_http_request.cc
@@ -686,6 +686,13 @@
   }
 }
 
+void XMLHttpRequest::TraceMembers(script::Tracer* tracer) {
+  XMLHttpRequestEventTarget::TraceMembers(tracer);
+
+  tracer->Trace(upload_or_null());
+  tracer->Trace(response_array_buffer_or_null());
+}
+
 XMLHttpRequest::~XMLHttpRequest() {
   DCHECK(thread_checker_.CalledOnValidThread());
   dom::GlobalStats::GetInstance()->Remove(this);
diff --git a/src/cobalt/xhr/xml_http_request.h b/src/cobalt/xhr/xml_http_request.h
index 5e10595..7a7f997 100644
--- a/src/cobalt/xhr/xml_http_request.h
+++ b/src/cobalt/xhr/xml_http_request.h
@@ -169,6 +169,8 @@
     return response_array_buffer_.get();
   }
 
+  void TraceMembers(script::Tracer* tracer) OVERRIDE;
+
   friend std::ostream& operator<<(std::ostream& os, const XMLHttpRequest& xhr);
   DEFINE_WRAPPABLE_TYPE(XMLHttpRequest);
 
diff --git a/src/cobalt/xhr/xml_http_request.idl b/src/cobalt/xhr/xml_http_request.idl
index 3b164d6..a95bfcd 100644
--- a/src/cobalt/xhr/xml_http_request.idl
+++ b/src/cobalt/xhr/xml_http_request.idl
@@ -17,7 +17,6 @@
 [
     Constructor,
     ConstructorCallWith=EnvironmentSettings,
-    AddOpaqueRoots=("upload_or_null","response_array_buffer_or_null"),
 ] interface XMLHttpRequest : XMLHttpRequestEventTarget {
     // event handler
     attribute EventHandler onreadystatechange;
diff --git a/src/cobalt/xhr/xml_http_request_event_target.cc b/src/cobalt/xhr/xml_http_request_event_target.cc
index 0bdd713..6ddc698 100644
--- a/src/cobalt/xhr/xml_http_request_event_target.cc
+++ b/src/cobalt/xhr/xml_http_request_event_target.cc
@@ -123,5 +123,10 @@
   }
   SetAttributeEventListener(base::Tokens::timeout(), listener);
 }
+
+void XMLHttpRequestEventTarget::TraceMembers(script::Tracer* tracer) {
+  dom::EventTarget::TraceMembers(tracer);
+}
+
 }  // namespace xhr
 }  // namespace cobalt
diff --git a/src/cobalt/xhr/xml_http_request_event_target.h b/src/cobalt/xhr/xml_http_request_event_target.h
index a4f8c6d..9297eeb 100644
--- a/src/cobalt/xhr/xml_http_request_event_target.h
+++ b/src/cobalt/xhr/xml_http_request_event_target.h
@@ -43,6 +43,8 @@
   void set_onprogress(const EventListenerScriptValue& listener);
   void set_ontimeout(const EventListenerScriptValue& listener);
 
+  void TraceMembers(script::Tracer* tracer) OVERRIDE;
+
   DEFINE_WRAPPABLE_TYPE(XMLHttpRequestEventTarget);
 
  protected:
diff --git a/src/media/base/sbplayer_pipeline.cc b/src/media/base/sbplayer_pipeline.cc
index 044871c..aaaff86 100644
--- a/src/media/base/sbplayer_pipeline.cc
+++ b/src/media/base/sbplayer_pipeline.cc
@@ -326,6 +326,7 @@
 
   if (!player_) {
     seek_cb.Run(PIPELINE_ERROR_INVALID_STATE);
+    return;
   }
 
   player_->PrepareForSeek();
diff --git a/src/media/player/web_media_player_impl.cc b/src/media/player/web_media_player_impl.cc
index e1690a9..d6d920d 100644
--- a/src/media/player/web_media_player_impl.cc
+++ b/src/media/player/web_media_player_impl.cc
@@ -29,8 +29,12 @@
 #include "media/filters/video_renderer_base.h"
 #include "media/player/web_media_player_proxy.h"
 
+namespace media {
 namespace {
 
+// Used to ensure that there is no more than one instance of WebMediaPlayerImpl.
+WebMediaPlayerImpl* s_instance;
+
 // Limits the range of playback rate.
 //
 // TODO(kylep): Revisit these.
@@ -92,8 +96,6 @@
 
 }  // namespace
 
-namespace media {
-
 #define BIND_TO_RENDER_LOOP(function)          \
   BindToLoop(main_loop_->message_loop_proxy(), \
              base::Bind(function, AsWeakPtr()))
@@ -137,6 +139,9 @@
       is_local_source_(false),
       supports_save_(true),
       suppress_destruction_errors_(false) {
+  DCHECK(!s_instance);
+  s_instance = this;
+
   media_log_->AddEvent(
       media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
 
@@ -181,6 +186,9 @@
 WebMediaPlayerImpl::~WebMediaPlayerImpl() {
   DCHECK(!main_loop_ || main_loop_ == MessageLoop::current());
 
+  DCHECK_EQ(s_instance, this);
+  s_instance = NULL;
+
   if (delegate_) {
     delegate_->UnregisterPlayer(this);
   }
diff --git a/src/starboard/cryptography.h b/src/starboard/cryptography.h
index e9e2cab..8f700a9 100644
--- a/src/starboard/cryptography.h
+++ b/src/starboard/cryptography.h
@@ -25,18 +25,22 @@
 // to maximize usage for SSL.
 //
 //   1. GCM - The preferred block cipher mode for OpenSSL, mainly due to speed.
-//   2. CBC - If GCM is disabled, then SSL will use the CBC stream cipher.
-//      Normally this is less desirable because GCM is faster, but if CBC is
-//      hardware-accelerated, it is likely to be better than software GCM. CBC
-//      is also considered by some to be more secure.
-//   3. CTR - This can be used internally with GCM, as long as the CTR
-//      implementation only uses the last 4 bytes of the IV for the counter.
-//      (i.e. 96-bit IV, 32-bit counter)
-//   4. ECB - This is for if you only have core AES block encryption. It can be
-//      used with any of the other cipher block modes to accelerate the core AES
-//      algorithm if none of the streaming modes can be accelerated.
+//   2. CTR - This can be used internally with GCM, as long as the CTR
+//            implementation only uses the last 4 bytes of the IV for the
+//            counter. (i.e. 96-bit IV, 32-bit counter)
+//   3. ECB - This can be used (with a null IV) with any of the other cipher
+//            block modes to accelerate the core AES algorithm if none of the
+//            streaming modes can be accelerated.
+//   4. CBC - GCM is always preferred if the server and client both support
+//            it. If not, they will generally negotiate down to AES-CBC. If this
+//            happens, and CBC is supported by SbCryptography, then it will be
+//            accelerated appropriately. But, most servers should support GCM,
+//            so it is not likely to come up much, which is why it is the lowest
+//            priority.
 //
-// Further reading on GCM vs CBC vs CTR:
+// Further reading on block cipher modes:
+// https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+// https://crbug.com/442572
 // https://crypto.stackexchange.com/questions/10775/practical-disadvantages-of-gcm-mode-encryption
 
 #ifndef STARBOARD_CRYPTOGRAPHY_H_
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 3982d15..a58e369 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -359,7 +359,7 @@
   SbMediaTransferId transfer;
 
   // [Color Space field] The Matrix Coefficients of the video used to
-  // derive luma and chroma values from reg, green, and blue color
+  // derive luma and chroma values from red, green, and blue color
   // primaries. For clarity, the value and meanings for
   // MatrixCoefficients are adopted from Table 4 of ISO/IEC
   // 23001-8:2013/DCOR1. (0:GBR, 1: BT709, 2: Unspecified, 3:
@@ -368,14 +368,9 @@
   // Luminance)
   SbMediaMatrixId matrix;
 
-  // [Color Space field] The Matrix Coefficients of the video used to
-  // derive luma and chroma values from reg, green, and blue color
-  // primaries. For clarity, the value and meanings for
-  // MatrixCoefficients are adopted from Table 4 of ISO/IEC
-  // 23001-8:2013/DCOR1. (0:GBR, 1: BT709, 2: Unspecified, 3:
-  // Reserved, 4: FCC, 5: BT470BG, 6: SMPTE 170M, 7: SMPTE 240M, 8:
-  // YCOCG, 9: BT2020 Non-constant Luminance, 10: BT2020 Constant
-  // Luminance)
+  // [Color Space field] Clipping of the color ranges. (0:
+  // Unspecified, 1: Broadcast Range, 2: Full range (no clipping), 3:
+  // Defined by MatrixCoefficients/TransferCharacteristics)
   SbMediaRangeId range;
 
   // [Color Space field] Only used if primaries ==
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
index 63b5a66..f38eea8 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
@@ -29,6 +29,7 @@
 using starboard::shared::starboard::cryptography::Algorithm;
 using starboard::shared::starboard::cryptography::kAlgorithmAes128Cbc;
 using starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Ecb;
 using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
 
 SbCryptographyTransformer SbCryptographyCreateTransformer(
@@ -50,11 +51,35 @@
     return kSbCryptographyInvalidTransformer;
   }
 
-  // TODO: Support 64-bit IV with CTR mode.
-  if ((mode == kSbCryptographyBlockCipherModeGcm &&
-       initialization_vector_size != 0) ||
-      (mode != kSbCryptographyBlockCipherModeGcm &&
-       initialization_vector_size != block_size_bits / 8)) {
+  Algorithm combined_algorithm;
+  if (mode == kSbCryptographyBlockCipherModeCbc) {
+    combined_algorithm = kAlgorithmAes128Cbc;
+  } else if (mode == kSbCryptographyBlockCipherModeCtr) {
+    combined_algorithm = kAlgorithmAes128Ctr;
+  } else if (mode == kSbCryptographyBlockCipherModeEcb) {
+    combined_algorithm = kAlgorithmAes128Ecb;
+  } else if (mode == kSbCryptographyBlockCipherModeGcm) {
+    combined_algorithm = kAlgorithmAes128Gcm;
+  } else {
+    SB_DLOG(WARNING) << "Unsupported block cipher mode: " << mode;
+    return kSbCryptographyInvalidTransformer;
+  }
+
+  if (mode == kSbCryptographyBlockCipherModeGcm ||
+      mode == kSbCryptographyBlockCipherModeEcb) {
+    if (initialization_vector_size != 0) {
+      SB_DLOG(WARNING) << "Unsupported initialization_vector_size: "
+                       << initialization_vector_size;
+      return kSbCryptographyInvalidTransformer;
+    }
+  } else if (mode == kSbCryptographyBlockCipherModeCtr) {
+    if (initialization_vector_size != 0 && initialization_vector_size != 12 &&
+        initialization_vector_size != 16 && initialization_vector_size != 32) {
+      SB_DLOG(WARNING) << "Unsupported CTR initialization_vector_size: "
+                       << initialization_vector_size;
+      return kSbCryptographyInvalidTransformer;
+    }
+  } else if (initialization_vector_size != block_size_bits / 8) {
     SB_DLOG(WARNING) << "Unsupported initialization_vector_size: "
                      << initialization_vector_size;
     return kSbCryptographyInvalidTransformer;
@@ -65,22 +90,11 @@
     return kSbCryptographyInvalidTransformer;
   }
 
-  Algorithm combined_algorithm;
-  if (mode == kSbCryptographyBlockCipherModeCbc) {
-    combined_algorithm = kAlgorithmAes128Cbc;
-  } else if (mode == kSbCryptographyBlockCipherModeCtr) {
-    combined_algorithm = kAlgorithmAes128Ctr;
-  } else if (mode == kSbCryptographyBlockCipherModeGcm) {
-    combined_algorithm = kAlgorithmAes128Gcm;
-  } else {
-    SB_DLOG(WARNING) << "Unsupported block cipher mode: " << mode;
-    return kSbCryptographyInvalidTransformer;
-  }
-
   AES_KEY aeskey = {0};
   int result = -1;
   if (direction == kSbCryptographyDirectionDecode &&
       mode != kSbCryptographyBlockCipherModeCtr &&
+      mode != kSbCryptographyBlockCipherModeEcb &&
       mode != kSbCryptographyBlockCipherModeGcm) {
     result = AES_set_decrypt_key(key, key_size * 8, &aeskey);
   } else {
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
index 0eb09a9..f81d293 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/configuration.h"
 #include "starboard/cryptography.h"
+
+#include <algorithm>
+
+#include "starboard/configuration.h"
+#include "starboard/memory.h"
 #include "starboard/shared/starboard/cryptography/cryptography_internal.h"
 #include "starboard/shared/starboard/cryptography/software_aes.h"
 
@@ -23,17 +27,18 @@
 
 using starboard::shared::starboard::cryptography::AES_gcm128_setiv;
 using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr;
 
 void SbCryptographySetInitializationVector(
     SbCryptographyTransformer transformer,
     const void* initialization_vector,
     int initialization_vector_size) {
-  if (transformer->algorithm != kAlgorithmAes128Gcm) {
-    SB_DLOG(ERROR) << "Trying to set initialization vector on non-GCM "
-                   << "transformer.";
-    return;
+  if (transformer->algorithm == kAlgorithmAes128Gcm) {
+    AES_gcm128_setiv(&transformer->gcm_context, &transformer->key,
+                     initialization_vector, initialization_vector_size);
+  } else {
+    SbMemoryCopy(transformer->ivec, initialization_vector,
+                 std::min(initialization_vector_size,
+                          static_cast<int>(sizeof(transformer->ivec))));
   }
-
-  AES_gcm128_setiv(&transformer->gcm_context, &transformer->key,
-                   initialization_vector, initialization_vector_size);
 }
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_transform.cc b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
index 0981a63..bc78761 100644
--- a/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
+++ b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
@@ -21,6 +21,17 @@
 #error "SbCryptography requires SB_API_VERSION >= 4."
 #endif
 
+using starboard::shared::starboard::cryptography::AES_cbc_encrypt;
+using starboard::shared::starboard::cryptography::AES_ctr128_encrypt;
+using starboard::shared::starboard::cryptography::AES_decrypt;
+using starboard::shared::starboard::cryptography::AES_encrypt;
+using starboard::shared::starboard::cryptography::AES_gcm128_decrypt;
+using starboard::shared::starboard::cryptography::AES_gcm128_encrypt;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Cbc;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Ecb;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
 int SbCryptographyTransform(SbCryptographyTransformer transformer,
                             const void* in_data,
                             int in_data_size,
@@ -33,32 +44,50 @@
     return 0;
   }
 
-  if (transformer->algorithm ==
-      starboard::shared::starboard::cryptography::kAlgorithmAes128Cbc) {
-    int enc = transformer->direction == kSbCryptographyDirectionEncode
-                  ? SB_AES_ENCRYPT
-                  : SB_AES_DECRYPT;
-    starboard::shared::starboard::cryptography::AES_cbc_encrypt(
-        in_data, out_data, in_data_size, &(transformer->key), transformer->ivec,
-        enc);
-  } else if (transformer->algorithm ==
-             starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr) {
-    starboard::shared::starboard::cryptography::AES_ctr128_encrypt(
-        in_data, out_data, in_data_size, &(transformer->key), transformer->ivec,
-        transformer->ecount_buf, &transformer->counter);
-  } else if (transformer->algorithm ==
-             starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm) {
-    if (transformer->direction == kSbCryptographyDirectionEncode) {
-      starboard::shared::starboard::cryptography::AES_gcm128_encrypt(
-          &transformer->gcm_context, &transformer->key, in_data, out_data,
-          in_data_size);
-    } else if (transformer->direction == kSbCryptographyDirectionDecode) {
-      starboard::shared::starboard::cryptography::AES_gcm128_decrypt(
-          &transformer->gcm_context, &transformer->key, in_data, out_data,
-          in_data_size);
-    } else {
+  switch (transformer->algorithm) {
+    case kAlgorithmAes128Cbc:
+      AES_cbc_encrypt(in_data, out_data, in_data_size, &(transformer->key),
+                      transformer->ivec,
+                      transformer->direction == kSbCryptographyDirectionEncode ?
+                      SB_AES_ENCRYPT : SB_AES_DECRYPT);
+      break;
+
+    case kAlgorithmAes128Ctr:
+      AES_ctr128_encrypt(in_data, out_data, in_data_size, &(transformer->key),
+                         transformer->ivec, transformer->ecount_buf,
+                         &transformer->counter);
+      break;
+
+    case kAlgorithmAes128Ecb:
+      if (in_data_size % 16 != 0) {
+        SB_DLOG(ERROR) << "ECB called with a non-multiple of the block size.";
+        return -1;
+      }
+
+      if (transformer->direction == kSbCryptographyDirectionEncode) {
+        AES_encrypt(in_data, out_data, &transformer->key);
+      } else if (transformer->direction == kSbCryptographyDirectionDecode) {
+        AES_decrypt(in_data, out_data, &transformer->key);
+      } else {
+        SB_NOTREACHED();
+      }
+      break;
+
+    case kAlgorithmAes128Gcm:
+      if (transformer->direction == kSbCryptographyDirectionEncode) {
+        AES_gcm128_encrypt(&transformer->gcm_context, &transformer->key,
+                           in_data, out_data, in_data_size);
+      } else if (transformer->direction == kSbCryptographyDirectionDecode) {
+        AES_gcm128_decrypt(&transformer->gcm_context, &transformer->key,
+                           in_data, out_data, in_data_size);
+      } else {
+        SB_NOTREACHED();
+      }
+      break;
+
+    default:
       SB_NOTREACHED();
-    }
+      return -1;
   }
 
   return in_data_size;
diff --git a/src/starboard/shared/win32/application_stub.cc b/src/starboard/shared/uwp/application_uwp.cc
similarity index 68%
rename from src/starboard/shared/win32/application_stub.cc
rename to src/starboard/shared/uwp/application_uwp.cc
index 1f56080..9af9928 100644
--- a/src/starboard/shared/win32/application_stub.cc
+++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -12,51 +12,51 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/shared/win32/application_stub.h"
+#include "starboard/shared/uwp/application_uwp.h"
 
 #include "starboard/event.h"
 #include "starboard/log.h"
 
 namespace starboard {
 namespace shared {
-namespace win32 {
+namespace uwp {
 
-ApplicationStub::ApplicationStub() {
+ApplicationUwp::ApplicationUwp() {
   SB_NOTIMPLEMENTED();
 }
 
-ApplicationStub::~ApplicationStub() {
+ApplicationUwp::~ApplicationUwp() {
   SB_NOTIMPLEMENTED();
 }
 
-void ApplicationStub::Initialize() {
+void ApplicationUwp::Initialize() {
   SB_NOTIMPLEMENTED();
 }
 
-void ApplicationStub::Teardown() {
+void ApplicationUwp::Teardown() {
   SB_NOTIMPLEMENTED();
 }
 
-bool ApplicationStub::MayHaveSystemEvents() {
+bool ApplicationUwp::MayHaveSystemEvents() {
   SB_NOTIMPLEMENTED();
   return false;
 }
 
-shared::starboard::Application::Event* ApplicationStub::PollNextSystemEvent() {
+shared::starboard::Application::Event* ApplicationUwp::PollNextSystemEvent() {
   SB_NOTIMPLEMENTED();
   return NULL;
 }
 
 shared::starboard::Application::Event*
-ApplicationStub::WaitForSystemEventWithTimeout(SbTime time) {
+ApplicationUwp::WaitForSystemEventWithTimeout(SbTime time) {
   SB_NOTIMPLEMENTED();
   return NULL;
 }
 
-void ApplicationStub::WakeSystemEventWait() {
+void ApplicationUwp::WakeSystemEventWait() {
   SB_NOTIMPLEMENTED();
 }
 
-}  // namespace win32
+}  // namespace uwp
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/win32/application_stub.h b/src/starboard/shared/uwp/application_uwp.h
similarity index 75%
rename from src/starboard/shared/win32/application_stub.h
rename to src/starboard/shared/uwp/application_uwp.h
index c112677..437033a 100644
--- a/src/starboard/shared/win32/application_stub.h
+++ b/src/starboard/shared/uwp/application_uwp.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_SHARED_WIN32_APPLICATION_STUB_H_
-#define STARBOARD_SHARED_WIN32_APPLICATION_STUB_H_
+#ifndef STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
+#define STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
 
 #include "starboard/configuration.h"
 #include "starboard/shared/internal_only.h"
@@ -23,16 +23,16 @@
 
 namespace starboard {
 namespace shared {
-namespace win32 {
+namespace uwp {
 
 // Stub application engine using the generic queue and a stub implementation.
-class ApplicationStub : public shared::starboard::QueueApplication {
+class ApplicationUwp : public shared::starboard::QueueApplication {
  public:
-  ApplicationStub();
-  ~ApplicationStub() SB_OVERRIDE;
+  ApplicationUwp();
+  ~ApplicationUwp() SB_OVERRIDE;
 
-  static ApplicationStub* Get() {
-    return static_cast<ApplicationStub*>(shared::starboard::Application::Get());
+  static ApplicationUwp* Get() {
+    return static_cast<ApplicationUwp*>(shared::starboard::Application::Get());
   }
 
  protected:
@@ -47,8 +47,8 @@
   void WakeSystemEventWait() SB_OVERRIDE;
 };
 
-}  // namespace win32
+}  // namespace uwp
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // STARBOARD_SHARED_WIN32_APPLICATION_STUB_H_
+#endif  // STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
diff --git a/src/starboard/shared/win32/atomic_public.h b/src/starboard/shared/win32/atomic_public.h
new file mode 100644
index 0000000..56088b3
--- /dev/null
+++ b/src/starboard/shared/win32/atomic_public.h
@@ -0,0 +1,238 @@
+// 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_WIN32_ATOMIC_PUBLIC_H_
+#define STARBOARD_SHARED_WIN32_ATOMIC_PUBLIC_H_
+
+#include "starboard/atomic.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Declarations for Windows Intrinsic Functions
+// Defined here to avoid including Windows headers
+// See https://msdn.microsoft.com/en-us/library/w5405h95.aspx
+
+long _InterlockedCompareExchange(
+  long volatile * Destination,
+  long Exchange,
+  long Comparand
+);
+#pragma intrinsic(_InterlockedCompareExchange)
+
+__int64 _InterlockedCompareExchange64(
+  __int64 volatile * Destination,
+  __int64 Exchange,
+  __int64 Comparand
+);
+#pragma intrinsic(_InterlockedCompareExchange64)
+
+long _InterlockedExchange(
+  long volatile * Target,
+  long Value
+);
+#pragma intrinsic(_InterlockedExchange)
+
+__int64 _InterlockedExchange64(
+  __int64 volatile * Target,
+  __int64 Value
+);
+#pragma intrinsic(_InterlockedExchange64)
+
+long _InterlockedExchangeAdd(
+  long volatile * Addend,
+  long Value
+);
+#pragma intrinsic(_InterlockedExchangeAdd)
+
+__int64 _InterlockedExchangeAdd64(
+  __int64 volatile * Addend,
+  __int64 Value
+);
+#pragma intrinsic(_InterlockedExchangeAdd64)
+
+void _ReadWriteBarrier(void);
+#pragma intrinsic(_ReadWriteBarrier)
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicNoBarrier_CompareAndSwap(volatile SbAtomic32* ptr,
+                                 SbAtomic32 old_value,
+                                 SbAtomic32 new_value) {
+  // Note this does a full memory barrier
+  return _InterlockedCompareExchange(
+      (volatile long*) ptr, (long) new_value, (long) old_value);
+}
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicNoBarrier_Exchange(volatile SbAtomic32* ptr, SbAtomic32 new_value) {
+  // Note this does a full memory barrier
+  return _InterlockedExchange((volatile long*)ptr, (long)new_value);
+}
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicNoBarrier_Increment(volatile SbAtomic32* ptr, SbAtomic32 increment) {
+  return SbAtomicBarrier_Increment(ptr, increment);
+}
+
+SB_C_FORCE_INLINE SbAtomic32 SbAtomicBarrier_Increment(volatile SbAtomic32* ptr,
+                                                       SbAtomic32 increment) {
+  // Note InterlockedExchangeAdd does a full memory barrier
+  return increment + _InterlockedExchangeAdd(
+      (volatile long *)ptr, (long)increment);
+}
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicAcquire_CompareAndSwap(volatile SbAtomic32* ptr,
+                               SbAtomic32 old_value,
+                               SbAtomic32 new_value) {
+  // Note this does a full memory barrier
+  return _InterlockedCompareExchange(
+      (volatile long*) ptr, (long) new_value, (long) old_value);
+}
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicRelease_CompareAndSwap(volatile SbAtomic32* ptr,
+                               SbAtomic32 old_value,
+                               SbAtomic32 new_value) {
+  // Note this does a full memory barrier
+  return _InterlockedCompareExchange(
+      (volatile long*) ptr, (long) new_value, (long) old_value);
+}
+
+// NOTE: https://msdn.microsoft.com/en-us/library/f20w0x5e.aspx
+// states _ReadWriteBarrier() is deprecated and
+// recommends "atomic_thread_fence", which is C++11 and violates
+// Starboard's "C-only header" policy
+SB_C_FORCE_INLINE void SbAtomicMemoryBarrier() {
+  _ReadWriteBarrier();
+}
+
+SB_C_FORCE_INLINE void SbAtomicNoBarrier_Store(volatile SbAtomic32* ptr,
+                                               SbAtomic32 value) {
+  *ptr = value;
+}
+
+SB_C_FORCE_INLINE void SbAtomicAcquire_Store(volatile SbAtomic32* ptr,
+                                             SbAtomic32 value) {
+  *ptr = value;
+  SbAtomicMemoryBarrier();
+}
+
+SB_C_FORCE_INLINE void SbAtomicRelease_Store(volatile SbAtomic32* ptr,
+                                             SbAtomic32 value) {
+  SbAtomicMemoryBarrier();
+  *ptr = value;
+}
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicNoBarrier_Load(volatile const SbAtomic32* ptr) {
+  return *ptr;
+}
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicAcquire_Load(volatile const SbAtomic32* ptr) {
+  SbAtomic32 value = *ptr;
+  SbAtomicMemoryBarrier();
+  return value;
+}
+
+SB_C_FORCE_INLINE SbAtomic32
+SbAtomicRelease_Load(volatile const SbAtomic32* ptr) {
+  SbAtomicMemoryBarrier();
+  return *ptr;
+}
+
+// 64-bit atomic operations (only available on 64-bit processors).
+#if SB_HAS(64_BIT_ATOMICS)
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicNoBarrier_CompareAndSwap64(volatile SbAtomic64* ptr,
+                                   SbAtomic64 old_value,
+                                   SbAtomic64 new_value) {
+  return _InterlockedCompareExchange64(ptr, new_value, old_value);
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicNoBarrier_Exchange64(volatile SbAtomic64* ptr, SbAtomic64 new_value) {
+  return _InterlockedExchange64(ptr, new_value);
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicNoBarrier_Increment64(volatile SbAtomic64* ptr, SbAtomic64 increment) {
+  return increment + _InterlockedExchangeAdd64(ptr, increment);
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicBarrier_Increment64(volatile SbAtomic64* ptr, SbAtomic64 increment) {
+  // Note this does a full memory barrier
+  return increment + _InterlockedExchangeAdd64(ptr, increment);
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicAcquire_CompareAndSwap64(volatile SbAtomic64* ptr,
+                                 SbAtomic64 old_value,
+                                 SbAtomic64 new_value) {
+  // Note this does a full memory barrier
+  return _InterlockedCompareExchange64(ptr, new_value, old_value);
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicRelease_CompareAndSwap64(volatile SbAtomic64* ptr,
+                                 SbAtomic64 old_value,
+                                 SbAtomic64 new_value) {
+  // Note this does a full memory barrier
+  return _InterlockedCompareExchange64(ptr, new_value, old_value);
+}
+
+SB_C_FORCE_INLINE void SbAtomicNoBarrier_Store64(volatile SbAtomic64* ptr,
+                                                 SbAtomic64 value) {
+  *ptr = value;
+}
+
+SB_C_FORCE_INLINE void SbAtomicAcquire_Store64(volatile SbAtomic64* ptr,
+                                               SbAtomic64 value) {
+  *ptr = value;
+  SbAtomicMemoryBarrier();
+}
+
+SB_C_FORCE_INLINE void SbAtomicRelease_Store64(volatile SbAtomic64* ptr,
+                                               SbAtomic64 value) {
+  SbAtomicMemoryBarrier();
+  *ptr = value;
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicNoBarrier_Load64(volatile const SbAtomic64* ptr) {
+  return *ptr;
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicAcquire_Load64(volatile const SbAtomic64* ptr) {
+  SbAtomic64 value = *ptr;
+  SbAtomicMemoryBarrier();
+  return value;
+}
+
+SB_C_FORCE_INLINE SbAtomic64
+SbAtomicRelease_Load64(volatile const SbAtomic64* ptr) {
+  SbAtomicMemoryBarrier();
+  return *ptr;
+}
+#endif  // SB_HAS(64_BIT_ATOMICS)
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // STARBOARD_SHARED_WIN32_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/set_non_blocking_internal.cc
similarity index 62%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/set_non_blocking_internal.cc
index fff4bd9..35d74aa 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/set_non_blocking_internal.cc
@@ -12,10 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/shared/win32/set_non_blocking_internal.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include <winsock2.h>
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+bool SetNonBlocking(SOCKET socket_handle) {
+  u_long kOne = 1;
+  bool success = (ioctlsocket(socket_handle, FIONBIO, &kOne) == 0);
+  return success;
 }
+
+}  // namespace win32
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/win32/set_non_blocking_internal.h b/src/starboard/shared/win32/set_non_blocking_internal.h
new file mode 100644
index 0000000..745a14b
--- /dev/null
+++ b/src/starboard/shared/win32/set_non_blocking_internal.h
@@ -0,0 +1,33 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_WIN32_SET_NON_BLOCKING_INTERNAL_H_
+#define STARBOARD_SHARED_WIN32_SET_NON_BLOCKING_INTERNAL_H_
+
+#include "starboard/shared/internal_only.h"
+
+#include <winsock2.h>
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// Makes the socket file descriptor non-blocking.
+bool SetNonBlocking(SOCKET socket_handle);
+
+}  // namespace win32
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_WIN32_SET_NON_BLOCKING_INTERNAL_H_
diff --git a/src/starboard/shared/win32/socket_accept.cc b/src/starboard/shared/win32/socket_accept.cc
new file mode 100644
index 0000000..08be946
--- /dev/null
+++ b/src/starboard/shared/win32/socket_accept.cc
@@ -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.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/set_non_blocking_internal.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+SbSocket SbSocketAccept(SbSocket socket) {
+  if (!SbSocketIsValid(socket)) {
+    return kSbSocketInvalid;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+
+  SOCKET socket_handle = accept(socket->socket_handle, nullptr, nullptr);
+  if (socket_handle == INVALID_SOCKET) {
+    socket->error = sbwin32::TranslateSocketErrorStatus(WSAGetLastError());
+    return kSbSocketInvalid;
+  }
+
+  // All Starboard sockets are non-blocking, so let's ensure it.
+  if (!sbwin32::SetNonBlocking(socket_handle)) {
+    // Something went wrong, we'll clean up and return failure.
+    socket->error = sbwin32::TranslateSocketErrorStatus(WSAGetLastError());
+    closesocket(socket_handle);
+    return kSbSocketInvalid;
+  }
+
+  socket->error = kSbSocketOk;
+
+  // Adopt the newly accepted socket.
+  return new SbSocketPrivate(socket->address_type, socket->protocol,
+                             socket_handle);
+}
diff --git a/src/starboard/shared/win32/socket_bind.cc b/src/starboard/shared/win32/socket_bind.cc
new file mode 100644
index 0000000..9bc2afe
--- /dev/null
+++ b/src/starboard/shared/win32/socket_bind.cc
@@ -0,0 +1,76 @@
+// 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/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace {
+
+bool IsIpv6InaddrAny(const SbSocketAddress* local_address) {
+  return SbMemoryIsZero(local_address->address, sbwin32::kAddressLengthIpv6);
+}
+
+}  // namespace
+
+SbSocketError SbSocketBind(SbSocket socket,
+                           const SbSocketAddress* local_address) {
+  if (!SbSocketIsValid(socket)) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid socket";
+    return kSbSocketErrorFailed;
+  }
+
+  sbwin32::SockAddr sock_addr;
+  if (!sock_addr.FromSbSocketAddress(local_address)) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid address";
+    return (socket->error = sbwin32::TranslateSocketErrorStatus(EINVAL));
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  if (local_address->type != socket->address_type) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": Incompatible addresses: "
+                   << "socket type = " << socket->address_type
+                   << ", argument type = " << local_address->type;
+    return (socket->error = sbwin32::TranslateSocketErrorStatus(EAFNOSUPPORT));
+  }
+
+  // When binding to the IPV6 any address, ensure that the IPV6_V6ONLY flag is
+  // off to allow incoming IPV4 connections on the same socket.
+  // See https://www.ietf.org/rfc/rfc3493.txt for details.
+  if (local_address && (local_address->type == kSbSocketAddressTypeIpv6) &&
+      IsIpv6InaddrAny(local_address)) {
+    if (!sbwin32::SetBooleanSocketOption(socket, IPPROTO_IPV6, IPV6_V6ONLY,
+                                         "IPV6_V6ONLY", false)) {
+      // Silently ignore errors, assume the default behavior is as expected.
+      socket->error = kSbSocketOk;
+    }
+  }
+
+  int result =
+      bind(socket->socket_handle, sock_addr.sockaddr(), sock_addr.length);
+  if (result == SOCKET_ERROR) {
+    int last_error = WSAGetLastError();
+    SB_DLOG(ERROR) << __FUNCTION__
+                   << ": Bind failed. last_error=" << last_error;
+    return (socket->error = sbwin32::TranslateSocketErrorStatus(last_error));
+  }
+
+  return (socket->error = kSbSocketOk);
+}
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_clear_last_error.cc
similarity index 72%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_clear_last_error.cc
index fff4bd9..3c28d9b 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_clear_last_error.cc
@@ -12,10 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include "starboard/shared/win32/socket_internal.h"
+
+bool SbSocketClearLastError(SbSocket socket) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  socket->error = kSbSocketOk;
+  return true;
 }
diff --git a/src/starboard/shared/win32/socket_connect.cc b/src/starboard/shared/win32/socket_connect.cc
new file mode 100644
index 0000000..4287d48
--- /dev/null
+++ b/src/starboard/shared/win32/socket_connect.cc
@@ -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.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+SbSocketError SbSocketConnect(SbSocket socket, const SbSocketAddress* address) {
+  if (!SbSocketIsValid(socket)) {
+    return kSbSocketErrorFailed;
+  }
+
+  sbwin32::SockAddr sock_addr;
+  if (!sock_addr.FromSbSocketAddress(address)) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid address";
+    return (socket->error = kSbSocketErrorFailed);
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  if (address->type != socket->address_type) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": Incompatible addresses: "
+                   << "socket type = " << socket->address_type
+                   << ", argument type = " << address->type;
+    return (socket->error = kSbSocketErrorFailed);
+  }
+
+  int result =
+      connect(socket->socket_handle, sock_addr.sockaddr(), sock_addr.length);
+
+  if (result != SOCKET_ERROR) {
+    return (socket->error = kSbSocketOk);
+  }
+
+  const int last_error = WSAGetLastError();
+  if (last_error == WSAEWOULDBLOCK) {
+    return (socket->error = kSbSocketPending);
+  }
+
+  SB_DLOG(ERROR) << __FUNCTION__ << ": connect failed: " << last_error;
+  return (socket->error = kSbSocketErrorFailed);
+}
diff --git a/src/starboard/shared/win32/socket_create.cc b/src/starboard/shared/win32/socket_create.cc
new file mode 100644
index 0000000..bc3a89c
--- /dev/null
+++ b/src/starboard/shared/win32/socket_create.cc
@@ -0,0 +1,118 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+#include <mswsock.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/set_non_blocking_internal.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+SbSocket SbSocketCreate(SbSocketAddressType address_type,
+                        SbSocketProtocol protocol) {
+  int socket_domain;
+  switch (address_type) {
+    case kSbSocketAddressTypeIpv4:
+      socket_domain = AF_INET;
+      break;
+    case kSbSocketAddressTypeIpv6:
+      socket_domain = AF_INET6;
+      break;
+    default:
+      SB_NOTREACHED();
+      return kSbSocketInvalid;
+  }
+
+  int socket_type;
+  int socket_protocol;
+  switch (protocol) {
+    case kSbSocketProtocolTcp:
+      socket_type = SOCK_STREAM;
+      socket_protocol = IPPROTO_TCP;
+      break;
+    case kSbSocketProtocolUdp:
+      socket_type = SOCK_DGRAM;
+      socket_protocol = IPPROTO_UDP;
+      break;
+    default:
+      SB_NOTREACHED();
+      return kSbSocketInvalid;
+  }
+
+  // WSASocket with dwFlags=0, instead of socket() creates sockets that do not
+  // support overlapped IO.
+  SOCKET socket_handle =
+      WSASocketW(socket_domain, socket_type, socket_protocol, nullptr, 0, 0);
+  if (socket_handle == INVALID_SOCKET) {
+    return kSbSocketInvalid;
+  }
+
+  // From
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/cc136103(v=vs.85).aspx:
+  // "When the TargetOsVersion member is set to a value for Windows Vista or
+  // later, reductions to the TCP receive buffer size on this socket using the
+  // SO_RCVBUF socket option are allowed even after a TCP connection has been
+  // establishment."
+  // "When the TargetOsVersion member is set to a value for Windows Vista or
+  // later, receive window auto-tuning is enabled and the TCP window scale
+  // factor is reduced to 2 from the default value of 8."
+
+  // The main impetus for this change:
+
+  // "The WsaBehaviorAutoTuning option is needed on Windows Vista for some
+  // Internet gateway devices and firewalls that do not correctly support data
+  // flows for TCP connections that use the WSopt extension and a windows scale
+  // factor. On Windows Vista, a receiver by default negotiates a window scale
+  // factor of 8 for a maximum true window size of 16,776,960 bytes. When data
+  // begins to flow on a fast link, Windows initially starts with a 64 Kilobyte
+  // true window size by setting the Window field of the TCP header to 256 and
+  // setting the window scale factor to 8 in the TCP options (256*2^8=64KB).
+  // Some Internet gateway devices and firewalls ignore the window scale factor
+  // and only look at the advertised Window field in the TCP header specified as
+  // 256, and drop incoming packets for the connection that contain more than
+  // 256 bytes of TCP data. To support TCP receive window scaling, a gateway
+  // device or firewall must monitor the TCP handshake and track the negotiated
+  // window scale factor as part of the TCP connection data. Also some
+  // applications and TCP stack implementations on other platforms ignore the
+  // TCP WSopt extension and the window scaling factor. So the remote host
+  // sending the data may send data at the rate advertised in the Window field
+  // of the TCP header (256 bytes). This can result in data being received very
+  // slowly by the receiver."
+
+  if (protocol == kSbSocketProtocolTcp) {
+    WSA_COMPATIBILITY_MODE compatibility_mode = {WsaBehaviorAll, NTDDI_VISTA};
+
+    DWORD kZero = 0;
+    int return_value = WSAIoctl(
+        socket_handle, SIO_SET_COMPATIBILITY_MODE, &compatibility_mode,
+        sizeof(WSA_COMPATIBILITY_MODE), nullptr, 0, &kZero, nullptr, nullptr);
+    if (return_value == SOCKET_ERROR) {
+      closesocket(socket_handle);
+      return kSbSocketInvalid;
+    }
+  }
+
+  // All Starboard sockets are non-blocking, so let's ensure it.
+  if (!sbwin32::SetNonBlocking(socket_handle)) {
+    // Something went wrong, we'll clean up and return failure.
+    closesocket(socket_handle);
+    return kSbSocketInvalid;
+  }
+
+  return new SbSocketPrivate(address_type, protocol, socket_handle);
+}
diff --git a/src/starboard/shared/win32/socket_destroy.cc b/src/starboard/shared/win32/socket_destroy.cc
new file mode 100644
index 0000000..df47cae
--- /dev/null
+++ b/src/starboard/shared/win32/socket_destroy.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/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+#include "starboard/socket_waiter.h"
+
+bool SbSocketDestroy(SbSocket socket) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+
+  if (socket->waiter != nullptr) {
+    bool result = SbSocketWaiterRemove(socket->waiter, socket);
+    SB_DCHECK(result);
+  }
+
+  bool result = closesocket(socket->socket_handle) != SOCKET_ERROR;
+
+  delete socket;
+  return result;
+}
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_free_resolution.cc
similarity index 70%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_free_resolution.cc
index fff4bd9..212cde0 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_free_resolution.cc
@@ -12,10 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include "starboard/log.h"
+
+void SbSocketFreeResolution(SbSocketResolution* resolution) {
+  if (!resolution) {
+    return;
+  }
+
+  if (resolution->addresses) {
+    delete[] resolution->addresses;
+  }
+
+  delete resolution;
 }
diff --git a/src/starboard/shared/win32/socket_get_interface_address.cc b/src/starboard/shared/win32/socket_get_interface_address.cc
new file mode 100644
index 0000000..a8f8b04
--- /dev/null
+++ b/src/starboard/shared/win32/socket_get_interface_address.cc
@@ -0,0 +1,342 @@
+// 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/socket.h"
+
+#include <winsock2.h>
+
+#include <ifdef.h>
+#include <iphlpapi.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "starboard/byte_swap.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace {
+const ULONG kDefaultAdapterInfoBufferSizeInBytes = 16 * 1024;
+
+bool IsAnyAddress(const SbSocketAddress& address) {
+  switch (address.type) {
+    case kSbSocketAddressTypeIpv4:
+      return (address.address[0] == 0 && address.address[1] == 0 &&
+              address.address[2] == 0 && address.address[3] == 0);
+    case kSbSocketAddressTypeIpv6: {
+      bool found_nonzero = false;
+      for (std::size_t i = 0; i != sbwin32::kAddressLengthIpv6; ++i) {
+        found_nonzero |= (address.address[i] != 0);
+      }
+      return !found_nonzero;
+    }
+    default:
+      SB_NOTREACHED() << "Invalid address type " << address.type;
+      break;
+  }
+
+  return false;
+}
+
+void GenerateNetMaskFromPrefixLength(UINT8 prefix_length,
+                                     UINT32* const address_begin,
+                                     UINT32* const address_end) {
+  SB_DCHECK(address_end >= address_begin);
+  SB_DCHECK((reinterpret_cast<char*>(address_end) -
+             reinterpret_cast<char*>(address_begin)) %
+                4 ==
+            0);
+  UINT8 ones_left = prefix_length;
+  const int kBitsInOneDWORD = sizeof(UINT32) * 8;
+  for (UINT32* iterator = address_begin; iterator != address_end; ++iterator) {
+    UINT8 ones_in_this_dword = std::min<UINT8>(kBitsInOneDWORD, ones_left);
+    UINT64 mask_value =
+        kSbUInt64Max - ((1ULL << (kBitsInOneDWORD - ones_in_this_dword)) - 1);
+    *iterator =
+        SB_HOST_TO_NET_U32(static_cast<UINT32>(mask_value & kSbUInt64Max));
+    ones_left -= ones_in_this_dword;
+  }
+}
+
+bool PopulateInterfaceAddress(const IP_ADAPTER_UNICAST_ADDRESS& unicast_address,
+                              SbSocketAddress* out_interface_ip) {
+  if (!out_interface_ip) {
+    return true;
+  }
+
+  const SOCKET_ADDRESS& address = unicast_address.Address;
+  sbwin32::SockAddr addr;
+  return addr.FromSockaddr(address.lpSockaddr) &&
+         addr.ToSbSocketAddress(out_interface_ip);
+}
+
+bool PopulateNetmask(const IP_ADAPTER_UNICAST_ADDRESS& unicast_address,
+                     SbSocketAddress* out_netmask) {
+  if (!out_netmask) {
+    return true;
+  }
+
+  const SOCKET_ADDRESS& address = unicast_address.Address;
+  if (address.lpSockaddr == nullptr) {
+    return false;
+  }
+  const ADDRESS_FAMILY& family = address.lpSockaddr->sa_family;
+
+  switch (family) {
+    case AF_INET:
+      out_netmask->type = kSbSocketAddressTypeIpv4;
+      break;
+    case AF_INET6:
+      out_netmask->type = kSbSocketAddressTypeIpv6;
+      break;
+    default:
+      SB_NOTREACHED() << "Invalid family " << family;
+      return false;
+  }
+
+  UINT32* const begin_netmask =
+      reinterpret_cast<UINT32*>(&(out_netmask->address[0]));
+  UINT32* const end_netmask =
+      begin_netmask + SB_ARRAY_SIZE(out_netmask->address) / sizeof(UINT32);
+
+  GenerateNetMaskFromPrefixLength(unicast_address.OnLinkPrefixLength,
+                                  begin_netmask, end_netmask);
+  return true;
+}
+
+bool GetAdapters(const SbSocketAddressType address_type,
+                 std::unique_ptr<char[]>* adapter_info) {
+  SB_DCHECK(adapter_info);
+
+  ULONG family = 0;
+  int address_length_bytes = 0;
+
+  switch (address_type) {
+    case kSbSocketAddressTypeIpv4:
+      family = AF_INET;
+      address_length_bytes = sbwin32::kAddressLengthIpv4;
+      break;
+    case kSbSocketAddressTypeIpv6:
+      family = AF_INET6;
+      address_length_bytes = sbwin32::kAddressLengthIpv6;
+      break;
+    default:
+      SB_NOTREACHED() << "Invalid address type.";
+      return false;
+  }
+
+  ULONG adapter_addresses_number_bytes = kDefaultAdapterInfoBufferSizeInBytes;
+
+  for (int try_count = 0; try_count != 2; ++try_count) {
+    // Using auto for return value here, since different versions of windows use
+    // slightly different datatypes.  These differences do not matter to us, but
+    // the compiler might warn on them.
+    adapter_info->reset(new char[adapter_addresses_number_bytes]);
+    PIP_ADAPTER_ADDRESSES adapter_addresses =
+        reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info->get());
+
+    // Note: If |GetAdapterAddresses| deems that buffer supplied is not enough,
+    // it will return the recommended number of bytes in
+    // |adapter_addresses_number_bytes|.
+    auto retval = GetAdaptersAddresses(
+        family, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER, nullptr,
+        adapter_addresses, &adapter_addresses_number_bytes);
+
+    if (retval == ERROR_SUCCESS) {
+      return true;
+    }
+    if (retval != ERROR_BUFFER_OVERFLOW) {
+      // Only retry with more memory if the error says so.
+      break;
+    }
+    SB_LOG(ERROR) << "GetAdapterAddresses() failed with error code " << retval;
+  }
+
+  return false;
+}
+
+bool GetNetmaskForInterfaceAddress(const SbSocketAddress& interface_address,
+                                   SbSocketAddress* out_netmask) {
+  std::unique_ptr<char[]> adapter_info_memory_block;
+  if (!GetAdapters(interface_address.type, &adapter_info_memory_block)) {
+    return false;
+  }
+  const void* const interface_address_buffer =
+      reinterpret_cast<const void* const>(interface_address.address);
+  for (PIP_ADAPTER_ADDRESSES adapter = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(
+           adapter_info_memory_block.get());
+       adapter != nullptr; adapter = adapter->Next) {
+    if ((adapter->OperStatus != IfOperStatusUp) ||
+        (adapter->IfType != IF_TYPE_ETHERNET_CSMACD)) {
+      // Note: IfType == IF_TYPE_SOFTWARE_LOOPBACK is also skipped.
+      continue;
+    }
+
+    for (PIP_ADAPTER_UNICAST_ADDRESS unicast_address =
+             adapter->FirstUnicastAddress;
+         unicast_address != nullptr; unicast_address = unicast_address->Next) {
+      sbwin32::SockAddr addr;
+      if (!addr.FromSockaddr(unicast_address->Address.lpSockaddr)) {
+        continue;
+      }
+
+      const void* unicast_address_buffer = nullptr;
+      int bytes_to_check = 0;
+
+      switch (interface_address.type) {
+        case kSbSocketAddressTypeIpv4:
+          unicast_address_buffer =
+              reinterpret_cast<void*>(&(addr.sockaddr_in()->sin_addr));
+          bytes_to_check = sbwin32::kAddressLengthIpv4;
+          break;
+        case kSbSocketAddressTypeIpv6:
+          unicast_address_buffer =
+              reinterpret_cast<void*>(&(addr.sockaddr_in6()->sin6_addr));
+          bytes_to_check = sbwin32::kAddressLengthIpv6;
+          break;
+        default:
+          SB_DLOG(ERROR) << "Invalid interface address type "
+                         << interface_address.type;
+          return false;
+      }
+
+      if (SbMemoryCompare(unicast_address_buffer, interface_address_buffer,
+                          bytes_to_check) != 0) {
+        continue;
+      }
+
+      if (PopulateNetmask(*unicast_address, out_netmask)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+bool IsUniqueLocalAddress(const unsigned char ip[16]) {
+  // Unique Local Addresses are in fd08::/8.
+  return ip[0] == 0xfd && ip[1] == 0x08;
+}
+
+bool FindInterfaceIP(const SbSocketAddressType address_type,
+                     SbSocketAddress* out_interface_ip,
+                     SbSocketAddress* out_netmask) {
+  if (out_interface_ip == nullptr) {
+    SB_NOTREACHED() << "out_interface_ip must be specified";
+    return false;
+  }
+
+  std::unique_ptr<char[]> adapter_info_memory_block;
+  if (!GetAdapters(address_type, &adapter_info_memory_block)) {
+    return false;
+  }
+
+  for (PIP_ADAPTER_ADDRESSES adapter = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(
+           adapter_info_memory_block.get());
+       adapter != nullptr; adapter = adapter->Next) {
+    if ((adapter->OperStatus != IfOperStatusUp) ||
+        (adapter->IfType != IF_TYPE_ETHERNET_CSMACD)) {
+      // Note: IfType == IF_TYPE_SOFTWARE_LOOPBACK is also skipped.
+      continue;
+    }
+
+    for (PIP_ADAPTER_UNICAST_ADDRESS unicast_address =
+             adapter->FirstUnicastAddress;
+         unicast_address != nullptr; unicast_address = unicast_address->Next) {
+      if (unicast_address->Flags & (IP_ADAPTER_ADDRESS_TRANSIENT)) {
+        continue;
+      }
+      if (!(unicast_address->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE)) {
+        continue;
+      }
+
+      // TODO: For IPv6, Prioritize interface with highest scope.
+      // Skip ULAs for now.
+      if (address_type == kSbSocketAddressTypeIpv6) {
+        // Documentation on MSDN states:
+        // "The SOCKADDR structure pointed to by the lpSockaddr member varies
+        // depending on the protocol or address family selected. For example,
+        // the sockaddr_in6 structure is used for an IPv6 socket address
+        // while the sockaddr_in4 structure is used for an IPv4 socket address."
+        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740507(v=vs.85).aspx
+
+        sockaddr_in6* addr = reinterpret_cast<sockaddr_in6*>(
+            unicast_address->Address.lpSockaddr);
+        SB_DCHECK(addr->sin6_family == AF_INET6);
+        if (IsUniqueLocalAddress(addr->sin6_addr.u.Byte)) {
+          continue;
+        }
+      }
+
+      if (!PopulateInterfaceAddress(*unicast_address, out_interface_ip)) {
+        continue;
+      }
+      if (!PopulateNetmask(*unicast_address, out_netmask)) {
+        continue;
+      }
+
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindSourceAddressForDestination(const SbSocketAddress& destination,
+                                     SbSocketAddress* out_source_address) {
+  SbSocket socket = SbSocketCreate(destination.type, kSbSocketProtocolUdp);
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SbSocketError connect_retval = SbSocketConnect(socket, &destination);
+  if (connect_retval != kSbSocketOk) {
+    bool socket_destroyed = SbSocketDestroy(socket);
+    SB_DCHECK(socket_destroyed);
+    return false;
+  }
+
+  bool success = SbSocketGetLocalAddress(socket, out_source_address);
+  bool socket_destroyed = SbSocketDestroy(socket);
+  SB_DCHECK(socket_destroyed);
+  return success;
+}
+
+}  // namespace
+
+bool SbSocketGetInterfaceAddress(const SbSocketAddress* const destination,
+                                 SbSocketAddress* out_source_address,
+                                 SbSocketAddress* out_netmask) {
+  if (!out_source_address) {
+    return false;
+  }
+
+  if (destination == nullptr) {
+    // Return either a v4 or a v6 address.  Per spec.
+    return (FindInterfaceIP(kSbSocketAddressTypeIpv4, out_source_address,
+                            out_netmask) ||
+            FindInterfaceIP(kSbSocketAddressTypeIpv6, out_source_address,
+                            out_netmask));
+  } else if (IsAnyAddress(*destination)) {
+    return FindInterfaceIP(destination->type, out_source_address, out_netmask);
+  } else {
+    return (FindSourceAddressForDestination(*destination, out_source_address) &&
+            GetNetmaskForInterfaceAddress(*out_source_address, out_netmask));
+  }
+
+  return false;
+}
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_get_last_error.cc
similarity index 72%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_get_last_error.cc
index fff4bd9..51d2e9a 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_get_last_error.cc
@@ -12,10 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include "starboard/shared/win32/socket_internal.h"
+
+SbSocketError SbSocketGetLastError(SbSocket socket) {
+  if (!SbSocketIsValid(socket)) {
+    return kSbSocketErrorFailed;
+  }
+
+  return socket->error;
 }
diff --git a/src/starboard/shared/win32/socket_get_local_address.cc b/src/starboard/shared/win32/socket_get_local_address.cc
new file mode 100644
index 0000000..d49d5b0
--- /dev/null
+++ b/src/starboard/shared/win32/socket_get_local_address.cc
@@ -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.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketGetLocalAddress(SbSocket socket, SbSocketAddress* out_address) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  sbwin32::SockAddr sock_addr;
+  int result = getsockname(socket->socket_handle, sock_addr.sockaddr(),
+                           &sock_addr.length);
+  if (result == SOCKET_ERROR) {
+    int last_error = WSAGetLastError();
+    SB_LOG(ERROR) << "getsockname() failed with last_error = " << last_error;
+    socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
+    return false;
+  }
+  if (!sock_addr.ToSbSocketAddress(out_address)) {
+    socket->error = kSbSocketErrorFailed;
+    return false;
+  }
+
+  socket->error = kSbSocketOk;
+  return true;
+}
diff --git a/src/starboard/shared/win32/socket_internal.cc b/src/starboard/shared/win32/socket_internal.cc
new file mode 100644
index 0000000..c81df3e
--- /dev/null
+++ b/src/starboard/shared/win32/socket_internal.cc
@@ -0,0 +1,193 @@
+// 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/win32/socket_internal.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+namespace {
+const socklen_t kAddressStructLengthIpv4 =
+    static_cast<socklen_t>(sizeof(struct sockaddr_in));
+const socklen_t kAddressStructLengthIpv6 =
+    static_cast<socklen_t>(sizeof(struct sockaddr_in6));
+}
+
+SbSocketError TranslateSocketErrorStatus(int error) {
+  switch (error) {
+    case 0:
+      return kSbSocketOk;
+    case WSAEINPROGRESS:
+    case WSAEWOULDBLOCK:
+      return kSbSocketPending;
+  }
+
+  // Here's where we would be more nuanced if we need to be.
+  return kSbSocketErrorFailed;
+}
+
+bool SetBooleanSocketOption(SbSocket socket,
+                            int level,
+                            int option_code,
+                            const char* option_name,
+                            bool value) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  const int on = value ? 1 : 0;
+  int result = setsockopt(socket->socket_handle, level, option_code,
+                          reinterpret_cast<const char*>(&on), sizeof(on));
+  if (result == SOCKET_ERROR) {
+    int last_error = WSAGetLastError();
+    SB_DLOG(ERROR) << "Failed to set " << option_name << " on socket "
+                   << socket->socket_handle << ", last_error = " << last_error;
+    socket->error = TranslateSocketErrorStatus(last_error);
+    return false;
+  }
+
+  socket->error = kSbSocketOk;
+  return true;
+}
+
+bool SetIntegerSocketOption(SbSocket socket,
+                            int level,
+                            int option_code,
+                            const char* option_name,
+                            int value) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  int result = setsockopt(socket->socket_handle, level, option_code,
+                          reinterpret_cast<const char*>(&value), sizeof(value));
+  if (result == SOCKET_ERROR) {
+    int last_error = WSAGetLastError();
+    SB_DLOG(ERROR) << "Failed to set " << option_name << " on socket "
+                   << socket->socket_handle << ", last_error = " << last_error;
+    socket->error = TranslateSocketErrorStatus(last_error);
+    return false;
+  }
+
+  socket->error = kSbSocketOk;
+  return true;
+}
+
+bool SockAddr::FromSbSocketAddress(const SbSocketAddress* address) {
+  if (!address) {
+    return false;
+  }
+
+  length = sizeof(storage_);
+  switch (address->type) {
+    case kSbSocketAddressTypeIpv4: {
+      struct sockaddr_in* addr = sockaddr_in();
+      length = kAddressStructLengthIpv4;
+      SbMemorySet(addr, 0, length);
+      addr->sin_family = AF_INET;
+      addr->sin_port = htons(address->port);
+      SbMemoryCopy(&addr->sin_addr, address->address, kAddressLengthIpv4);
+      break;
+    }
+    case kSbSocketAddressTypeIpv6: {
+      struct sockaddr_in6* addr6 = sockaddr_in6();
+      length = kAddressStructLengthIpv6;
+      SbMemorySet(addr6, 0, length);
+      addr6->sin6_family = AF_INET6;
+      addr6->sin6_port = htons(address->port);
+      SbMemoryCopy(&addr6->sin6_addr, address->address, kAddressLengthIpv6);
+      break;
+    }
+    default:
+      SB_NOTREACHED() << "Unrecognized address type: " << address->type;
+      return false;
+  }
+
+  return true;
+}
+
+bool SockAddr::ToSbSocketAddress(SbSocketAddress* out_address) const {
+  if (!out_address) {
+    return false;
+  }
+
+// Check that we have been properly initialized.
+  SB_DCHECK(length == kAddressStructLengthIpv4 ||
+            length == kAddressStructLengthIpv6);
+
+  if (family() == AF_INET) {
+    const struct sockaddr_in* addr = sockaddr_in();
+    if (length < kAddressStructLengthIpv4) {
+      SB_NOTREACHED() << "Insufficient INET size: " << length;
+      return false;
+    }
+
+    SbMemoryCopy(out_address->address, &addr->sin_addr, kAddressLengthIpv4);
+    out_address->port = ntohs(addr->sin_port);
+    out_address->type = kSbSocketAddressTypeIpv4;
+    return true;
+  }
+
+  if (family() == AF_INET6) {
+    const struct sockaddr_in6* addr6 = sockaddr_in6();
+    if (length < kAddressStructLengthIpv6) {
+      SB_NOTREACHED() << "Insufficient INET6 size: " << length;
+      return false;
+    }
+
+    SbMemoryCopy(out_address->address, &addr6->sin6_addr, kAddressLengthIpv6);
+    out_address->port = ntohs(addr6->sin6_port);
+    out_address->type = kSbSocketAddressTypeIpv6;
+    return true;
+  }
+
+  SB_NOTREACHED() << "Unrecognized address family: " << family();
+  return false;
+}
+
+bool SockAddr::FromSockaddr(const struct sockaddr* sock_addr) {
+  if (!sock_addr) {
+    return false;
+  }
+
+  int family = sock_addr->sa_family;
+  if (family == AF_INET) {
+    const struct sockaddr_in* addr =
+        reinterpret_cast<const struct sockaddr_in*>(sock_addr);
+    *sockaddr_in() = *addr;
+    length = static_cast<socklen_t>(sizeof(*addr));
+    return true;
+  } else if (family == AF_INET6) {
+    const struct sockaddr_in6* addr =
+        reinterpret_cast<const struct sockaddr_in6*>(sock_addr);
+    *sockaddr_in6() = *addr;
+    length = static_cast<socklen_t>(sizeof(*addr));
+    return true;
+  }
+
+  SB_DLOG(WARNING) << "Unrecognized address family: " << family;
+  return false;
+}
+
+}  // namespace win32
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/win32/socket_internal.h b/src/starboard/shared/win32/socket_internal.h
new file mode 100644
index 0000000..3a92e18
--- /dev/null
+++ b/src/starboard/shared/win32/socket_internal.h
@@ -0,0 +1,134 @@
+// 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_WIN32_SOCKET_INTERNAL_H_
+#define STARBOARD_SHARED_WIN32_SOCKET_INTERNAL_H_
+
+#include <winsock2.h>
+#include <WS2tcpip.h>
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/socket.h"
+#include "starboard/socket_waiter.h"
+#include "starboard/types.h"
+
+struct SbSocketPrivate {
+  SbSocketPrivate(SbSocketAddressType address_type,
+                  SbSocketProtocol protocol,
+                  SOCKET fd)
+      : address_type(address_type),
+        protocol(protocol),
+        socket_handle(fd),
+        error(kSbSocketOk),
+        waiter(kSbSocketWaiterInvalid) {}
+  ~SbSocketPrivate() {}
+
+  // The address domain of this socket, IPv4 or IPv6.
+  SbSocketAddressType address_type;
+
+  // The protocol of this socket, UDP or TCP.
+  SbSocketProtocol protocol;
+
+  // The file descriptor for this socket.
+  SOCKET socket_handle;
+
+  // The last error that occurred on this socket, or kSbSocketOk.
+  SbSocketError error;
+
+  // The waiter this socket is registered with, or kSbSocketWaiterInvalid.
+  SbSocketWaiter waiter;
+};
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+const socklen_t kAddressLengthIpv4 = 4;
+const socklen_t kAddressLengthIpv6 = 16;
+
+// Translates an last_error from a socket call into an SbSocketError.
+SbSocketError TranslateSocketErrorStatus(int error);
+
+// Sets a boolean socket option, doing all appropriate checks.
+bool SetBooleanSocketOption(SbSocket socket,
+                            int level,
+                            int option_code,
+                            const char* option_name,
+                            bool value);
+
+// Sets an integer socket option, doing all appropriate checks.
+bool SetIntegerSocketOption(SbSocket socket,
+                            int level,
+                            int option_code,
+                            const char* option_name,
+                            int value);
+
+// A helper class for converting back and forth from sockaddrs, ugh.
+class SockAddr {
+ public:
+  SockAddr() : length(sizeof(storage_)) {}
+  ~SockAddr() {}
+
+  // Initializes this SockAddr with the given SbSocketAddress, overwriting
+  // anything any address previously held.
+  bool FromSbSocketAddress(const SbSocketAddress* address);
+
+  // Initializes the given SbSocketAddress with this SockAddr, which must have
+  // been previously initialized.
+  bool ToSbSocketAddress(SbSocketAddress* out_address) const;
+
+  // Initializes this SockAddr with |sock_addr|, assuming it is appropriately
+  // sized for its type.
+  bool FromSockaddr(const struct sockaddr* sock_addr);
+
+  // The sockaddr family. We only support INET and INET6.
+  u_short family() const { return sockaddr()->sa_family; }
+
+  struct sockaddr* sockaddr() {
+    return reinterpret_cast<struct sockaddr*>(&storage_);
+  }
+
+  const struct sockaddr* sockaddr() const {
+    return reinterpret_cast<const struct sockaddr*>(&storage_);
+  }
+
+  struct sockaddr_in* sockaddr_in() {
+    return reinterpret_cast<struct sockaddr_in*>(&storage_);
+  }
+
+  const struct sockaddr_in* sockaddr_in() const {
+    return reinterpret_cast<const struct sockaddr_in*>(&storage_);
+  }
+
+  struct sockaddr_in6* sockaddr_in6() {
+    return reinterpret_cast<struct sockaddr_in6*>(&storage_);
+  }
+
+  const struct sockaddr_in6* sockaddr_in6() const {
+    return reinterpret_cast<const struct sockaddr_in6*>(&storage_);
+  }
+
+  // Public on purpose, because it will be handy to be passed directly by
+  // reference into other functions.
+  socklen_t length;
+
+ private:
+  sockaddr_storage storage_;
+};
+
+}  // namespace win32
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_WIN32_SOCKET_INTERNAL_H_
diff --git a/src/starboard/shared/win32/socket_is_connected.cc b/src/starboard/shared/win32/socket_is_connected.cc
new file mode 100644
index 0000000..76e982d
--- /dev/null
+++ b/src/starboard/shared/win32/socket_is_connected.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 "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+bool SbSocketIsConnected(SbSocket socket) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+
+  // To tell if it is really connected, we peek a byte from the stream. We
+  // should get a byte back, or an EAGAIN/EWOULDBLOCK telling us the socket is
+  // waiting for data.
+  char c;
+  const int result = recv(socket->socket_handle, &c, 1, MSG_PEEK);
+  if (result == 0) {
+    // If the connection is closed, the return value is 0.
+    return false;
+  }
+
+  const int last_error = WSAGetLastError();
+  return (result > 0 || last_error == WSAEWOULDBLOCK);
+}
diff --git a/src/starboard/shared/win32/socket_is_connected_and_idle.cc b/src/starboard/shared/win32/socket_is_connected_and_idle.cc
new file mode 100644
index 0000000..d66c508
--- /dev/null
+++ b/src/starboard/shared/win32/socket_is_connected_and_idle.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+bool SbSocketIsConnectedAndIdle(SbSocket socket) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+
+  // To tell if it is really connected and idle, we peek a byte from the
+  // stream. We should get an EAGAIN/EWOULDBLOCK telling us the socket is
+  // waiting for data.
+  char c;
+  int rv = recv(socket->socket_handle, &c, 1, MSG_PEEK);
+  if (rv != SOCKET_ERROR) {
+    // Either not connected, or not idle.
+    return false;
+  }
+
+  return (WSAGetLastError() == WSAEWOULDBLOCK);
+}
diff --git a/src/starboard/shared/win32/socket_join_multicast_group.cc b/src/starboard/shared/win32/socket_join_multicast_group.cc
new file mode 100644
index 0000000..459fcbd
--- /dev/null
+++ b/src/starboard/shared/win32/socket_join_multicast_group.cc
@@ -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.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketJoinMulticastGroup(SbSocket socket,
+                                const SbSocketAddress* address) {
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  if (!address) {
+    return false;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  ip_mreq imreq = {0};
+  IN_ADDR addr = *reinterpret_cast<const IN_ADDR*>(address->address);
+  imreq.imr_multiaddr.s_addr = addr.S_un.S_addr;
+  imreq.imr_interface.s_addr = INADDR_ANY;
+
+  int result = setsockopt(socket->socket_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                          reinterpret_cast<const char*>(&imreq), sizeof(imreq));
+  if (result == SOCKET_ERROR) {
+    int last_error = WSAGetLastError();
+    SB_DLOG(ERROR) << "Failed to IP_ADD_MEMBERSHIP on socket "
+                   << socket->socket_handle << ", last_error = " << last_error;
+    socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
+    return false;
+  }
+
+  socket->error = kSbSocketOk;
+  return true;
+}
diff --git a/src/starboard/shared/win32/socket_listen.cc b/src/starboard/shared/win32/socket_listen.cc
new file mode 100644
index 0000000..fb8de43
--- /dev/null
+++ b/src/starboard/shared/win32/socket_listen.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/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+SbSocketError SbSocketListen(SbSocket socket) {
+  if (!SbSocketIsValid(socket)) {
+    return kSbSocketErrorFailed;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  // TODO: Determine if we need to specify a > 0 backlog. It can go up to
+  // SOMAXCONN according to the documentation. Several places in chromium
+  // specify the literal "10" with the comment "maybe dont allow any backlog?"
+  int result = listen(socket->socket_handle, 0);
+  if (result == SOCKET_ERROR) {
+    return (socket->error = sbwin32::TranslateSocketErrorStatus(result));
+  }
+
+  return (socket->error = kSbSocketOk);
+}
diff --git a/src/starboard/shared/win32/socket_receive_from.cc b/src/starboard/shared/win32/socket_receive_from.cc
new file mode 100644
index 0000000..e00f31f
--- /dev/null
+++ b/src/starboard/shared/win32/socket_receive_from.cc
@@ -0,0 +1,107 @@
+// 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/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace {
+
+bool IsReportableErrno(int code) {
+  return (code != WSAEWOULDBLOCK && code != WSAECONNRESET);
+}
+
+}  // namespace
+
+int SbSocketReceiveFrom(SbSocket socket,
+                        char* out_data,
+                        int data_size,
+                        SbSocketAddress* out_source) {
+  const int kRecvFlags = 0;
+
+  if (!SbSocketIsValid(socket)) {
+    return -1;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  if (socket->protocol == kSbSocketProtocolTcp) {
+    if (out_source) {
+      sbwin32::SockAddr sock_addr;
+      int result = getpeername(socket->socket_handle, sock_addr.sockaddr(),
+                               &sock_addr.length);
+      if (result == SOCKET_ERROR) {
+        int last_error = WSAGetLastError();
+        SB_DLOG(ERROR) << __FUNCTION__
+                       << ": getpeername failed, last_error = " << last_error;
+        socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
+        return -1;
+      }
+
+      if (!sock_addr.ToSbSocketAddress(out_source)) {
+        SB_DLOG(FATAL) << __FUNCTION__ << ": Bad TCP source address.";
+        socket->error = kSbSocketErrorFailed;
+        return -1;
+      }
+    }
+
+    int bytes_read =
+        recv(socket->socket_handle, out_data, data_size, kRecvFlags);
+    if (bytes_read >= 0) {
+      socket->error = kSbSocketOk;
+      return static_cast<int>(bytes_read);
+    }
+
+    int last_error = WSAGetLastError();
+    if (IsReportableErrno(last_error) &&
+        socket->error != sbwin32::TranslateSocketErrorStatus(last_error)) {
+      SB_DLOG(ERROR) << "recv failed, last_error = " << last_error;
+    }
+    socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
+    return -1;
+  } else if (socket->protocol == kSbSocketProtocolUdp) {
+    sbwin32::SockAddr sock_addr;
+    ssize_t bytes_read =
+        recvfrom(socket->socket_handle, out_data, data_size, kRecvFlags,
+                 sock_addr.sockaddr(), &sock_addr.length);
+
+    if (bytes_read >= 0) {
+      if (out_source) {
+        if (!sock_addr.ToSbSocketAddress(out_source)) {
+          SB_DLOG(FATAL) << __FUNCTION__ << ": Bad UDP source address.";
+          socket->error = kSbSocketErrorFailed;
+          return -1;
+        }
+      }
+
+      socket->error = kSbSocketOk;
+      return static_cast<int>(bytes_read);
+    }
+
+    int last_error = WSAGetLastError();
+    if (last_error != WSAEWOULDBLOCK &&
+        socket->error != sbwin32::TranslateSocketErrorStatus(last_error)) {
+      SB_DLOG(ERROR) << "recvfrom failed, last_error = " << last_error;
+    }
+    socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
+    return -1;
+  }
+
+  SB_NOTREACHED() << "Unrecognized protocol: " << socket->protocol;
+  return -1;
+}
diff --git a/src/starboard/shared/win32/socket_resolve.cc b/src/starboard/shared/win32/socket_resolve.cc
new file mode 100644
index 0000000..d2fef44
--- /dev/null
+++ b/src/starboard/shared/win32/socket_resolve.cc
@@ -0,0 +1,90 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include <memory>
+#include <vector>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+SbSocketResolution* SbSocketResolve(const char* hostname, int filters) {
+  struct addrinfo* ai = nullptr;
+  struct addrinfo hints = {0};
+
+  if (filters & kSbSocketResolveFilterIpv4) {
+    if (filters & kSbSocketResolveFilterIpv6) {
+      hints.ai_family = AF_UNSPEC;
+    } else {
+      hints.ai_family = AF_INET;
+    }
+  } else if (filters & kSbSocketResolveFilterIpv6) {
+    hints.ai_family = AF_INET6;
+  } else {
+    hints.ai_family = AF_UNSPEC;
+  }
+
+  hints.ai_flags = AI_ADDRCONFIG;
+  hints.ai_socktype = SOCK_STREAM;
+
+  // Actually make the call to get the data.
+  int err = getaddrinfo(hostname, nullptr, &hints, &ai);
+  if (err != 0 || ai == nullptr) {
+    SB_DLOG(ERROR) << "getaddrinfo failed.  last_error: " << WSAGetLastError();
+    return nullptr;
+  }
+
+  int address_count = 0;
+  for (const auto* i = ai; i != nullptr; i = i->ai_next) {
+    ++address_count;
+  }
+
+  SbSocketResolution* result = new SbSocketResolution();
+
+  // Translate all the sockaddrs.
+  std::vector<sbwin32::SockAddr> sock_addrs;
+  sock_addrs.resize(address_count);
+
+  std::vector<bool> parsed;
+  parsed.resize(address_count);
+
+  int index = 0;
+  int skip = 0;
+  for (const auto *i = ai; i != nullptr; i = i->ai_next, ++index) {
+    // Skip over any addresses we can't parse.
+    parsed[index] = sock_addrs[index].FromSockaddr(i->ai_addr);
+    if (!parsed[index]) {
+      ++skip;
+    }
+  }
+
+  result->address_count = address_count - skip;
+  result->addresses = new SbSocketAddress[result->address_count];
+
+  int result_index = 0;
+  for (int i = 0; i < address_count; ++i) {
+    if (parsed[i] &&
+        sock_addrs[i].ToSbSocketAddress(&result->addresses[result_index])) {
+      ++result_index;
+    }
+  }
+
+  freeaddrinfo(ai);
+  return result;
+}
diff --git a/src/starboard/shared/win32/socket_send_to.cc b/src/starboard/shared/win32/socket_send_to.cc
new file mode 100644
index 0000000..36cdd8b
--- /dev/null
+++ b/src/starboard/shared/win32/socket_send_to.cc
@@ -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.
+
+#include "starboard/socket.h"
+
+#include <winsock2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+int SbSocketSendTo(SbSocket socket,
+                   const char* data,
+                   int data_size,
+                   const SbSocketAddress* destination) {
+  const int kSendFlags = 0;
+  if (!SbSocketIsValid(socket)) {
+    return -1;
+  }
+
+  SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
+  if (socket->protocol == kSbSocketProtocolTcp) {
+    if (destination) {
+      SB_DLOG(FATAL) << "Destination passed to TCP send.";
+      socket->error = kSbSocketErrorFailed;
+      return -1;
+    }
+
+    int bytes_written =
+        send(socket->socket_handle, data, data_size, kSendFlags);
+    if (bytes_written >= 0) {
+      socket->error = kSbSocketOk;
+      return static_cast<int>(bytes_written);
+    }
+
+    int last_error = WSAGetLastError();
+    if (last_error != EWOULDBLOCK) {
+      SB_DLOG(ERROR) << "send failed, last_error = " << last_error;
+    }
+    socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
+    return -1;
+  } else if (socket->protocol == kSbSocketProtocolUdp) {
+    if (!destination) {
+      SB_DLOG(FATAL) << "No destination passed to UDP send.";
+      socket->error = kSbSocketErrorFailed;
+      return -1;
+    }
+
+    sbwin32::SockAddr sock_addr;
+    const sockaddr* sockaddr = nullptr;
+    socklen_t sockaddr_length = 0;
+    if (destination) {
+      if (!sock_addr.FromSbSocketAddress(destination)) {
+        SB_DLOG(FATAL) << "Invalid destination passed to UDP send.";
+        socket->error = kSbSocketErrorFailed;
+        return -1;
+      }
+      sockaddr = sock_addr.sockaddr();
+      sockaddr_length = sock_addr.length;
+    }
+
+    int bytes_written = sendto(socket->socket_handle, data, data_size,
+                               kSendFlags, sockaddr, sockaddr_length);
+    if (bytes_written >= 0) {
+      socket->error = kSbSocketOk;
+      return static_cast<int>(bytes_written);
+    }
+
+    int last_error = WSAGetLastError();
+    if (last_error != WSAEWOULDBLOCK) {
+      SB_DLOG(ERROR) << "sendto failed, last_error = " << last_error;
+    }
+    socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
+    return -1;
+  }
+
+  SB_NOTREACHED() << "Unrecognized protocol: " << socket->protocol;
+  return -1;
+}
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_set_broadcast.cc
similarity index 63%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_set_broadcast.cc
index fff4bd9..b60156b 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_set_broadcast.cc
@@ -12,10 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include <winsock2.h>
+
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketSetBroadcast(SbSocket socket, bool value) {
+  return sbwin32::SetBooleanSocketOption(socket, SOL_SOCKET, SO_BROADCAST,
+                                         "SO_BROADCAST", value);
 }
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_set_receive_buffer_size.cc
similarity index 63%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_set_receive_buffer_size.cc
index fff4bd9..61d108d 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_set_receive_buffer_size.cc
@@ -12,10 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include <winsock2.h>
+
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketSetReceiveBufferSize(SbSocket socket, int32_t size) {
+  return sbwin32::SetIntegerSocketOption(socket, SOL_SOCKET, SO_RCVBUF,
+                                         "SO_RCVBUF", size);
 }
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_set_reuse_address.cc
similarity index 63%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_set_reuse_address.cc
index fff4bd9..91145e6 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_set_reuse_address.cc
@@ -12,10 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include <winsock2.h>
+
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketSetReuseAddress(SbSocket socket, bool value) {
+  return sbwin32::SetBooleanSocketOption(socket, SOL_SOCKET, SO_REUSEADDR,
+                                         "SO_REUSEADDR", value);
 }
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_set_send_buffer_size.cc
similarity index 63%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_set_send_buffer_size.cc
index fff4bd9..2350e95 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_set_send_buffer_size.cc
@@ -12,10 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include <winsock2.h>
+
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketSetSendBufferSize(SbSocket socket, int32_t size) {
+  return sbwin32::SetIntegerSocketOption(socket, SOL_SOCKET, SO_SNDBUF,
+                                         "SO_SNDBUF", size);
 }
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_set_tcp_keep_alive.cc
similarity index 60%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_set_tcp_keep_alive.cc
index fff4bd9..aca24ef 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_set_tcp_keep_alive.cc
@@ -12,10 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include <winsock2.h>
+
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketSetTcpKeepAlive(SbSocket socket, bool value, SbTime period) {
+  const DWORD should_set_keepalive = value;
+  bool result = sbwin32::SetBooleanSocketOption(
+      socket, SOL_SOCKET, SO_KEEPALIVE, "SO_KEEPALIVE", value);
+  return result;
 }
diff --git a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc b/src/starboard/shared/win32/socket_set_tcp_no_delay.cc
similarity index 63%
copy from src/starboard/shared/win32/time_get_monotonic_thread_now.cc
copy to src/starboard/shared/win32/socket_set_tcp_no_delay.cc
index fff4bd9..694d743 100644
--- a/src/starboard/shared/win32/time_get_monotonic_thread_now.cc
+++ b/src/starboard/shared/win32/socket_set_tcp_no_delay.cc
@@ -12,10 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/time.h"
+#include "starboard/socket.h"
 
-SbTimeMonotonic SbTimeGetMonotonicThreadNow() {
-  // Neither QueryThreadCycleTime nor GetThreadTimes are listed as being
-  // available in UWP.
-  return SbTimeGetMonotonicNow();
+#include <winsock2.h>
+
+#include "starboard/shared/win32/socket_internal.h"
+
+namespace sbwin32 = starboard::shared::win32;
+
+bool SbSocketSetTcpNoDelay(SbSocket socket, bool value) {
+  return sbwin32::SetBooleanSocketOption(socket, IPPROTO_TCP, TCP_NODELAY,
+                                         "TCP_NODELAY", value);
 }
diff --git a/src/starboard/shared/win32/socket_set_tcp_window_scaling.cc b/src/starboard/shared/win32/socket_set_tcp_window_scaling.cc
new file mode 100644
index 0000000..9a0e29a
--- /dev/null
+++ b/src/starboard/shared/win32/socket_set_tcp_window_scaling.cc
@@ -0,0 +1,44 @@
+// 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/socket.h"
+
+#include <windows.h>
+#include <winsock2.h>
+
+// Note that windows.h and winsock2.h must be included before these.
+#include <mswsock.h>
+#include <sdkddkver.h>
+
+#include "starboard/shared/win32/socket_internal.h"
+
+bool SbSocketSetTcpWindowScaling(SbSocket socket, bool enable_window_scaling) {
+  // From
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/cc136103(v=vs.85).aspx:
+  // "When the TargetOsVersion member is set to a value for Windows Vista or
+  // later, receive window auto-tuning is enabled and the TCP window scale
+  // factor is reduced to 2 from the default value of 8."
+
+  const ULONG target_os_version =
+      enable_window_scaling ? NTDDI_VISTA : NTDDI_WS03;
+  WSA_COMPATIBILITY_MODE compatibility_mode = {WsaBehaviorAutoTuning,
+                                               target_os_version};
+
+  DWORD kZero = 0;
+  int return_value = WSAIoctl(
+      socket->socket_handle, SIO_SET_COMPATIBILITY_MODE, &compatibility_mode,
+      sizeof(WSA_COMPATIBILITY_MODE), nullptr, 0, &kZero, nullptr, nullptr);
+
+  return return_value != SOCKET_ERROR;
+}
diff --git a/src/starboard/shared/win32/time_get_monotonic_now.cc b/src/starboard/shared/win32/time_get_monotonic_now.cc
index 56a162a..e761368 100644
--- a/src/starboard/shared/win32/time_get_monotonic_now.cc
+++ b/src/starboard/shared/win32/time_get_monotonic_now.cc
@@ -16,9 +16,32 @@
 
 #include <windows.h>
 
-SbTimeMonotonic SbTimeGetMonotonicNow() {
-  ULONGLONG result;
-  QueryUnbiasedInterruptTimePrecise(&result);
+#include "starboard/log.h"
 
-  return static_cast<SbTimeMonotonic>(result) / 10;
+SbTimeMonotonic SbTimeGetMonotonicNow() {
+  LARGE_INTEGER counts;
+  bool success;
+
+  success = QueryPerformanceCounter(&counts);
+
+  // "On systems that run Windows XP or later,
+  // the function will always succeed and will thus never return zero."
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
+  SB_DCHECK(success);
+
+  LARGE_INTEGER countsPerSecond;
+  success = QueryPerformanceFrequency(&countsPerSecond);
+  // "On systems that run Windows XP or later,
+  // the function will always succeed and will thus never return zero."
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx
+  SB_DCHECK(success);
+
+  // An observed value of countsPerSecond on a desktop x86 machine is
+  // ~2.5e6. With this frequency, it will take ~37500 days to exceed
+  // 2^53, which is the mantissa precision of a double.
+  // Hence, we can safely convert to a double here without losing precision.
+  double result = static_cast<double>(counts.QuadPart);
+  result *= (1000.0 * 1000.0) / countsPerSecond.QuadPart;
+
+  return static_cast<SbTimeMonotonic>(result);
 }
diff --git a/src/starboard/shared/win32/time_utils.h b/src/starboard/shared/win32/time_utils.h
index cad1368..fea9e12 100644
--- a/src/starboard/shared/win32/time_utils.h
+++ b/src/starboard/shared/win32/time_utils.h
@@ -53,7 +53,6 @@
 inline DWORD ConvertSbTimeToMillisRoundUp(SbTime duration) {
   const int64_t milliseconds_to_sleep =
       (duration + kSbTimeMillisecond - 1) / kSbTimeMillisecond;
-  SB_DCHECK(milliseconds_to_sleep <= kSbInt32Max);
   return static_cast<DWORD>(milliseconds_to_sleep);
 }
 
diff --git a/src/third_party/openssl/openssl/crypto/evp/e_aes.c b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
index c81005f..fb0d026 100644
--- a/src/third_party/openssl/openssl/crypto/evp/e_aes.c
+++ b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
@@ -118,18 +118,9 @@
     case EVP_CIPH_CBC_MODE:
       *mode = kSbCryptographyBlockCipherModeCbc;
       return true;
-    case EVP_CIPH_CFB_MODE:
-      *mode = kSbCryptographyBlockCipherModeCfb;
-      return true;
     case EVP_CIPH_CTR_MODE:
       *mode = kSbCryptographyBlockCipherModeCtr;
       return true;
-    case EVP_CIPH_ECB_MODE:
-      *mode = kSbCryptographyBlockCipherModeEcb;
-      return true;
-    case EVP_CIPH_OFB_MODE:
-      *mode = kSbCryptographyBlockCipherModeOfb;
-      return true;
     default:
       break;
   }
@@ -145,6 +136,14 @@
   return SbCryptographyIsTransformerValid(context->gcm_transformer);
 }
 
+static inline bool sb_gcm_has_ctr_stream(GCM128_CONTEXT *context) {
+  return SbCryptographyIsTransformerValid(context->ctr_transformer);
+}
+
+static inline bool sb_gcm_has_ecb_stream(GCM128_CONTEXT *context) {
+  return SbCryptographyIsTransformerValid(context->ecb_transformer);
+}
+
 static int sb_init_key(EVP_CIPHER_CTX* context,
                        const unsigned char* key,
                        const unsigned char* initialization_vector,
@@ -187,27 +186,75 @@
   return (result == len);
 }
 
-static bool sb_gcm_init(GCM128_CONTEXT *context, const void *key, int key_length,
-                        int encrypt) {
+static inline SbCryptographyTransformer sb_create_ctr128(
+    SbCryptographyDirection direction, const void *key, int key_length) {
+  return SbCryptographyCreateTransformer(
+      kSbCryptographyAlgorithmAes,
+      128,
+      direction,
+      kSbCryptographyBlockCipherModeCtr,
+      NULL,
+      0,
+      key,
+      key_length);
+}
+
+static inline SbCryptographyTransformer sb_create_ecb128(
+    SbCryptographyDirection direction, const void *key, int key_length) {
+  return SbCryptographyCreateTransformer(
+      kSbCryptographyAlgorithmAes,
+      128,
+      direction,
+      kSbCryptographyBlockCipherModeEcb,
+      NULL,
+      0,
+      key,
+      key_length);
+}
+
+static inline SbCryptographyTransformer sb_create_gcm128(
+    SbCryptographyDirection direction, const void *key, int key_length) {
+  return SbCryptographyCreateTransformer(
+      kSbCryptographyAlgorithmAes,
+      128,
+      direction,
+      kSbCryptographyBlockCipherModeGcm,
+      NULL,
+      0,
+      key,
+      key_length);
+}
+
+static bool sb_gcm_init(GCM128_CONTEXT *context, const void *key,
+                        int key_length, int encrypt) {
+  context->ctr_transformer = kSbCryptographyInvalidTransformer;
+  context->ecb_transformer = kSbCryptographyInvalidTransformer;
+  context->encrypt = encrypt;
+  context->raw_key_length = key_length;
+  SbMemoryCopy(context->raw_key, key, key_length);
+
   SbCryptographyDirection direction =
       (encrypt ? kSbCryptographyDirectionEncode :
        kSbCryptographyDirectionDecode);
-  context->gcm_transformer =
-      SbCryptographyCreateTransformer(
-          kSbCryptographyAlgorithmAes,
-          128,
-          direction,
-          kSbCryptographyBlockCipherModeGcm,
-          NULL,
-          0,
-          key,
-          key_length);
-  return sb_gcm_has_stream(context);
+  context->gcm_transformer = sb_create_gcm128(direction, key, key_length);
+  if (sb_gcm_has_stream(context)) {
+    return true;
+  }
+
+  return false;
 }
 
-static inline void sb_gcm_setiv(GCM128_CONTEXT *context,
-                                const unsigned char *initialization_vector,
-                                size_t initialization_vector_size) {
+static void sb_gcm_init_backup(GCM128_CONTEXT *context, const void *key,
+                               int key_length) {
+  context->ctr_transformer = sb_create_ctr128(kSbCryptographyDirectionEncode,
+                                              key, key_length);
+  context->ecb_transformer = sb_create_ecb128(kSbCryptographyDirectionEncode,
+                                              key, key_length);
+}
+
+static void sb_gcm_setiv(GCM128_CONTEXT *context,
+                         const unsigned char *initialization_vector,
+                         size_t initialization_vector_size) {
   if (sb_gcm_has_stream(context)) {
     SbCryptographySetInitializationVector(context->gcm_transformer,
                                           initialization_vector,
@@ -230,7 +277,7 @@
   return CRYPTO_gcm128_aad(context, data, data_size);
 }
 
-static inline int sb_gcm_encrypt(GCM128_CONTEXT *context,
+static inline int sb_gcm_encrypt(GCM128_CONTEXT *context, ctr128_f ctr,
                                  const unsigned char *in, unsigned char *out,
                                  size_t len) {
   if (sb_gcm_has_stream(context)) {
@@ -239,10 +286,13 @@
     return result == len ? 0 : -1;
   }
 
+  if (ctr || sb_gcm_has_ctr_stream(context)) {
+    return CRYPTO_gcm128_encrypt_ctr32(context, in, out, len, ctr);
+  }
   return CRYPTO_gcm128_encrypt(context, in, out, len);
 }
 
-static inline int sb_gcm_decrypt(GCM128_CONTEXT *context,
+static inline int sb_gcm_decrypt(GCM128_CONTEXT *context, ctr128_f ctr,
                                  const unsigned char *in, unsigned char *out,
                                  size_t len) {
   if (sb_gcm_has_stream(context)) {
@@ -251,6 +301,9 @@
     return result == len ? 0 : -1;
   }
 
+  if (ctr || sb_gcm_has_ctr_stream(context)) {
+    return CRYPTO_gcm128_decrypt_ctr32(context, in, out, len, ctr);
+  }
   return CRYPTO_gcm128_decrypt(context, in, out, len);
 }
 
@@ -295,11 +348,22 @@
   return false;
 }
 
+static inline bool sb_gcm_has_ctr_stream(GCM128_CONTEXT *context) {
+  return false;
+}
+
+static inline bool sb_gcm_has_ecb_stream(GCM128_CONTEXT *context) {
+  return false;
+}
+
 static bool sb_gcm_init(GCM128_CONTEXT *context, const void *key, int key_length,
                         int encrypt) {
   return false;
 }
 
+static void sb_gcm_init_backup(GCM128_CONTEXT *context, const void *key,
+                               int key_length) {}
+
 static inline void sb_gcm_setiv(GCM128_CONTEXT *context,
                                 const unsigned char *initialization_vector,
                                 size_t initialization_vector_size) {
@@ -313,15 +377,21 @@
   return CRYPTO_gcm128_aad(context, data, data_size);
 }
 
-static inline int sb_gcm_encrypt(GCM128_CONTEXT *context,
+static inline int sb_gcm_encrypt(GCM128_CONTEXT *context, ctr128_f ctr,
                                  const unsigned char *in, unsigned char *out,
                                  size_t len) {
+  if (ctr) {
+    return CRYPTO_gcm128_encrypt_ctr32(context, in, out, len, ctr);
+  }
   return CRYPTO_gcm128_encrypt(context, in, out, len);
 }
 
-static inline int sb_gcm_decrypt(GCM128_CONTEXT *context,
+static inline int sb_gcm_decrypt(GCM128_CONTEXT *context, ctr128_f ctr,
                                  const unsigned char *in, unsigned char *out,
                                  size_t len) {
+  if (ctr) {
+    return CRYPTO_gcm128_decrypt_ctr32(context, in, out, len, ctr);
+  }
   return CRYPTO_gcm128_decrypt(context, in, out, len);
 }
 
@@ -1076,6 +1146,7 @@
                     CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
                                        (block128_f) AES_encrypt);
                     gctx->ctr = (ctr128_f) bsaes_ctr32_encrypt_blocks;
+                    sb_gcm_init_backup(&gctx->gcm, key, ctx->key_len);
                 }
                 break;
             } else
@@ -1087,6 +1158,7 @@
                     CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
                                        (block128_f) vpaes_encrypt);
                     gctx->ctr = NULL;
+                    sb_gcm_init_backup(&gctx->gcm, key, ctx->key_len);
                 }
                 break;
             } else
@@ -1102,6 +1174,7 @@
 #  else
                 gctx->ctr = NULL;
 #  endif
+                sb_gcm_init_backup(&gctx->gcm, key, ctx->key_len);
             }
         } while (0);
 
@@ -1139,6 +1212,7 @@
 {
     EVP_AES_GCM_CTX *gctx = ctx->cipher_data;
     int rv = -1;
+
     /* Encrypt/decrypt must be performed in place */
     if (out != in
         || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
@@ -1160,28 +1234,16 @@
     len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
     if (ctx->encrypt) {
         /* Encrypt payload */
-        if (gctx->ctr) {
-            if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
-                                            in, out, len, gctx->ctr))
-                goto err;
-        } else {
-            if (sb_gcm_encrypt(&gctx->gcm, in, out, len))
-                goto err;
-        }
+        if (sb_gcm_encrypt(&gctx->gcm, gctx->ctr, in, out, len))
+            goto err;
         out += len;
         /* Finally write tag */
         sb_gcm_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
         rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
     } else {
         /* Decrypt */
-        if (gctx->ctr) {
-            if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
-                                            in, out, len, gctx->ctr))
-                goto err;
-        } else {
-            if (sb_gcm_decrypt(&gctx->gcm, in, out, len))
-                goto err;
-        }
+        if (sb_gcm_decrypt(&gctx->gcm, gctx->ctr, in, out, len))
+            goto err;
         /* Retrieve tag */
         sb_gcm_tag(&gctx->gcm, ctx->buf, EVP_GCM_TLS_TAG_LEN);
         /* If tag mismatch wipe buffer */
@@ -1216,23 +1278,11 @@
             if (sb_gcm_aad(&gctx->gcm, in, len))
                 return -1;
         } else if (ctx->encrypt) {
-            if (gctx->ctr) {
-                if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
-                                                in, out, len, gctx->ctr))
-                    return -1;
-            } else {
-                if (sb_gcm_encrypt(&gctx->gcm, in, out, len))
-                    return -1;
-            }
+          if (sb_gcm_encrypt(&gctx->gcm, gctx->ctr, in, out, len))
+            return -1;
         } else {
-            if (gctx->ctr) {
-                if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
-                                                in, out, len, gctx->ctr))
-                    return -1;
-            } else {
-                if (sb_gcm_decrypt(&gctx->gcm, in, out, len))
-                    return -1;
-            }
+          if (sb_gcm_decrypt(&gctx->gcm, gctx->ctr, in, out, len))
+            return -1;
         }
         return len;
     } else {
diff --git a/src/third_party/openssl/openssl/crypto/modes/gcm128.c b/src/third_party/openssl/openssl/crypto/modes/gcm128.c
index cacc123..1df77da 100644
--- a/src/third_party/openssl/openssl/crypto/modes/gcm128.c
+++ b/src/third_party/openssl/openssl/crypto/modes/gcm128.c
@@ -86,6 +86,62 @@
         } \
 } while(0)
 
+
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+static const int kBlockSizeBits = 128;
+static const int kBlockSizeBytes = 16;
+static inline void sb_block128(GCM128_CONTEXT *ctx,
+                               block128_f block,
+                               const unsigned char in[16],
+                               unsigned char out[16],
+                               const void *key) {
+  if (SbCryptographyIsTransformerValid(ctx->ecb_transformer)) {
+    int result = SbCryptographyTransform(ctx->ecb_transformer, in,
+                                         kBlockSizeBytes, out);
+    SB_DCHECK(result == kBlockSizeBytes);
+  } else {
+    (*block)(in, out, key);
+  }
+}
+
+static inline void sb_ctr128(GCM128_CONTEXT *ctx,
+                             ctr128_f ctr,
+                             const unsigned char *in,
+                             unsigned char *out,
+                             size_t blocks,
+                             const void *key,
+                             const unsigned char ivec[16]) {
+  if (SbCryptographyIsTransformerValid(ctx->ctr_transformer)) {
+    SbCryptographySetInitializationVector(ctx->ctr_transformer, ivec, 16);
+    int len = (int)(blocks) * kBlockSizeBytes;
+    int result = SbCryptographyTransform(ctx->ctr_transformer, in, len, out);
+    SB_DCHECK(result == len);
+  } else {
+    (*ctr)(in, out, blocks, key, ivec);
+  }
+}
+#else  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+static inline void sb_block128(GCM128_CONTEXT *ctx,
+                               block128_f block,
+                               const unsigned char in[16],
+                               unsigned char out[16],
+                               const void *key) {
+  SB_UNREFERENCED_PARAMETER(ctx);
+  (*block)(in, out, key);
+}
+
+static inline void sb_ctr128(GCM128_CONTEXT *ctx,
+                             ctr128_f ctr,
+                             const unsigned char *in,
+                             unsigned char *out,
+                             size_t blocks,
+                             const void *key,
+                             const unsigned char ivec[16]) {
+  SB_UNREFERENCED_PARAMETER(ctx);
+  (*ctr)(in, out, blocks, key, ivec);
+}
+#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+
 /*-
  * Even though permitted values for TABLE_BITS are 8, 4 and 1, it should
  * never be set to 8. 8 is effectively reserved for testing purposes.
@@ -749,7 +805,7 @@
     ctx->block = block;
     ctx->key = key;
 
-    (*block) (ctx->H.c, ctx->H.c, key);
+    sb_block128(ctx, block, ctx->H.c, ctx->H.c, key);
 
     if (is_endian.little) {
         /* H is stored in host byte order */
@@ -882,7 +938,7 @@
             ctr = ctx->Yi.d[3];
     }
 
-    (*ctx->block) (ctx->Yi.c, ctx->EK0.c, ctx->key);
+    sb_block128(ctx, ctx->block, ctx->Yi.c, ctx->EK0.c, ctx->key);
     ++ctr;
     if (is_endian.little)
 #ifdef BSWAP4
@@ -1030,7 +1086,7 @@
                     size_t *out_t = (size_t *)out;
                     const size_t *in_t = (const size_t *)in;
 
-                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                     ++ctr;
                     if (is_endian.little)
 #  ifdef BSWAP4
@@ -1056,7 +1112,7 @@
                     size_t *out_t = (size_t *)out;
                     const size_t *in_t = (const size_t *)in;
 
-                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                     ++ctr;
                     if (is_endian.little)
 #  ifdef BSWAP4
@@ -1079,7 +1135,7 @@
                 size_t *out_t = (size_t *)out;
                 const size_t *in_t = (const size_t *)in;
 
-                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                 ++ctr;
                 if (is_endian.little)
 #  ifdef BSWAP4
@@ -1098,7 +1154,7 @@
             }
 # endif
             if (len) {
-                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                 ++ctr;
                 if (is_endian.little)
 # ifdef BSWAP4
@@ -1121,7 +1177,7 @@
 #endif
     for (i = 0; i < len; ++i) {
         if (n == 0) {
-            (*block) (ctx->Yi.c, ctx->EKi.c, key);
+            sb_block128(ctx, *block, ctx->Yi.c, ctx->EKi.c, key);
             ++ctr;
             if (is_endian.little)
 #ifdef BSWAP4
@@ -1217,7 +1273,7 @@
                     size_t *out_t = (size_t *)out;
                     const size_t *in_t = (const size_t *)in;
 
-                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                     ++ctr;
                     if (is_endian.little)
 #  ifdef BSWAP4
@@ -1241,7 +1297,7 @@
                     size_t *out_t = (size_t *)out;
                     const size_t *in_t = (const size_t *)in;
 
-                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                     ++ctr;
                     if (is_endian.little)
 #  ifdef BSWAP4
@@ -1263,7 +1319,7 @@
                 size_t *out_t = (size_t *)out;
                 const size_t *in_t = (const size_t *)in;
 
-                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                 ++ctr;
                 if (is_endian.little)
 #  ifdef BSWAP4
@@ -1285,7 +1341,7 @@
             }
 # endif
             if (len) {
-                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
                 ++ctr;
                 if (is_endian.little)
 # ifdef BSWAP4
@@ -1311,7 +1367,7 @@
     for (i = 0; i < len; ++i) {
         u8 c;
         if (n == 0) {
-            (*block) (ctx->Yi.c, ctx->EKi.c, key);
+            sb_block128(ctx, block, ctx->Yi.c, ctx->EKi.c, key);
             ++ctr;
             if (is_endian.little)
 #ifdef BSWAP4
@@ -1392,7 +1448,7 @@
     }
 #if defined(GHASH) && !defined(OPENSSL_SMALL_FOOTPRINT)
     while (len >= GHASH_CHUNK) {
-        (*stream) (in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
+        sb_ctr128(ctx, stream, in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
         ctr += GHASH_CHUNK / 16;
         if (is_endian.little)
 # ifdef BSWAP4
@@ -1411,7 +1467,7 @@
     if ((i = (len & (size_t)-16))) {
         size_t j = i / 16;
 
-        (*stream) (in, out, j, key, ctx->Yi.c);
+        sb_ctr128(ctx, stream, in, out, j, key, ctx->Yi.c);
         ctr += (unsigned int)j;
         if (is_endian.little)
 #ifdef BSWAP4
@@ -1436,7 +1492,7 @@
 #endif
     }
     if (len) {
-        (*ctx->block) (ctx->Yi.c, ctx->EKi.c, key);
+        sb_block128(ctx, ctx->block, ctx->Yi.c, ctx->EKi.c, key);
         ++ctr;
         if (is_endian.little)
 #ifdef BSWAP4
@@ -1517,7 +1573,7 @@
 #if defined(GHASH) && !defined(OPENSSL_SMALL_FOOTPRINT)
     while (len >= GHASH_CHUNK) {
         GHASH(ctx, in, GHASH_CHUNK);
-        (*stream) (in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
+        sb_ctr128(ctx, stream, in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
         ctr += GHASH_CHUNK / 16;
         if (is_endian.little)
 # ifdef BSWAP4
@@ -1548,7 +1604,7 @@
         j = i / 16;
         in -= i;
 #endif
-        (*stream) (in, out, j, key, ctx->Yi.c);
+        sb_ctr128(ctx, stream, in, out, j, key, ctx->Yi.c);
         ctr += (unsigned int)j;
         if (is_endian.little)
 #ifdef BSWAP4
@@ -1563,7 +1619,7 @@
         len -= i;
     }
     if (len) {
-        (*ctx->block) (ctx->Yi.c, ctx->EKi.c, key);
+        sb_block128(ctx, ctx->block, ctx->Yi.c, ctx->EKi.c, key);
         ++ctr;
         if (is_endian.little)
 #ifdef BSWAP4
diff --git a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
index 54febc9..db4bf70 100644
--- a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
+++ b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
@@ -122,10 +122,14 @@
     void *key;
 
 #if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
-    // A handle to a HW-accelerated transformer for the GCM block cipher
-    // mode. If valid, then this is used instead of any other state in the
-    // context.
-    SbCryptographyTransformer gcm_transformer;
+    // Whether in decrypt or encrypt mode.
+    int encrypt;
+
+    // The raw key specified to gcm_init.
+    char raw_key[512 / 8];
+
+    // The length of raw_key in bytes.
+    int raw_key_length;
 
     // A handle to a HW-accelerated transformer for the CTR block cipher
     // mode. If valid, then the software GCM code will wrap the hardware CTR
@@ -136,7 +140,12 @@
     // mode. If valid, then the software GCM code will wrap the hardware ECB
     // block cipher.
     SbCryptographyTransformer ecb_transformer;
-#endif
+
+    // A handle to a HW-accelerated transformer for the GCM block cipher
+    // mode. If valid, then this is used instead of any other state in the
+    // context.
+    SbCryptographyTransformer gcm_transformer;
+#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
 };
 
 struct xts128_context {
diff --git a/src/tools/gyp/pylib/gyp/MSVSVersion.py b/src/tools/gyp/pylib/gyp/MSVSVersion.py
index ad05788..6c44bb4 100644
--- a/src/tools/gyp/pylib/gyp/MSVSVersion.py
+++ b/src/tools/gyp/pylib/gyp/MSVSVersion.py
@@ -284,7 +284,8 @@
   return path
 
 
-def _DetectVisualStudioVersions(versions_to_check, force_express):
+def _DetectVisualStudioVersions(versions_to_check, force_express,
+                                vs_install_dir=None):
   """Collect the list of installed visual studio versions.
 
   Returns:
@@ -306,7 +307,10 @@
   for version in versions_to_check:
     if version == '15.0':
       # Version 15 does not include registry entries.
-      path = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE'
+      if vs_install_dir:
+        path = os.path.join(vs_install_dir, r'Common7\IDE')
+      else:
+        path = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE'
       path = _ConvertToCygpath(path)
       full_path = os.path.join(path, 'devenv.exe')
       if os.path.exists(full_path):
@@ -377,7 +381,9 @@
   }
 
   version = str(version)
-  versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
+  vs_install_dir = os.environ.get('VS_INSTALL_DIR')
+  versions = _DetectVisualStudioVersions(version_map[version], 'e' in version,
+                                         vs_install_dir)
   if not versions:
     if version == 'auto':
       # Default to 2005 if we couldn't find anything