Import Cobalt 16.140175

Change-Id: I1874a9229c64dffde87b3c3d5664ef915a6b9977
diff --git a/src/build/file_exists.py b/src/build/file_exists.py
new file mode 100644
index 0000000..9af5d79
--- /dev/null
+++ b/src/build/file_exists.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Writes True if the argument is a file."""
+
+import os.path
+import sys
+
+def main():
+  sys.stdout.write(str(os.path.isfile(sys.argv[1])))
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/cobalt/audio/audio_buffer_source_node.cc b/src/cobalt/audio/audio_buffer_source_node.cc
index 49ba2ad..adbfc95 100644
--- a/src/cobalt/audio/audio_buffer_source_node.cc
+++ b/src/cobalt/audio/audio_buffer_source_node.cc
@@ -111,35 +111,38 @@
   size_t channels = static_cast<size_t>(buffer_->number_of_channels());
 
   if (sample_type == kSampleTypeFloat32) {
-    std::vector<float*> audio_buffer(channels, NULL);
+    std::vector<scoped_refptr<dom::Float32Array>> audio_buffer_storages(
+        channels);
+    std::vector<float*> audio_buffers(channels, NULL);
     for (size_t i = 0; i < channels; ++i) {
       scoped_refptr<dom::Float32Array> buffer_data =
           buffer_->GetChannelData(static_cast<uint32>(i), NULL);
-      scoped_refptr<dom::Float32Array> sub_array = buffer_data->Subarray(
+      audio_buffer_storages[i] = buffer_data->Subarray(
           NULL, read_index_, read_index_ + number_of_frames);
-      audio_buffer[i] = sub_array->data();
+      audio_buffers[i] = audio_buffer_storages[i]->data();
     }
 
     read_index_ += number_of_frames;
 
-    scoped_ptr<ShellAudioBus> audio_bus(
-        new ShellAudioBus(static_cast<size_t>(number_of_frames), audio_buffer));
+    scoped_ptr<ShellAudioBus> audio_bus(new ShellAudioBus(
+        static_cast<size_t>(number_of_frames), audio_buffers));
 
     return audio_bus.Pass();
   } else if (sample_type == kSampleTypeInt16) {
-    std::vector<int16*> audio_buffer(channels, NULL);
+    std::vector<scoped_refptr<dom::Int16Array>> audio_buffer_storages(channels);
+    std::vector<int16*> audio_buffers(channels, NULL);
     for (size_t i = 0; i < channels; ++i) {
       scoped_refptr<dom::Int16Array> buffer_data =
           buffer_->GetChannelDataInt16(static_cast<uint32>(i), NULL);
-      scoped_refptr<dom::Int16Array> sub_array = buffer_data->Subarray(
+      audio_buffer_storages[i] = buffer_data->Subarray(
           NULL, read_index_, read_index_ + number_of_frames);
-      audio_buffer[i] = sub_array->data();
+      audio_buffers[i] = audio_buffer_storages[i]->data();
     }
 
     read_index_ += number_of_frames;
 
-    scoped_ptr<ShellAudioBus> audio_bus(
-        new ShellAudioBus(static_cast<size_t>(number_of_frames), audio_buffer));
+    scoped_ptr<ShellAudioBus> audio_bus(new ShellAudioBus(
+        static_cast<size_t>(number_of_frames), audio_buffers));
 
     return audio_bus.Pass();
   }
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 28d1bce..53d041d 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -170,14 +170,14 @@
   *frames_in_buffer = static_cast<int>(frames_rendered_ - frames_consumed_);
 
   if ((kFramesPerChannel - *frames_in_buffer) >= kRenderBufferSizeFrames) {
-    bool silence = false;
-
     // If there was silence last time we were called, then the buffer has
     // already been zeroed out and we don't need to do it again.
     if (!was_silence_last_update_) {
       input_audio_bus_.ZeroAllFrames();
     }
 
+    bool silence = true;
+
     // Fill our temporary buffer with planar PCM float samples.
     render_callback_->FillAudioBus(&input_audio_bus_, &silence);
 
@@ -345,7 +345,7 @@
 
   if ((kFramesPerChannel - *total_frames) >= kRenderBufferSizeFrames) {
     // Fill our temporary buffer with PCM float samples.
-    bool silence = false;
+    bool silence = true;
     render_callback_->FillAudioBus(&audio_bus_, &silence);
 
     if (!silence) {
diff --git a/src/cobalt/audio/audio_device.h b/src/cobalt/audio/audio_device.h
index fec6cea..dd5f898 100644
--- a/src/cobalt/audio/audio_device.h
+++ b/src/cobalt/audio/audio_device.h
@@ -38,6 +38,10 @@
     typedef ::media::ShellAudioBus ShellAudioBus;
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
+    // |silence| will be set to true before calling if |audio_buffer| contains
+    // only silence samples, it will be set to |false| otherwise.  On return
+    // FillAudioBus() will set |silence| to |false| if it has modified
+    // |audio_buffer|.
     virtual void FillAudioBus(ShellAudioBus* audio_buffer, bool* silence) = 0;
 
    protected:
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index 2e76dcf..529986d 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -231,8 +231,6 @@
   // This is called by Audio thread.
   owner_node_->audio_lock()->AssertLocked();
 
-  *silence = true;
-
   // TODO: Consider computing computedNumberOfChannels and do up-mix or
   // down-mix base on computedNumberOfChannels. The current implementation
   // is based on the fact that the channelCountMode is max.
@@ -251,8 +249,12 @@
         output_audio_bus->sample_type());
 
     if (audio_bus) {
-      MixAudioBuffer(owner_node_->channel_interpretation(), audio_bus.get(),
-                     output_audio_bus);
+      if (*silence && audio_bus->channels() == output_audio_bus->channels()) {
+        output_audio_bus->Assign(*audio_bus);
+      } else {
+        MixAudioBuffer(owner_node_->channel_interpretation(), audio_bus.get(),
+                       output_audio_bus);
+      }
       *silence = false;
     }
   }
diff --git a/src/cobalt/base/accessibility_caption_settings_changed_event.h b/src/cobalt/base/accessibility_caption_settings_changed_event.h
new file mode 100644
index 0000000..d0c0a69
--- /dev/null
+++ b/src/cobalt/base/accessibility_caption_settings_changed_event.h
@@ -0,0 +1,35 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_ACCESSIBILITY_CAPTION_SETTINGS_CHANGED_EVENT_H_
+#define COBALT_BASE_ACCESSIBILITY_CAPTION_SETTINGS_CHANGED_EVENT_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/string_util.h"
+#include "cobalt/base/event.h"
+
+namespace base {
+
+class AccessibilityCaptionSettingsChangedEvent : public Event {
+ public:
+  AccessibilityCaptionSettingsChangedEvent() {}
+
+  BASE_EVENT_SUBCLASS(AccessibilityCaptionSettingsChangedEvent);
+};
+
+}  // namespace base
+
+#endif  // COBALT_BASE_ACCESSIBILITY_CAPTION_SETTINGS_CHANGED_EVENT_H_
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index 2f25385..c5eadee 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -78,6 +78,8 @@
         'unused.h',
         'user_log.cc',
         'user_log.h',
+        'version_compatibility.cc',
+        'version_compatibility.h',
       ],
       'dependencies': [
         '<(DEPTH)/base/base.gyp:base',
diff --git a/src/cobalt/base/version_compatibility.cc b/src/cobalt/base/version_compatibility.cc
new file mode 100644
index 0000000..b912f6f
--- /dev/null
+++ b/src/cobalt/base/version_compatibility.cc
@@ -0,0 +1,52 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
+#include "cobalt/base/version_compatibility.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+VersionCompatibility* VersionCompatibility::GetInstance() {
+  return Singleton<VersionCompatibility,
+                   StaticMemorySingletonTraits<VersionCompatibility> >::get();
+}
+
+VersionCompatibility::VersionCompatibility()
+    : violation_count(
+          "Count.VersionCompatibilityViolation", 0,
+          "The number of Cobalt version compatibility violations encountered."),
+      minimum_version_(0) {}
+
+void VersionCompatibility::SetMinimumVersion(int minimum_version) {
+  base::subtle::NoBarrier_Store(&minimum_version_, minimum_version);
+}
+
+int VersionCompatibility::GetMinimumVersion() const {
+  return base::subtle::NoBarrier_Load(&minimum_version_);
+}
+
+void VersionCompatibility::ReportViolation(
+    const std::string& violation_message) {
+  const std::string kVersionCompatibilityViolationTag =
+      "[VERSION COMPATIBILITY VIOLATION]";
+  LOG(ERROR) << kVersionCompatibilityViolationTag << " " << violation_message;
+  ++violation_count;
+}
+
+}  // namespace base
+
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
diff --git a/src/cobalt/base/version_compatibility.h b/src/cobalt/base/version_compatibility.h
new file mode 100644
index 0000000..4a776d7
--- /dev/null
+++ b/src/cobalt/base/version_compatibility.h
@@ -0,0 +1,63 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_VERSION_COMPATIBILITY_H_
+#define COBALT_BASE_VERSION_COMPATIBILITY_H_
+
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/memory/singleton.h"
+#include "cobalt/base/c_val.h"
+
+namespace base {
+
+// This thread-safe singleton class provides basic support for Cobalt version
+// compatibility validations.
+// NOTE: This class exists for convenience and can be removed when we create a
+// better mechanism for dependency injection in Cobalt.
+class VersionCompatibility {
+ public:
+  // Method to get the singleton instance of this class.
+  static VersionCompatibility* GetInstance();
+
+  void SetMinimumVersion(int minimum_version);
+  int GetMinimumVersion() const;
+
+  // Called when a version compatibility violation is encountered.
+  void ReportViolation(const std::string& violation_info);
+
+ private:
+  friend struct StaticMemorySingletonTraits<VersionCompatibility>;
+
+  VersionCompatibility();
+  ~VersionCompatibility() = default;
+
+  // The number of violations that have been encountered.
+  base::CVal<int, base::CValDebug> violation_count;
+
+  // The minimum version of Cobalt to validate compatibility against.
+  base::subtle::Atomic32 minimum_version_;
+
+  DISALLOW_COPY_AND_ASSIGN(VersionCompatibility);
+};
+
+}  // namespace base
+
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
+#endif  // COBALT_BASE_VERSION_COMPATIBILITY_H_
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h
index 01fdd20..82fa889 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h
@@ -41,9 +41,11 @@
       const scoped_refptr<script::Wrappable>& callback_this,
       const scoped_refptr<ArbitraryInterface>& value,
       bool* had_exception) const override;
+
   JSObject* handle() const { return implementing_object_.GetObject(); }
   const JS::Value& value() const { return implementing_object_.GetValue(); }
   bool WasCollected() const { return implementing_object_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { implementing_object_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc
index 7b85687..47af2a9 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,113 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 0;
+
+
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousIndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousIndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousIndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -127,52 +231,141 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "AnonymousIndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "AnonymousIndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 0;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "AnonymousIndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+  {
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
+  }
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cAnonymousIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cAnonymousIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -182,14 +375,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cAnonymousIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cAnonymousIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h
index 8d168df..4af4572 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cAnonymousIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
index 587bce5..ab85a2f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,49 +84,226 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 1;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousNamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousNamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "AnonymousNamedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "AnonymousNamedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "AnonymousNamedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+  {
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
+  }
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 1;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cAnonymousNamedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cAnonymousNamedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -139,14 +313,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cAnonymousNamedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cAnonymousNamedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h
index 6e3f21b..190de7f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cAnonymousNamedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
index 1d2d4c1..3947292 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,221 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 2;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousNamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousNamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousIndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousIndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -127,52 +339,153 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "AnonymousNamedIndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "AnonymousNamedIndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 2;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "AnonymousNamedIndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+  {
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
+  }
+
+  {
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
+  }
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cAnonymousNamedIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cAnonymousNamedIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -182,14 +495,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cAnonymousNamedIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cAnonymousNamedIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h
index 59b1d60..fd83275 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cAnonymousNamedIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc
index f644082..627c95d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 3;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -211,64 +216,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ArbitraryInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "ArbitraryInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "arbitraryProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    arbitraryPropertyAttributeGetter
-    ,arbitraryPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, arbitraryFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "arbitraryFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "arbitraryProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        arbitraryPropertyAttributeGetter,
+        arbitraryPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "arbitraryFunction");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 3;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, arbitraryFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "arbitraryFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ArbitraryInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cArbitraryInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cArbitraryInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -278,14 +383,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cArbitraryInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cArbitraryInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h
index 89dfb9a..1ac26dd 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h
@@ -39,8 +39,7 @@
 class V8cArbitraryInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc
index fd2ca14..e447a9c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 4;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -174,63 +179,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "BaseInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "BaseInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "baseAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    baseAttributeAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, baseOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "baseOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "baseAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        baseAttributeAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "baseOperation");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 4;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, baseOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "baseOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "BaseInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cBaseInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cBaseInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -240,14 +346,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cBaseInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cBaseInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h
index aaecf17..64c0f77 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h
@@ -39,8 +39,7 @@
 class V8cBaseInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc
index 4425931..a3706ab 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 5;
+
+
+
+
+
+
+
 
 
 void booleanPropertyAttributeGetter(
@@ -246,75 +251,196 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "BooleanTypeTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "BooleanTypeTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "booleanProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    booleanPropertyAttributeGetter
-    ,booleanPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, booleanArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "booleanArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        booleanPropertyAttributeGetter,
+        booleanPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, booleanReturnOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, booleanArgumentOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "booleanReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "booleanArgumentOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, booleanReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "booleanReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "BooleanTypeTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 5;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cBooleanTypeTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cBooleanTypeTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -324,14 +450,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cBooleanTypeTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cBooleanTypeTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h
index d2af8d7..64ffe39 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h
@@ -39,8 +39,7 @@
 class V8cBooleanTypeTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc
index 74dbe19..1328e44 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 6;
+
+
+
+
+
+
+
 
 
 void callbackAttributeAttributeGetter(
@@ -167,6 +172,7 @@
   return;
 }
 
+
 void nullableCallbackAttributeAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -472,115 +478,329 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "CallbackFunctionInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "CallbackFunctionInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "callbackAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    callbackAttributeAttributeGetter
-    ,callbackAttributeAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableCallbackAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableCallbackAttributeAttributeGetter
-    ,nullableCallbackAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionThatReturnsStringMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionThatReturnsString",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "callbackAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        callbackAttributeAttributeGetter,
+        callbackAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableCallbackAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableCallbackAttributeAttributeGetter,
+        nullableCallbackAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithNullableParametersMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionThatReturnsString");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionThatReturnsStringMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionWithNullableParameters",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "takesFunctionThatReturnsString"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionWithNullableParameters");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionWithNullableParametersMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesFunctionWithNullableParameters"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionWithOneParameter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionWithOneParameterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesFunctionWithOneParameter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionWithSeveralParameters");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionWithSeveralParametersMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesFunctionWithSeveralParameters"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesVoidFunction");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesVoidFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesVoidFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithOneParameterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionWithOneParameter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithSeveralParametersMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionWithSeveralParameters",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesVoidFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesVoidFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "CallbackFunctionInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 6;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cCallbackFunctionInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cCallbackFunctionInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -590,14 +810,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cCallbackFunctionInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cCallbackFunctionInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h
index a6cf0f1..f54da19 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h
@@ -39,8 +39,7 @@
 class V8cCallbackFunctionInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc
index c582b3b..6f254dc 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 7;
+
+
+
+
+
+
+
 
 
 void callbackAttributeAttributeGetter(
@@ -244,75 +249,196 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "CallbackInterfaceInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "CallbackInterfaceInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "callbackAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    callbackAttributeAttributeGetter
-    ,callbackAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, registerCallbackMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "registerCallback",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "callbackAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        callbackAttributeAttributeGetter,
+        callbackAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, someOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "registerCallback");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, registerCallbackMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "someOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "registerCallback"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "someOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, someOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "someOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "CallbackInterfaceInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 7;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cCallbackInterfaceInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cCallbackInterfaceInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -322,14 +448,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cCallbackInterfaceInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cCallbackInterfaceInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h
index 64a407e..9fc8f8f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h
@@ -39,8 +39,7 @@
 class V8cCallbackInterfaceInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
index 42eb6bb..28b4236 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -60,7 +62,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -68,18 +69,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -89,9 +86,17 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 8;
+
+
+
+
+
+
 
 
 #if defined(ENABLE_CONDITIONAL_PROPERTY)
+
 void enabledAttributeAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -168,6 +173,7 @@
 
 #endif  // ENABLE_CONDITIONAL_PROPERTY
 #if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+
 void disabledAttributeAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -308,90 +314,238 @@
 
 #endif  // ENABLE_CONDITIONAL_PROPERTY
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConditionalInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ConditionalInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-#if defined(ENABLE_CONDITIONAL_PROPERTY)
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "enabledAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    enabledAttributeAttributeGetter
-    ,enabledAttributeAttributeSetter
-  );
-#endif  // defined(ENABLE_CONDITIONAL_PROPERTY)
-#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "disabledAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    disabledAttributeAttributeGetter
-    ,disabledAttributeAttributeSetter
-  );
-#endif  // defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
-#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, disabledOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "disabledOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-#endif  // defined(NO_ENABLE_CONDITIONAL_PROPERTY)
-
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
 #if defined(ENABLE_CONDITIONAL_PROPERTY)
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, enabledOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "enabledOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "enabledAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        enabledAttributeAttributeGetter,
+        enabledAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
-#endif  // defined(ENABLE_CONDITIONAL_PROPERTY)
+#endif  // ENABLE_CONDITIONAL_PROPERTY
+#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        disabledAttributeAttributeGetter,
+        disabledAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 8;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+#endif  // NO_ENABLE_CONDITIONAL_PROPERTY
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, disabledOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "disabledOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+#endif  // NO_ENABLE_CONDITIONAL_PROPERTY
+#if defined(ENABLE_CONDITIONAL_PROPERTY)
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "enabledOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, enabledOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "enabledOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+#endif  // ENABLE_CONDITIONAL_PROPERTY
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConditionalInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConditionalInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConditionalInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -401,14 +555,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConditionalInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConditionalInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
index a98041d..212639b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
@@ -41,8 +41,7 @@
 class V8cConditionalInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc
index 4077c2d..ea5e9af 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,73 +84,158 @@
 
 namespace {
 
-
-void INTEGER_CONSTANTAttributeGetter(
-    v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
-  // TODO: Consider just attaching a constant instead.
-  v8::Local<v8::Value> result_value;
-  ToJSValue(info.GetIsolate(), 5, &result_value);
-  info.GetReturnValue().Set(result_value);
-}
-void DOUBLE_CONSTANTAttributeGetter(
-    v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
-  // TODO: Consider just attaching a constant instead.
-  v8::Local<v8::Value> result_value;
-  ToJSValue(info.GetIsolate(), 2.718, &result_value);
-  info.GetReturnValue().Set(result_value);
-}
+const int kInterfaceUniqueId = 9;
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConstantsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ConstantsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
 
-  prototype_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "INTEGER_CONSTANT",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    INTEGER_CONSTANTAttributeGetter);
-  prototype_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "DOUBLE_CONSTANT",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    DOUBLE_CONSTANTAttributeGetter);
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+  {
+    // The name of the property is the identifier of the constant.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "INTEGER_CONSTANT");
+
+    // The value of the property is that which is obtained by converting the
+    // constant's IDL value to an ECMAScript value.
+    v8::Local<v8::Value> constant_value;
+    ToJSValue(isolate, 5, &constant_value);
+
+    // The property has attributes { [[Writable]]: false, [[Enumerable]]: true,
+    // [[Configurable]]: false }.
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        v8::ReadOnly | v8::DontDelete);
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the interface has an interface prototype object, then the
+    // property exists on it.
+    prototype_template->Set(name, constant_value, attributes);
+
+    // In addition, a property with the same characteristics must exist on the
+    // interface object or the legacy callback interface object, if either of
+    // those objects exists.
+    function_template->Set(name, constant_value, attributes);
+  }
+  {
+    // The name of the property is the identifier of the constant.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "DOUBLE_CONSTANT");
+
+    // The value of the property is that which is obtained by converting the
+    // constant's IDL value to an ECMAScript value.
+    v8::Local<v8::Value> constant_value;
+    ToJSValue(isolate, 2.718, &constant_value);
+
+    // The property has attributes { [[Writable]]: false, [[Enumerable]]: true,
+    // [[Configurable]]: false }.
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        v8::ReadOnly | v8::DontDelete);
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the interface has an interface prototype object, then the
+    // property exists on it.
+    prototype_template->Set(name, constant_value, attributes);
+
+    // In addition, a property with the same characteristics must exist on the
+    // interface object or the legacy callback interface object, if either of
+    // those objects exists.
+    function_template->Set(name, constant_value, attributes);
+  }
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConstantsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 9;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConstantsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConstantsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -163,14 +245,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConstantsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConstantsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h
index 9ea42b7..c30024c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h
@@ -39,8 +39,7 @@
 class V8cConstantsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc
index 0aef9a1..48c0906 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 10;
+
+
+
+
+
+
+
 
 void Constructor1(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -172,46 +177,96 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConstructorInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "ConstructorInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConstructorInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 10;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConstructorInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConstructorInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -221,14 +276,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConstructorInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConstructorInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h
index 21cc81d..857b2cf 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h
@@ -39,8 +39,7 @@
 class V8cConstructorInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc
index 6723e45..0903cc0 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 11;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -184,6 +189,7 @@
 }
 
 
+
 void booleanArgAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -221,6 +227,7 @@
 }
 
 
+
 void stringArgAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -259,64 +266,199 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConstructorWithArgumentsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          2);
+  function_template->SetLength(2);
+  function_template->SetClassName(NewInternalString(isolate, "ConstructorWithArgumentsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "longArg",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    longArgAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "booleanArg",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    booleanArgAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "stringArg",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    stringArgAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longArg");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        longArgAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 11;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanArg");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        booleanArgAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "stringArg");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        stringArgAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConstructorWithArgumentsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConstructorWithArgumentsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConstructorWithArgumentsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -326,14 +468,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConstructorWithArgumentsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConstructorWithArgumentsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h
index 6c01a6e..a5b004c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h
@@ -39,8 +39,7 @@
 class V8cConstructorWithArgumentsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
index 2acd721..f2d42d6 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,221 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 14;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousNamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousNamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->DerivedIndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->DerivedIndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -130,6 +342,7 @@
 }
 
 
+
 void propertyOnDerivedClassAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -343,94 +556,297 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DerivedGetterSetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DerivedGetterSetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-  v8::Local<v8::FunctionTemplate> parent_template = V8cNamedIndexedGetterInterface::CreateTemplate(isolate);
-  function_template->Inherit(parent_template);
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
-
-
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "propertyOnDerivedClass",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    propertyOnDerivedClassAttributeGetter
-    ,propertyOnDerivedClassAttributeSetter
-  );
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedIndexedGetterMethod);
+    // An interface can be defined to inherit from another interface. If the
+    // identifier of the interface is followed by a U+003A COLON (":") character
+    // and an identifier, then that identifier identifies the inherited
+    // interface. An object that implements an interface that inherits from
+    // another also implements that inherited interface. The object therefore
+    // will also have members that correspond to the interface members from the
+    // inherited interface.
+    v8::Local<v8::FunctionTemplate> parent_template = V8cNamedIndexedGetterInterface::GetTemplate(isolate);
+    function_template->Inherit(parent_template);
+  }
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "propertyOnDerivedClass");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        propertyOnDerivedClassAttributeGetter,
+        propertyOnDerivedClassAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedIndexedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedIndexedGetterMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedIndexedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "derivedIndexedGetter"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedIndexedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedIndexedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "derivedIndexedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "operationOnDerivedClass");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, operationOnDerivedClassMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "operationOnDerivedClass"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DerivedGetterSetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+  {
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
   }
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedIndexedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedIndexedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, operationOnDerivedClassMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "operationOnDerivedClass",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 14;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDerivedGetterSetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDerivedGetterSetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -440,14 +856,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDerivedGetterSetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDerivedGetterSetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h
index a938db4..1c50e6d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h
@@ -40,8 +40,7 @@
 class V8cDerivedGetterSetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
index 9e13e18..db63e88 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 15;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -178,65 +183,175 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DerivedInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "DerivedInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-  v8::Local<v8::FunctionTemplate> parent_template = V8cBaseInterface::CreateTemplate(isolate);
-  function_template->Inherit(parent_template);
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
-
-
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "derivedAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    derivedAttributeAttributeGetter
-  );
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // An interface can be defined to inherit from another interface. If the
+    // identifier of the interface is followed by a U+003A COLON (":") character
+    // and an identifier, then that identifier identifies the inherited
+    // interface. An object that implements an interface that inherits from
+    // another also implements that inherited interface. The object therefore
+    // will also have members that correspond to the interface members from the
+    // inherited interface.
+    v8::Local<v8::FunctionTemplate> parent_template = V8cBaseInterface::GetTemplate(isolate);
+    function_template->Inherit(parent_template);
   }
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedAttribute");
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 15;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        derivedAttributeAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "derivedOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DerivedInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDerivedInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDerivedInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -246,14 +361,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDerivedInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDerivedInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h
index ff15ad0..1814d02 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h
@@ -40,8 +40,7 @@
 class V8cDerivedInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc
index a2de658..48ee0bd 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc
@@ -36,12 +36,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -64,7 +66,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -72,18 +73,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -93,6 +90,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 16;
+
+
+
+
+
+
+
 
 
 void dictionarySequenceAttributeGetter(
@@ -308,86 +313,229 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DictionaryInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DictionaryInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "dictionarySequence",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    dictionarySequenceAttributeGetter
-    ,dictionarySequenceAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedDictionaryOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedDictionaryOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "dictionarySequence");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        dictionarySequenceAttributeGetter,
+        dictionarySequenceAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, dictionaryOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedDictionaryOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedDictionaryOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "dictionaryOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "derivedDictionaryOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "dictionaryOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, dictionaryOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "dictionaryOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "testOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, testOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "testOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, testOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "testOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DictionaryInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 16;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDictionaryInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDictionaryInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -397,14 +545,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDictionaryInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDictionaryInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h
index 7c976d4..ef0a08a 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h
@@ -39,8 +39,7 @@
 class V8cDictionaryInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
index d800dbc..d14ac28 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -60,7 +62,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -68,18 +69,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -89,6 +86,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 18;
+
+
+
+
+
+
+
 
 
 void disabledPropertyAttributeGetter(
@@ -196,64 +201,163 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DisabledInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DisabledInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "disabledProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    disabledPropertyAttributeGetter
-    ,disabledPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, disabledFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "disabledFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        disabledPropertyAttributeGetter,
+        disabledPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledFunction");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 18;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, disabledFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "disabledFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DisabledInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDisabledInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDisabledInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -263,14 +367,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDisabledInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDisabledInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
index ff9b4ee..e5734ce 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
@@ -41,8 +41,7 @@
 class V8cDisabledInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc
index ac89400..d9fb0da 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 12;
+
+
+
+
+
+
+
 
 
 void propertyAttributeGetter(
@@ -163,6 +168,7 @@
   return;
 }
 
+
 void readOnlyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -200,6 +206,7 @@
 }
 
 
+
 void readOnlyTokenPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -237,6 +244,7 @@
 }
 
 
+
 void nullIsEmptyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -311,6 +319,7 @@
   return;
 }
 
+
 void undefinedIsEmptyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -385,6 +394,7 @@
   return;
 }
 
+
 void nullableUndefinedIsEmptyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -460,86 +470,300 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DOMStringTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DOMStringTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "property",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    propertyAttributeGetter
-    ,propertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "readOnlyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    readOnlyPropertyAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "readOnlyTokenProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    readOnlyTokenPropertyAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullIsEmptyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullIsEmptyPropertyAttributeGetter
-    ,nullIsEmptyPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "undefinedIsEmptyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    undefinedIsEmptyPropertyAttributeGetter
-    ,undefinedIsEmptyPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableUndefinedIsEmptyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableUndefinedIsEmptyPropertyAttributeGetter
-    ,nullableUndefinedIsEmptyPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "property");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        propertyAttributeGetter,
+        propertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 12;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "readOnlyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        readOnlyPropertyAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "readOnlyTokenProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        readOnlyTokenPropertyAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullIsEmptyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullIsEmptyPropertyAttributeGetter,
+        nullIsEmptyPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "undefinedIsEmptyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        undefinedIsEmptyPropertyAttributeGetter,
+        undefinedIsEmptyPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableUndefinedIsEmptyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableUndefinedIsEmptyPropertyAttributeGetter,
+        nullableUndefinedIsEmptyPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DOMStringTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDOMStringTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDOMStringTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -549,14 +773,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDOMStringTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDOMStringTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h
index fbda377..e8fd6c3 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h
@@ -39,8 +39,7 @@
 class V8cDOMStringTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc
index 68e0e21..ba0038b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc
@@ -34,12 +34,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -60,7 +62,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -68,18 +69,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -89,6 +86,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 19;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -228,64 +233,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "EnumerationInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "EnumerationInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "enumProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    enumPropertyAttributeGetter
-    ,enumPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalEnumWithDefaultMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "optionalEnumWithDefault",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "enumProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        enumPropertyAttributeGetter,
+        enumPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "optionalEnumWithDefault");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 19;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, optionalEnumWithDefaultMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "optionalEnumWithDefault"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "EnumerationInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cEnumerationInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cEnumerationInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -295,14 +400,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cEnumerationInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cEnumerationInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h
index 677d83d..7554cfc 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h
@@ -39,8 +39,7 @@
 class V8cEnumerationInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc
index a389979..8c19687 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 20;
+
+
+
+
+
+
+
 
 
 void errorAttributeGetter(
@@ -126,6 +131,7 @@
 }
 
 
+
 void messageAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -164,59 +170,175 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ExceptionObjectInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ExceptionObjectInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-  NOTIMPLEMENTED();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  {
+    // A spicy hack from Chromium in order to achieve
+    // https://heycam.github.io/webidl/#es-DOMException-specialness
+    // See https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl?l=630&rcl=0f7c2c752bb24ad08c17017e4e68401093fe76a0
+    v8::Local<v8::FunctionTemplate> intrinsic_error_prototype_interface_template =
+        v8::FunctionTemplate::New(isolate);
+    intrinsic_error_prototype_interface_template->RemovePrototype();
+    intrinsic_error_prototype_interface_template->SetIntrinsicDataProperty(
+        NewInternalString(isolate, "prototype"), v8::kErrorPrototype);
+    function_template->Inherit(intrinsic_error_prototype_interface_template);
+  }
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "error");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "error",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    errorAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "message",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    messageAttributeGetter
-  );
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        errorAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "message");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        messageAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 20;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ExceptionObjectInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cExceptionObjectInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cExceptionObjectInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -226,14 +348,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cExceptionObjectInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cExceptionObjectInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h
index 7e8832a..0ec5d1b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h
@@ -39,8 +39,7 @@
 class V8cExceptionObjectInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc
index 574bb64..2476ba1 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 21;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -215,64 +220,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ExceptionsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "ExceptionsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "attributeThrowsException",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    attributeThrowsExceptionAttributeGetter
-    ,attributeThrowsExceptionAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, functionThrowsExceptionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "functionThrowsException",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "attributeThrowsException");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        attributeThrowsExceptionAttributeGetter,
+        attributeThrowsExceptionAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "functionThrowsException");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 21;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, functionThrowsExceptionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "functionThrowsException"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ExceptionsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cExceptionsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cExceptionsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -282,14 +387,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cExceptionsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cExceptionsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h
index 32e436b..13eb669 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h
@@ -39,8 +39,7 @@
 class V8cExceptionsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc
index 65e53a8..5de530d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 22;
+
+
+
+
+
+
+
 
 
 void defaultAttributeGetter(
@@ -241,75 +246,196 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ExtendedIDLAttributesInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ExtendedIDLAttributesInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "default",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    defaultAttributeGetter
-    ,defaultAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, callWithSettingsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "callWithSettings",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "default");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        defaultAttributeGetter,
+        defaultAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, clampArgumentMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "callWithSettings");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, callWithSettingsMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "clampArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "callWithSettings"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "clampArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, clampArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "clampArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ExtendedIDLAttributesInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 22;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cExtendedIDLAttributesInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cExtendedIDLAttributesInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -319,14 +445,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cExtendedIDLAttributesInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cExtendedIDLAttributesInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h
index a23c50d..ae2015d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h
@@ -39,8 +39,7 @@
 class V8cExtendedIDLAttributesInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc
index 95e9107..d6ded64 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 23;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -184,6 +189,7 @@
   return;
 }
 
+
 void nextAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -259,60 +265,165 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "GarbageCollectionTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "GarbageCollectionTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "previous",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    previousAttributeGetter
-    ,previousAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "next",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nextAttributeGetter
-    ,nextAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "previous");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        previousAttributeGetter,
+        previousAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 23;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "next");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nextAttributeGetter,
+        nextAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "GarbageCollectionTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cGarbageCollectionTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cGarbageCollectionTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -322,14 +433,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cGarbageCollectionTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cGarbageCollectionTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h
index 8b5f975..b452e3f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h
@@ -39,8 +39,7 @@
 class V8cGarbageCollectionTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc
index c1f2c66..d25a0d6 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,13 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 24;
+
+
+
+
+
+
 
 
 
@@ -120,57 +124,129 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "GlobalInterfaceParent",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "GlobalInterfaceParent"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, parentOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "parentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, parentOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "parentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "parentOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "GlobalInterfaceParent"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 24;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cGlobalInterfaceParent::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cGlobalInterfaceParent::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -180,14 +256,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cGlobalInterfaceParent::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cGlobalInterfaceParent::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h
index 5ad1e1e..bbccc96 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h
@@ -39,8 +39,7 @@
 class V8cGlobalInterfaceParent {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc
index a59977c..92f94ea 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,140 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 26;
+
+
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->IndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->IndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+void IndexedPropertyDeleterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+
+  impl->IndexedDeleter(index);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(v8::Boolean::New(isolate, true));
+}
+
 
 
 void lengthAttributeGetter(
@@ -281,85 +412,240 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "IndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "IndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedDeleterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedDeleter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedGetterMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedDeleter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedDeleterMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "indexedDeleter"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "IndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      IndexedPropertyDeleterCallback,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
   }
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 26;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -369,14 +655,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h
index 530f38a..f80fdc4 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc
index d150da6..22fbc8e 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 27;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,7 +112,6 @@
 
 
 
-
 void getAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
   v8::Local<v8::Object> object = info.This();
@@ -189,68 +193,163 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithAny",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithAny"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getAnyMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getAnyMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "getAny"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setAnyMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setAny"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setAnyMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithAny"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 27;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithAny::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithAny::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -260,14 +359,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithAny::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithAny::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h
index c088986..7f029c4 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithAny {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc
index 27fb410..d707a52 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 28;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,7 +112,6 @@
 
 
 
-
 void getAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
   v8::Local<v8::Object> object = info.This();
@@ -261,90 +265,229 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithAnyDictionary",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithAnyDictionary"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getAnyMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getAnyMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "getAny"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "hasAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, hasAnyMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "hasAny"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "hasAnyDefault");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, hasAnyDefaultMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "hasAnyDefault"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setAnyMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setAny"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, hasAnyMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "hasAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, hasAnyDefaultMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "hasAnyDefault",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setAnyMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithAnyDictionary"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 28;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithAnyDictionary::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithAnyDictionary::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -354,14 +497,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithAnyDictionary::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithAnyDictionary::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h
index af10b0a..124c7ab 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithAnyDictionary {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc
index 0451ae2..4539d83 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 29;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,7 +112,6 @@
 
 
 
-
 void getDateMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
   v8::Local<v8::Object> object = info.This();
@@ -189,68 +193,163 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithDate",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithDate"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getDateMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getDate");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getDateMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getDate",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "getDate"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setDate");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setDateMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setDate"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setDateMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setDate",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithDate"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 29;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithDate::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithDate::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -260,14 +359,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithDate::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithDate::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h
index 9154494..49a1bc0 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithDate {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc
index fd54ff0..c815e50 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 30;
+
+
+
+
+
+
+
 
 
 void supportedAttributeAttributeGetter(
@@ -127,52 +132,130 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithUnsupportedProperties",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithUnsupportedProperties"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "supportedAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    supportedAttributeAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "supportedAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        supportedAttributeAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 30;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithUnsupportedProperties"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithUnsupportedProperties::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithUnsupportedProperties::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -182,14 +265,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithUnsupportedProperties::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithUnsupportedProperties::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h
index 5d68181..dfd519f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithUnsupportedProperties {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc
index fffa517..692da5b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 31;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,46 +112,96 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NamedConstructorInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "NamedConstructorInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NamedConstructorInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 31;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNamedConstructorInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNamedConstructorInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -156,14 +211,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNamedConstructorInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNamedConstructorInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h
index b08e649..76fcdb2 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h
@@ -39,8 +39,7 @@
 class V8cNamedConstructorInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
index 4c63633..9d39173 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,146 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 32;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->NamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->NamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyDeleterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  impl->NamedDeleter(property_name);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
 
 
 
@@ -244,79 +381,207 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NamedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NamedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedDeleterMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedDeleter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedDeleterMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedDeleter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "namedDeleter"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedGetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NamedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      NamedPropertyDeleterCallback,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
   }
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 32;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNamedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNamedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -326,14 +591,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNamedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNamedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h
index 2160453..12f0ec7 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cNamedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
index f90afa4..ff6c171 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,221 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 33;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->NamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->NamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->IndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->IndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -126,6 +338,7 @@
 }
 
 
+
 void propertyOnBaseClassAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -447,114 +660,352 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NamedIndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NamedIndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "propertyOnBaseClass",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    propertyOnBaseClassAttributeGetter
-    ,propertyOnBaseClassAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "propertyOnBaseClass");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        propertyOnBaseClassAttributeGetter,
+        propertyOnBaseClassAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "operationOnBaseClass");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, operationOnBaseClassMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "operationOnBaseClass"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NamedIndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedGetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
   }
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedGetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, operationOnBaseClassMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "operationOnBaseClass",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 33;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNamedIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNamedIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -564,14 +1015,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNamedIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNamedIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h
index f0d55dc..ea787aa 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cNamedIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc
index e61240f..e0f5669 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 34;
+
+
+
+
+
+
+
 
 
 void nestedForwardingAttributeAttributeGetter(
@@ -190,53 +195,130 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NestedPutForwardsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NestedPutForwardsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nestedForwardingAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nestedForwardingAttributeAttributeGetter
-    ,nestedForwardingAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nestedForwardingAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nestedForwardingAttributeAttributeGetter,
+        nestedForwardingAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 34;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NestedPutForwardsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNestedPutForwardsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNestedPutForwardsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -246,14 +328,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNestedPutForwardsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNestedPutForwardsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h
index d052834..25633dc 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h
@@ -39,8 +39,7 @@
 class V8cNestedPutForwardsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc
index ccda656..570064a 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,49 +84,106 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 35;
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NoConstructorInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NoConstructorInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NoConstructorInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 35;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNoConstructorInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNoConstructorInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -139,14 +193,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNoConstructorInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNoConstructorInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h
index b5065a6..7b08cf2 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h
@@ -39,8 +39,7 @@
 class V8cNoConstructorInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc
index 640f64e..3bc962c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,49 +84,106 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 36;
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NoInterfaceObjectInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NoInterfaceObjectInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NoInterfaceObjectInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 36;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNoInterfaceObjectInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNoInterfaceObjectInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -139,14 +193,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNoInterfaceObjectInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNoInterfaceObjectInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h
index cad6f66..77e220c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h
@@ -39,7 +39,7 @@
 class V8cNoInterfaceObjectInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc
index 7cc380e..8ab1042 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 37;
+
+
+
+
+
+
+
 
 
 void nullableBooleanPropertyAttributeGetter(
@@ -167,6 +172,7 @@
   return;
 }
 
+
 void nullableNumericPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -241,6 +247,7 @@
   return;
 }
 
+
 void nullableStringPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -315,6 +322,7 @@
   return;
 }
 
+
 void nullableObjectPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -718,162 +726,496 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NullableTypesTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NullableTypesTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableBooleanProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableBooleanPropertyAttributeGetter
-    ,nullableBooleanPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableNumericProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableNumericPropertyAttributeGetter
-    ,nullableNumericPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableStringProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableStringPropertyAttributeGetter
-    ,nullableStringPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableObjectProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableObjectPropertyAttributeGetter
-    ,nullableObjectPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableBooleanArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableBooleanArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableBooleanProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableBooleanPropertyAttributeGetter,
+        nullableBooleanPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableNumericProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableNumericPropertyAttributeGetter,
+        nullableNumericPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableStringProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableStringPropertyAttributeGetter,
+        nullableStringPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableObjectProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableObjectPropertyAttributeGetter,
+        nullableObjectPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableBooleanOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableBooleanArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableBooleanArgumentMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableBooleanOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "nullableBooleanArgument"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableBooleanOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableBooleanOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableBooleanOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableNumericArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableNumericArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableNumericArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableNumericOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableNumericOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableNumericOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableObjectArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableObjectArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableObjectArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableObjectOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableObjectOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableObjectOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableStringArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableStringArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableStringArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableStringOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableStringOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableStringOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableNumericArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableNumericArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableNumericOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableNumericOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableObjectArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableObjectArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableObjectOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableObjectOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableStringArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableStringArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableStringOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableStringOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NullableTypesTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 37;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNullableTypesTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNullableTypesTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -883,14 +1225,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNullableTypesTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNullableTypesTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.h
index baa094a..0374822 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.h
@@ -39,8 +39,7 @@
 class V8cNullableTypesTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc
index 167152d..e866d90 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 38;
+
+
+
+
+
+
+
 
 
 void bytePropertyAttributeGetter(
@@ -163,6 +168,7 @@
   return;
 }
 
+
 void byteClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -237,6 +243,7 @@
   return;
 }
 
+
 void octetPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -311,6 +318,7 @@
   return;
 }
 
+
 void octetClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -385,6 +393,7 @@
   return;
 }
 
+
 void shortPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -459,6 +468,7 @@
   return;
 }
 
+
 void shortClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -533,6 +543,7 @@
   return;
 }
 
+
 void unsignedShortPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -607,6 +618,7 @@
   return;
 }
 
+
 void unsignedShortClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -681,6 +693,7 @@
   return;
 }
 
+
 void longPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -755,6 +768,7 @@
   return;
 }
 
+
 void longClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -829,6 +843,7 @@
   return;
 }
 
+
 void unsignedLongPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -903,6 +918,7 @@
   return;
 }
 
+
 void unsignedLongClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -977,6 +993,7 @@
   return;
 }
 
+
 void longLongPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -1051,6 +1068,7 @@
   return;
 }
 
+
 void longLongClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -1125,6 +1143,7 @@
   return;
 }
 
+
 void unsignedLongLongPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -1199,6 +1218,7 @@
   return;
 }
 
+
 void unsignedLongLongClampPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -1273,6 +1293,7 @@
   return;
 }
 
+
 void doublePropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -1347,6 +1368,7 @@
   return;
 }
 
+
 void unrestrictedDoublePropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -2242,392 +2264,1368 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NumericTypesTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NumericTypesTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "byteProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    bytePropertyAttributeGetter
-    ,bytePropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "byteClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    byteClampPropertyAttributeGetter
-    ,byteClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "octetProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    octetPropertyAttributeGetter
-    ,octetPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "octetClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    octetClampPropertyAttributeGetter
-    ,octetClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "shortProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    shortPropertyAttributeGetter
-    ,shortPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "shortClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    shortClampPropertyAttributeGetter
-    ,shortClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unsignedShortProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unsignedShortPropertyAttributeGetter
-    ,unsignedShortPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unsignedShortClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unsignedShortClampPropertyAttributeGetter
-    ,unsignedShortClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "longProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    longPropertyAttributeGetter
-    ,longPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "longClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    longClampPropertyAttributeGetter
-    ,longClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unsignedLongProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unsignedLongPropertyAttributeGetter
-    ,unsignedLongPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unsignedLongClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unsignedLongClampPropertyAttributeGetter
-    ,unsignedLongClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "longLongProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    longLongPropertyAttributeGetter
-    ,longLongPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "longLongClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    longLongClampPropertyAttributeGetter
-    ,longLongClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unsignedLongLongProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unsignedLongLongPropertyAttributeGetter
-    ,unsignedLongLongPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unsignedLongLongClampProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unsignedLongLongClampPropertyAttributeGetter
-    ,unsignedLongLongClampPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "doubleProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    doublePropertyAttributeGetter
-    ,doublePropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unrestrictedDoubleProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unrestrictedDoublePropertyAttributeGetter
-    ,unrestrictedDoublePropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, byteArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "byteArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "byteProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        bytePropertyAttributeGetter,
+        bytePropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "byteClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        byteClampPropertyAttributeGetter,
+        byteClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "octetProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        octetPropertyAttributeGetter,
+        octetPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "octetClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        octetClampPropertyAttributeGetter,
+        octetClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "shortProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        shortPropertyAttributeGetter,
+        shortPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "shortClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        shortClampPropertyAttributeGetter,
+        shortClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedShortProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unsignedShortPropertyAttributeGetter,
+        unsignedShortPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedShortClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unsignedShortClampPropertyAttributeGetter,
+        unsignedShortClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        longPropertyAttributeGetter,
+        longPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        longClampPropertyAttributeGetter,
+        longClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unsignedLongPropertyAttributeGetter,
+        unsignedLongPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unsignedLongClampPropertyAttributeGetter,
+        unsignedLongClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longLongProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        longLongPropertyAttributeGetter,
+        longLongPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longLongClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        longLongClampPropertyAttributeGetter,
+        longLongClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongLongProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unsignedLongLongPropertyAttributeGetter,
+        unsignedLongLongPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongLongClampProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unsignedLongLongClampPropertyAttributeGetter,
+        unsignedLongLongClampPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "doubleProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        doublePropertyAttributeGetter,
+        doublePropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unrestrictedDoubleProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unrestrictedDoublePropertyAttributeGetter,
+        unrestrictedDoublePropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, byteReturnOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "byteArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, byteArgumentOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "byteReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "byteArgumentOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "byteReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, byteReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "byteReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "doubleArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, doubleArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "doubleArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "doubleReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, doubleReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "doubleReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, longArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "longArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longLongArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, longLongArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "longLongArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longLongReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, longLongReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "longLongReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, longReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "longReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "octetArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, octetArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "octetArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "octetReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, octetReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "octetReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "shortArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, shortArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "shortArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "shortReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, shortReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "shortReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unrestrictedDoubleArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unrestrictedDoubleArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "unrestrictedDoubleArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unrestrictedDoubleReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unrestrictedDoubleReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "unrestrictedDoubleReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unsignedLongArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "unsignedLongArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongLongArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unsignedLongLongArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "unsignedLongLongArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongLongReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unsignedLongLongReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "unsignedLongLongReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedLongReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unsignedLongReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "unsignedLongReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedShortArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unsignedShortArgumentOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "unsignedShortArgumentOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unsignedShortReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, unsignedShortReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "unsignedShortReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, doubleArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "doubleArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, doubleReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "doubleReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "longArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longLongArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "longLongArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longLongReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "longLongReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "longReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, octetArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "octetArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, octetReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "octetReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, shortArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "shortArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, shortReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "shortReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unrestrictedDoubleArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unrestrictedDoubleArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unrestrictedDoubleReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unrestrictedDoubleReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unsignedLongArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongLongArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unsignedLongLongArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongLongReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unsignedLongLongReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unsignedLongReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedShortArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unsignedShortArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedShortReturnOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "unsignedShortReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NumericTypesTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 38;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNumericTypesTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNumericTypesTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -2637,14 +3635,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNumericTypesTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNumericTypesTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.h
index 5f80b25..1a65152 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.h
@@ -39,8 +39,7 @@
 class V8cNumericTypesTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc
index f99b365..de2e838 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc
@@ -39,12 +39,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -70,7 +72,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -78,18 +79,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -99,6 +96,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 39;
+
+
+
+
+
+
+
 
 
 void arbitraryObjectAttributeGetter(
@@ -175,6 +180,7 @@
   return;
 }
 
+
 void baseInterfaceAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -212,6 +218,7 @@
 }
 
 
+
 void derivedInterfaceAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -286,6 +293,7 @@
   return;
 }
 
+
 void objectPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -361,73 +369,232 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ObjectTypeBindingsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ObjectTypeBindingsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "arbitraryObject",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    arbitraryObjectAttributeGetter
-    ,arbitraryObjectAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "baseInterface",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    baseInterfaceAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "derivedInterface",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    derivedInterfaceAttributeGetter
-    ,derivedInterfaceAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "objectProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    objectPropertyAttributeGetter
-    ,objectPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "arbitraryObject");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        arbitraryObjectAttributeGetter,
+        arbitraryObjectAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 39;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "baseInterface");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        baseInterfaceAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedInterface");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        derivedInterfaceAttributeGetter,
+        derivedInterfaceAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "objectProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        objectPropertyAttributeGetter,
+        objectPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ObjectTypeBindingsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cObjectTypeBindingsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cObjectTypeBindingsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -437,14 +604,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cObjectTypeBindingsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cObjectTypeBindingsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.h
index b17754e..7ddbe67 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.h
@@ -39,8 +39,7 @@
 class V8cObjectTypeBindingsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc
index 4d404e0..93a3e46 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,13 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 40;
+
+
+
+
+
+
 
 
 
@@ -1244,200 +1248,592 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "OperationsTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "OperationsTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longFunctionNoArgsMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longFunctionNoArgs");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, longFunctionNoArgsMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "longFunctionNoArgs",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "longFunctionNoArgs"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "objectFunctionNoArgs");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, objectFunctionNoArgsMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "objectFunctionNoArgs"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "optionalArgumentWithDefault");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, optionalArgumentWithDefaultMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "optionalArgumentWithDefault"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "optionalArguments");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, optionalArgumentsMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "optionalArguments"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "optionalNullableArgumentsWithDefaults");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, optionalNullableArgumentsWithDefaultsMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "optionalNullableArgumentsWithDefaults"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "overloadedFunction");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, overloadedFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "overloadedFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "overloadedNullable");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, overloadedNullableMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "overloadedNullable"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "stringFunctionNoArgs");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, stringFunctionNoArgsMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "stringFunctionNoArgs"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "variadicPrimitiveArguments");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, variadicPrimitiveArgumentsMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "variadicPrimitiveArguments"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "variadicStringArgumentsAfterOptionalArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, variadicStringArgumentsAfterOptionalArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "variadicStringArgumentsAfterOptionalArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "voidFunctionLongArg");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, voidFunctionLongArgMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "voidFunctionLongArg"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "voidFunctionNoArgs");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, voidFunctionNoArgsMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "voidFunctionNoArgs"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "voidFunctionObjectArg");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, voidFunctionObjectArgMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "voidFunctionObjectArg"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "voidFunctionStringArg");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, voidFunctionStringArgMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "voidFunctionStringArg"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "overloadedFunction");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // If the operation is static, then the property exists on the interface
+    // object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, overloadedFunctionStaticMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    function_template->Set(
+        NewInternalString(isolate, "overloadedFunction"),
+        method_template);
+
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, objectFunctionNoArgsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "objectFunctionNoArgs",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalArgumentWithDefaultMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "optionalArgumentWithDefault",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalArgumentsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "optionalArguments",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalNullableArgumentsWithDefaultsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "optionalNullableArgumentsWithDefaults",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, overloadedFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "overloadedFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, overloadedNullableMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "overloadedNullable",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, stringFunctionNoArgsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "stringFunctionNoArgs",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, variadicPrimitiveArgumentsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "variadicPrimitiveArguments",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, variadicStringArgumentsAfterOptionalArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "variadicStringArgumentsAfterOptionalArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionLongArgMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "voidFunctionLongArg",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionNoArgsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "voidFunctionNoArgs",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionObjectArgMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "voidFunctionObjectArg",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionStringArgMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "voidFunctionStringArg",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "OperationsTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 40;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cOperationsTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cOperationsTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -1447,14 +1843,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cOperationsTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cOperationsTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.h
index 782fac0..7d1fe92 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.h
@@ -39,8 +39,7 @@
 class V8cOperationsTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc
index f506f7c..88abc4d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,13 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 41;
+
+
+
+
+
+
 
 
 
@@ -264,101 +268,261 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "PromiseInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "PromiseInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, onSuccessMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "onSuccess");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, onSuccessMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "onSuccess",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "onSuccess"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "returnBooleanPromise");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, returnBooleanPromiseMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "returnBooleanPromise"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "returnInterfacePromise");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, returnInterfacePromiseMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "returnInterfacePromise"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "returnStringPromise");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, returnStringPromiseMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "returnStringPromise"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "returnVoidPromise");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, returnVoidPromiseMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "returnVoidPromise"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnBooleanPromiseMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "returnBooleanPromise",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnInterfacePromiseMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "returnInterfacePromise",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnStringPromiseMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "returnStringPromise",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnVoidPromiseMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "returnVoidPromise",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "PromiseInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 41;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cPromiseInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cPromiseInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -368,14 +532,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cPromiseInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cPromiseInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.h
index dc20fe6..e533a30 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.h
@@ -39,8 +39,7 @@
 class V8cPromiseInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc
index 3b85279..ee735c0 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 42;
+
+
+
+
+
+
+
 
 
 void forwardingAttributeAttributeGetter(
@@ -178,6 +183,7 @@
   } // End scope of scoped_refptr<ArbitraryInterface> forwarded_impl.
 }
 
+
 void staticForwardingAttributeStaticAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -231,53 +237,168 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "PutForwardsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "PutForwardsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "forwardingAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    forwardingAttributeAttributeGetter
-    ,forwardingAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "forwardingAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        forwardingAttributeAttributeGetter,
+        forwardingAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 42;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "staticForwardingAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Operations installed on the interface object must be static methods, so
+    // no need to specify a signature, i.e. no need to do type check against a
+    // holder.
+
+    // If the attribute is a static attribute, then there is a single
+    // corresponding property and it exists on the interface's interface object.
+    function_template->SetNativeDataProperty(
+        name,
+        staticForwardingAttributeStaticAttributeGetter,
+        staticForwardingAttributeStaticAttributeSetter,
+        v8::Local<v8::Value>(),
+        attributes);
+
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "PutForwardsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cPutForwardsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cPutForwardsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -287,14 +408,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cPutForwardsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cPutForwardsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.h
index 7d6e2da..093d146 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.h
@@ -39,8 +39,7 @@
 class V8cPutForwardsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc
index 465ef2e..ec73494 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 43;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -111,7 +116,6 @@
 
 
 
-
 void getInterfaceSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
   v8::Local<v8::Object> object = info.This();
@@ -685,200 +689,559 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "SequenceUser",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "SequenceUser"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getInterfaceSequenceMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getInterfaceSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getInterfaceSequenceMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getInterfaceSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "getInterfaceSequence"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getInterfaceSequenceSequenceSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getInterfaceSequenceSequenceSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "getInterfaceSequenceSequenceSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getLongSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getLongSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "getLongSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getStringSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getStringSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "getStringSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getStringSequenceSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getStringSequenceSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "getStringSequenceSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getUnionOfStringAndStringSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getUnionOfStringAndStringSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "getUnionOfStringAndStringSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getUnionSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getUnionSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "getUnionSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setInterfaceSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setInterfaceSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setInterfaceSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setInterfaceSequenceSequenceSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setInterfaceSequenceSequenceSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setInterfaceSequenceSequenceSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setLongSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setLongSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setLongSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setStringSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setStringSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setStringSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setStringSequenceSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setStringSequenceSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setStringSequenceSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setUnionOfStringAndStringSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setUnionOfStringAndStringSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setUnionOfStringAndStringSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setUnionSequence");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setUnionSequenceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setUnionSequence"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getInterfaceSequenceSequenceSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getInterfaceSequenceSequenceSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getLongSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getLongSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getStringSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getStringSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getStringSequenceSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getStringSequenceSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getUnionOfStringAndStringSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getUnionOfStringAndStringSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getUnionSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getUnionSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setInterfaceSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setInterfaceSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setInterfaceSequenceSequenceSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setInterfaceSequenceSequenceSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setLongSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setLongSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setStringSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setStringSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setStringSequenceSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setStringSequenceSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setUnionOfStringAndStringSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setUnionOfStringAndStringSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setUnionSequenceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setUnionSequence",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "SequenceUser"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 43;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cSequenceUser::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cSequenceUser::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -888,14 +1251,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cSequenceUser::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cSequenceUser::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.h
index b13e2e9..48eac36 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.h
@@ -39,8 +39,7 @@
 class V8cSequenceUser {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc
index 3ff1a99..f5b78d7 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc
@@ -26,6 +26,7 @@
 
 #include "cobalt/script/logging_exception_state.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/v8c_callback_interface.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
 #include "v8/include/v8.h"
@@ -40,6 +41,7 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::FromJSValue;
 using cobalt::script::v8c::GetCallableForCallbackInterface;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::V8cGlobalEnvironment;
 }  // namespace
@@ -52,53 +54,52 @@
     const scoped_refptr<script::Wrappable>& callback_this,
     const scoped_refptr<ArbitraryInterface>& value,
     bool* had_exception) const {
+  bool success = false;
   base::optional<int32_t > cobalt_return_value;
 
-  DCHECK(!this->IsEmpty());
   DCHECK(isolate_);
+  if (this->IsEmpty()) {
+    goto done;
+  }
+  {
+    EntryScope entry_scope(isolate_);
+    v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+    v8::TryCatch try_catch(isolate_);
 
-  EntryScope entry_scope(isolate_);
-  v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+    v8::Local<v8::Value> implementing_value = this->NewLocal(isolate_);
+    if (!implementing_value->IsObject()) {
+      LOG(WARNING) << "Implementing object is NULL";
+      goto done;
+    }
 
-  v8::MaybeLocal<v8::Object> maybe_implementing_object = this->NewLocal(isolate_)->ToObject(context);
-  v8::Local<v8::Object> implementing_object;
-  if (!maybe_implementing_object.ToLocal(&implementing_object)) {
-    *had_exception = true;
-    return cobalt_return_value;
+    v8::Local<v8::Function> callable;
+    if (!GetCallableForCallbackInterface(
+             isolate_, implementing_value.As<v8::Object>(),
+             NewInternalString(isolate_, "handleCallback"))
+             .ToLocal(&callable)) {
+      goto done;
+    }
+
+    v8::Local<v8::Value> this_value;
+    ToJSValue(isolate_, callback_this, &this_value);
+
+    const int kNumArguments = 1;
+    v8::Local<v8::Value> argv[kNumArguments];
+    ToJSValue(isolate_, value, &argv[0]);
+
+    v8::Local<v8::Value> return_value;
+    if (!callable->Call(context, this_value, kNumArguments, argv)
+             .ToLocal(&return_value)) {
+      goto done;
+    }
+    LoggingExceptionState exception_state;
+    FromJSValue(isolate_, return_value, 0, &exception_state,
+                &cobalt_return_value);
+    success = !exception_state.is_exception_set();
   }
 
-  v8::MaybeLocal<v8::Object> maybe_callable =
-      GetCallableForCallbackInterface(isolate_, implementing_object, "handleCallback");
-  v8::Local<v8::Object> callable;
-  if (!maybe_callable.ToLocal(&callable)) {
-    NOTIMPLEMENTED();
-    *had_exception = true;
-    return cobalt_return_value;
-  }
-  DCHECK(callable->IsCallable());
-
-  v8::Local<v8::Value> this_value;
-  ToJSValue(isolate_, callback_this, &this_value);
-
-  const int kNumArguments = 1;
-  v8::Local<v8::Value> argv[kNumArguments];
-
-  ToJSValue(isolate_, value, &argv[0]);
-
-  v8::MaybeLocal<v8::Value> maybe_return_value =
-      callable->CallAsFunction(isolate_->GetCurrentContext(), this_value, kNumArguments, argv);
-  v8::Local<v8::Value> return_value;
-  if (!maybe_return_value.ToLocal(&return_value)) {
-    *had_exception = true;
-  return cobalt_return_value;
-  }
-
-  LoggingExceptionState exception_state;
-  FromJSValue(isolate_, return_value, 0, &exception_state, &cobalt_return_value);
-  if (exception_state.is_exception_set()) {
-    *had_exception = true;
-  }
-
+done:
+  *had_exception = !success;
   return cobalt_return_value;
 
 }
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc
index f454dde..491198b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 45;
+
+
+
+
+
+
+
 
 
 void staticAttributeStaticAttributeGetter(
@@ -370,46 +375,168 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "StaticPropertiesInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "StaticPropertiesInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "staticAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Operations installed on the interface object must be static methods, so
+    // no need to specify a signature, i.e. no need to do type check against a
+    // holder.
+
+    // If the attribute is a static attribute, then there is a single
+    // corresponding property and it exists on the interface's interface object.
+    function_template->SetNativeDataProperty(
+        name,
+        staticAttributeStaticAttributeGetter,
+        staticAttributeStaticAttributeSetter,
+        v8::Local<v8::Value>(),
+        attributes);
+
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "staticFunction");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // If the operation is static, then the property exists on the interface
+    // object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, staticFunctionStaticMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    function_template->Set(
+        NewInternalString(isolate, "staticFunction"),
+        method_template);
+
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "StaticPropertiesInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 45;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cStaticPropertiesInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cStaticPropertiesInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -419,14 +546,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cStaticPropertiesInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cStaticPropertiesInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.h
index c95919c..4a767c1 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.h
@@ -39,8 +39,7 @@
 class V8cStaticPropertiesInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc
index b3bc42b..2afe72f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,49 +84,145 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 46;
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "StringifierAnonymousOperationInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+
+void Stringifier(v8::Local<v8::String> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state(isolate);
+
+    V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<StringifierAnonymousOperationInterface>())) {
+    V8cExceptionState exception(isolate);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return;
+  }
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+
+  // |WrapperPrivate::GetFromObject| can fail if |object| is not a |Wrapper|
+  // object.
+  if (!wrapper_private) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    return;
+  }
+
+  StringifierAnonymousOperationInterface* impl =
+      wrapper_private->wrappable<StringifierAnonymousOperationInterface>().get();
+  if (!impl) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    NOTREACHED();
+    return;
+  }
+  std::string stringified = impl->AnonymousStringifier();
+
+  v8::Local<v8::Value> v8_stringified;
+  ToJSValue(isolate, stringified, &v8_stringified);
+
+  info.GetReturnValue().Set(v8_stringified);
+}
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "StringifierAnonymousOperationInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "StringifierAnonymousOperationInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 46;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cStringifierAnonymousOperationInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cStringifierAnonymousOperationInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -139,14 +232,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cStringifierAnonymousOperationInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cStringifierAnonymousOperationInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.h
index 2034125..250fba9 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.h
@@ -39,8 +39,7 @@
 class V8cStringifierAnonymousOperationInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc
index 9b3a293..bdfb6ec 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 47;
+
+
+
+
+
+
+
 
 
 void theStringifierAttributeAttributeGetter(
@@ -164,53 +169,169 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "StringifierAttributeInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+void Stringifier(v8::Local<v8::String> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state(isolate);
+
+    V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<StringifierAttributeInterface>())) {
+    V8cExceptionState exception(isolate);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return;
+  }
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+
+  // |WrapperPrivate::GetFromObject| can fail if |object| is not a |Wrapper|
+  // object.
+  if (!wrapper_private) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    return;
+  }
+
+  StringifierAttributeInterface* impl =
+      wrapper_private->wrappable<StringifierAttributeInterface>().get();
+  if (!impl) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    NOTREACHED();
+    return;
+  }
+  std::string stringified = impl->the_stringifier_attribute();
+
+  v8::Local<v8::Value> v8_stringified;
+  ToJSValue(isolate, stringified, &v8_stringified);
+
+  info.GetReturnValue().Set(v8_stringified);
+}
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "StringifierAttributeInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "theStringifierAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    theStringifierAttributeAttributeGetter
-    ,theStringifierAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "theStringifierAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        theStringifierAttributeAttributeGetter,
+        theStringifierAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 47;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "StringifierAttributeInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cStringifierAttributeInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cStringifierAttributeInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -220,14 +341,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cStringifierAttributeInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cStringifierAttributeInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.h
index 2af0546..954bea1 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.h
@@ -39,8 +39,7 @@
 class V8cStringifierAttributeInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc
index 3096de4..3954c44 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,13 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 48;
+
+
+
+
+
+
 
 
 
@@ -126,57 +130,168 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "StringifierOperationInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+void Stringifier(v8::Local<v8::String> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state(isolate);
+
+    V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<StringifierOperationInterface>())) {
+    V8cExceptionState exception(isolate);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return;
+  }
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+
+  // |WrapperPrivate::GetFromObject| can fail if |object| is not a |Wrapper|
+  // object.
+  if (!wrapper_private) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    return;
+  }
+
+  StringifierOperationInterface* impl =
+      wrapper_private->wrappable<StringifierOperationInterface>().get();
+  if (!impl) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    NOTREACHED();
+    return;
+  }
+  std::string stringified = impl->TheStringifierOperation();
+
+  v8::Local<v8::Value> v8_stringified;
+  ToJSValue(isolate, stringified, &v8_stringified);
+
+  info.GetReturnValue().Set(v8_stringified);
+}
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "StringifierOperationInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, theStringifierOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "theStringifierOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, theStringifierOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "theStringifierOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "theStringifierOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "StringifierOperationInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 48;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cStringifierOperationInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cStringifierOperationInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -186,14 +301,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cStringifierOperationInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cStringifierOperationInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.h
index d8bde96..8cbb7df 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.h
@@ -39,8 +39,7 @@
 class V8cStringifierOperationInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc
index 70950ab..5e0cdbf 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,13 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 49;
+
+
+
+
+
+
 
 
 
@@ -150,68 +154,162 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "TargetInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "TargetInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, implementedInterfaceFunctionMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "implementedInterfaceFunction");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, implementedInterfaceFunctionMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "implementedInterfaceFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "implementedInterfaceFunction"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "partialInterfaceFunction");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, partialInterfaceFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "partialInterfaceFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, partialInterfaceFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "partialInterfaceFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "TargetInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 49;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cTargetInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cTargetInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -221,14 +319,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cTargetInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cTargetInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.h
index 43d3d35..5ebf82e 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.h
@@ -39,8 +39,7 @@
 class V8cTargetInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc
index 37d454c..2d077b1 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc
@@ -25,6 +25,7 @@
 #include "v8c_gen_type_conversion.h"
 #include "base/logging.h"
 #include "cobalt/script/exception_state.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "v8/include/v8.h"
 
 using cobalt::bindings::testing::TestEnum;
@@ -82,37 +83,37 @@
   bool match = false;
 // 3. Return the enumeration value of type E that is equal to S.
  if (
-      v8::String::NewFromUtf8(isolate, "alpha", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "alpha")->Equals(value))
   {
     *out_enum = cobalt::bindings::testing::kTestEnumAlpha;
   }
  else  if (
-      v8::String::NewFromUtf8(isolate, "beta", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "beta")->Equals(value))
   {
     *out_enum = cobalt::bindings::testing::kTestEnumBeta;
   }
  else  if (
-      v8::String::NewFromUtf8(isolate, "gamma", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "gamma")->Equals(value))
   {
     *out_enum = cobalt::bindings::testing::kTestEnumGamma;
   }
  else  if (
-      v8::String::NewFromUtf8(isolate, "enum-with-dashes", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "enum-with-dashes")->Equals(value))
   {
     *out_enum = cobalt::bindings::testing::kTestEnumEnumWithDashes;
   }
  else  if (
-      v8::String::NewFromUtf8(isolate, "enum with spaces", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "enum with spaces")->Equals(value))
   {
     *out_enum = cobalt::bindings::testing::kTestEnumEnumWithSpaces;
   }
  else  if (
-      v8::String::NewFromUtf8(isolate, "terrible----enum", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "terrible----enum")->Equals(value))
   {
     *out_enum = cobalt::bindings::testing::kTestEnumTerribleEnum;
   }
  else  if (
-      v8::String::NewFromUtf8(isolate, "this is a terrible @#$%#$% enum", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "this is a terrible @#$%#$% enum")->Equals(value))
   {
     *out_enum = cobalt::bindings::testing::kTestEnumThisIsATerribleEnum;
   }
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc
index 1322f56..4700703 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc
@@ -37,12 +37,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -66,7 +68,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -74,18 +75,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -95,6 +92,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 51;
+
+
+
+
+
+
+
 
 
 void unionPropertyAttributeGetter(
@@ -171,6 +176,7 @@
   return;
 }
 
+
 void unionWithNullableMemberPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -245,6 +251,7 @@
   return;
 }
 
+
 void nullableUnionPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -319,6 +326,7 @@
   return;
 }
 
+
 void unionBasePropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -394,74 +402,232 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "UnionTypesInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "UnionTypesInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unionProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unionPropertyAttributeGetter
-    ,unionPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unionWithNullableMemberProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unionWithNullableMemberPropertyAttributeGetter
-    ,unionWithNullableMemberPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableUnionProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableUnionPropertyAttributeGetter
-    ,nullableUnionPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "unionBaseProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    unionBasePropertyAttributeGetter
-    ,unionBasePropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unionProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unionPropertyAttributeGetter,
+        unionPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 51;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unionWithNullableMemberProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unionWithNullableMemberPropertyAttributeGetter,
+        unionWithNullableMemberPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableUnionProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableUnionPropertyAttributeGetter,
+        nullableUnionPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "unionBaseProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        unionBasePropertyAttributeGetter,
+        unionBasePropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "UnionTypesInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cUnionTypesInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cUnionTypesInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -471,14 +637,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cUnionTypesInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cUnionTypesInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.h
index 515c0be..416e149 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.h
@@ -39,8 +39,7 @@
 class V8cUnionTypesInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc
index e83ec7a..f50864a 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc
@@ -133,12 +133,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -266,7 +268,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -274,18 +275,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -295,6 +292,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 53;
+
+
+
+
+
+
+
 
 
 void windowPropertyAttributeGetter(
@@ -371,6 +376,7 @@
   return;
 }
 
+
 void windowAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -408,6 +414,7 @@
 }
 
 
+
 void onEventAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -636,118 +643,322 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "Window",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "Window"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-  v8::Local<v8::FunctionTemplate> parent_template = V8cGlobalInterfaceParent::CreateTemplate(isolate);
-  function_template->Inherit(parent_template);
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
-
-
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "windowProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    windowPropertyAttributeGetter
-    ,windowPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "window",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    windowAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "onEvent",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    onEventAttributeGetter
-    ,onEventAttributeSetter
-  );
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getStackTraceMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getStackTrace",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // An interface can be defined to inherit from another interface. If the
+    // identifier of the interface is followed by a U+003A COLON (":") character
+    // and an identifier, then that identifier identifies the inherited
+    // interface. An object that implements an interface that inherits from
+    // another also implements that inherited interface. The object therefore
+    // will also have members that correspond to the interface members from the
+    // inherited interface.
+    v8::Local<v8::FunctionTemplate> parent_template = V8cGlobalInterfaceParent::GetTemplate(isolate);
+    function_template->Inherit(parent_template);
   }
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setTimeoutMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setTimeout",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "windowProperty");
 
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the attribute is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    instance_template->SetAccessor(
+        name,
+        windowPropertyAttributeGetter,
+        windowPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+
+  }
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, windowOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "windowOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "window");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the attribute is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    instance_template->SetAccessor(
+        name,
+        windowAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "onEvent");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the attribute is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    instance_template->SetAccessor(
+        name,
+        onEventAttributeGetter,
+        onEventAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getStackTrace");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 53;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, if the operation is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getStackTraceMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    instance_template->Set(
+        NewInternalString(isolate, "getStackTrace"),
+        method_template);
+
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setTimeout");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the operation is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setTimeoutMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    instance_template->Set(
+        NewInternalString(isolate, "setTimeout"),
+        method_template);
+
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "windowOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the operation is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, windowOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    instance_template->Set(
+        NewInternalString(isolate, "windowOperation"),
+        method_template);
+
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "Window"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cWindow::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
-  EscapableEntryScope entry_scope(isolate);
-  v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
-  DCHECK(!interface_data->function_template.IsEmpty());
-
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
-  DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
-  v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
-  DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
-
-  // This |WrapperPrivate|'s lifetime will be managed by V8.
-  new WrapperPrivate(isolate, wrappable, object);
-  return entry_scope.Escape(object);
+// The global interface is special.  Just give them the global object proxy.
+v8::Local<v8::Object> V8cWindow::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>&) {
+  return isolate->GetCurrentContext()->Global();
 }
 
-v8::Local<v8::FunctionTemplate> V8cWindow::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cWindow::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
@@ -764,260 +975,11 @@
 void V8cGlobalEnvironment::CreateGlobalObject(
     const scoped_refptr<GlobalInterface>& global_interface,
     EnvironmentSettings* environment_settings) {
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::CreateGlobalObject()");
   // Intentionally not an |EntryScope|, since the context doesn't exist yet.
   v8::Isolate::Scope isolate_scope(isolate_);
   v8::HandleScope handle_scope(isolate_);
-  v8::Local<v8::ObjectTemplate> global_object_template = V8cWindow::CreateTemplate(isolate_)->InstanceTemplate();
-
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "AnonymousIndexedGetterInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cAnonymousIndexedGetterInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "AnonymousNamedGetterInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cAnonymousNamedGetterInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "AnonymousNamedIndexedGetterInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cAnonymousNamedIndexedGetterInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ArbitraryInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cArbitraryInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "BaseInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cBaseInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "BooleanTypeTestInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cBooleanTypeTestInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "CallbackFunctionInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cCallbackFunctionInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "CallbackInterfaceInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cCallbackInterfaceInterface::CreateTemplate(isolate_));
-#if defined(ENABLE_CONDITIONAL_INTERFACE)
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ConditionalInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cConditionalInterface::CreateTemplate(isolate_));
-#endif  // defined(ENABLE_CONDITIONAL_INTERFACE)
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ConstantsInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cConstantsInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ConstructorInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cConstructorInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ConstructorWithArgumentsInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cConstructorWithArgumentsInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "DOMStringTestInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cDOMStringTestInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "DerivedGetterSetterInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cDerivedGetterSetterInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "DerivedInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cDerivedInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "DictionaryInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cDictionaryInterface::CreateTemplate(isolate_));
-#if defined(NO_ENABLE_CONDITIONAL_INTERFACE)
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "DisabledInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cDisabledInterface::CreateTemplate(isolate_));
-#endif  // defined(NO_ENABLE_CONDITIONAL_INTERFACE)
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "EnumerationInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cEnumerationInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ExceptionObjectInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cExceptionObjectInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ExceptionsInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cExceptionsInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ExtendedIDLAttributesInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cExtendedIDLAttributesInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "GarbageCollectionTestInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cGarbageCollectionTestInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "GlobalInterfaceParent",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cGlobalInterfaceParent::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ImplementedInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cImplementedInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "IndexedGetterInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cIndexedGetterInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "InterfaceWithAny",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cInterfaceWithAny::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "InterfaceWithAnyDictionary",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cInterfaceWithAnyDictionary::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "InterfaceWithDate",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cInterfaceWithDate::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "InterfaceWithUnsupportedProperties",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cInterfaceWithUnsupportedProperties::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NamedConstructorInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNamedConstructorInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NamedGetterInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNamedGetterInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NamedIndexedGetterInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNamedIndexedGetterInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NestedPutForwardsInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNestedPutForwardsInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NoConstructorInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNoConstructorInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NoInterfaceObjectInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNoInterfaceObjectInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NullableTypesTestInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNullableTypesTestInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "NumericTypesTestInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cNumericTypesTestInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "ObjectTypeBindingsInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cObjectTypeBindingsInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "OperationsTestInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cOperationsTestInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "PromiseInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cPromiseInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "PutForwardsInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cPutForwardsInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "SequenceUser",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cSequenceUser::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "StaticPropertiesInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cStaticPropertiesInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "StringifierAnonymousOperationInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cStringifierAnonymousOperationInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "StringifierAttributeInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cStringifierAttributeInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "StringifierOperationInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cStringifierOperationInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "TargetInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cTargetInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "UnionTypesInterface",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cUnionTypesInterface::CreateTemplate(isolate_));
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "Window",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8cWindow::CreateTemplate(isolate_));
+  v8::Local<v8::ObjectTemplate> global_object_template = V8cWindow::GetTemplate(isolate_)->InstanceTemplate();
 
   v8::Local<v8::Context> context =
       v8::Context::New(isolate_, nullptr, global_object_template);
@@ -1029,207 +991,212 @@
   environment_settings_ = environment_settings;
   EvaluateAutomatics();
 
+  v8::Local<v8::Object> global_object = context->Global();
+  new WrapperPrivate(isolate_, global_interface, global_object);
+
+  auto actual_global_object = global_object->GetPrototype()->ToObject();
+  new WrapperPrivate(isolate_, global_interface, actual_global_object);
+
   wrapper_factory_->RegisterWrappableType(
       AnonymousIndexedGetterInterface::AnonymousIndexedGetterInterfaceWrappableType(),
       base::Bind(V8cAnonymousIndexedGetterInterface::CreateWrapper),
-      base::Bind(V8cAnonymousIndexedGetterInterface::CreateTemplate));
+      base::Bind(V8cAnonymousIndexedGetterInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       AnonymousNamedGetterInterface::AnonymousNamedGetterInterfaceWrappableType(),
       base::Bind(V8cAnonymousNamedGetterInterface::CreateWrapper),
-      base::Bind(V8cAnonymousNamedGetterInterface::CreateTemplate));
+      base::Bind(V8cAnonymousNamedGetterInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       AnonymousNamedIndexedGetterInterface::AnonymousNamedIndexedGetterInterfaceWrappableType(),
       base::Bind(V8cAnonymousNamedIndexedGetterInterface::CreateWrapper),
-      base::Bind(V8cAnonymousNamedIndexedGetterInterface::CreateTemplate));
+      base::Bind(V8cAnonymousNamedIndexedGetterInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ArbitraryInterface::ArbitraryInterfaceWrappableType(),
       base::Bind(V8cArbitraryInterface::CreateWrapper),
-      base::Bind(V8cArbitraryInterface::CreateTemplate));
+      base::Bind(V8cArbitraryInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       BaseInterface::BaseInterfaceWrappableType(),
       base::Bind(V8cBaseInterface::CreateWrapper),
-      base::Bind(V8cBaseInterface::CreateTemplate));
+      base::Bind(V8cBaseInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       BooleanTypeTestInterface::BooleanTypeTestInterfaceWrappableType(),
       base::Bind(V8cBooleanTypeTestInterface::CreateWrapper),
-      base::Bind(V8cBooleanTypeTestInterface::CreateTemplate));
+      base::Bind(V8cBooleanTypeTestInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       CallbackFunctionInterface::CallbackFunctionInterfaceWrappableType(),
       base::Bind(V8cCallbackFunctionInterface::CreateWrapper),
-      base::Bind(V8cCallbackFunctionInterface::CreateTemplate));
+      base::Bind(V8cCallbackFunctionInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       CallbackInterfaceInterface::CallbackInterfaceInterfaceWrappableType(),
       base::Bind(V8cCallbackInterfaceInterface::CreateWrapper),
-      base::Bind(V8cCallbackInterfaceInterface::CreateTemplate));
+      base::Bind(V8cCallbackInterfaceInterface::GetTemplate));
 #if defined(ENABLE_CONDITIONAL_INTERFACE)
   wrapper_factory_->RegisterWrappableType(
       ConditionalInterface::ConditionalInterfaceWrappableType(),
       base::Bind(V8cConditionalInterface::CreateWrapper),
-      base::Bind(V8cConditionalInterface::CreateTemplate));
+      base::Bind(V8cConditionalInterface::GetTemplate));
 #endif  // defined(ENABLE_CONDITIONAL_INTERFACE)
   wrapper_factory_->RegisterWrappableType(
       ConstantsInterface::ConstantsInterfaceWrappableType(),
       base::Bind(V8cConstantsInterface::CreateWrapper),
-      base::Bind(V8cConstantsInterface::CreateTemplate));
+      base::Bind(V8cConstantsInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ConstructorInterface::ConstructorInterfaceWrappableType(),
       base::Bind(V8cConstructorInterface::CreateWrapper),
-      base::Bind(V8cConstructorInterface::CreateTemplate));
+      base::Bind(V8cConstructorInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ConstructorWithArgumentsInterface::ConstructorWithArgumentsInterfaceWrappableType(),
       base::Bind(V8cConstructorWithArgumentsInterface::CreateWrapper),
-      base::Bind(V8cConstructorWithArgumentsInterface::CreateTemplate));
+      base::Bind(V8cConstructorWithArgumentsInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       DOMStringTestInterface::DOMStringTestInterfaceWrappableType(),
       base::Bind(V8cDOMStringTestInterface::CreateWrapper),
-      base::Bind(V8cDOMStringTestInterface::CreateTemplate));
+      base::Bind(V8cDOMStringTestInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       DerivedGetterSetterInterface::DerivedGetterSetterInterfaceWrappableType(),
       base::Bind(V8cDerivedGetterSetterInterface::CreateWrapper),
-      base::Bind(V8cDerivedGetterSetterInterface::CreateTemplate));
+      base::Bind(V8cDerivedGetterSetterInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       DerivedInterface::DerivedInterfaceWrappableType(),
       base::Bind(V8cDerivedInterface::CreateWrapper),
-      base::Bind(V8cDerivedInterface::CreateTemplate));
+      base::Bind(V8cDerivedInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       DictionaryInterface::DictionaryInterfaceWrappableType(),
       base::Bind(V8cDictionaryInterface::CreateWrapper),
-      base::Bind(V8cDictionaryInterface::CreateTemplate));
+      base::Bind(V8cDictionaryInterface::GetTemplate));
 #if defined(NO_ENABLE_CONDITIONAL_INTERFACE)
   wrapper_factory_->RegisterWrappableType(
       DisabledInterface::DisabledInterfaceWrappableType(),
       base::Bind(V8cDisabledInterface::CreateWrapper),
-      base::Bind(V8cDisabledInterface::CreateTemplate));
+      base::Bind(V8cDisabledInterface::GetTemplate));
 #endif  // defined(NO_ENABLE_CONDITIONAL_INTERFACE)
   wrapper_factory_->RegisterWrappableType(
       EnumerationInterface::EnumerationInterfaceWrappableType(),
       base::Bind(V8cEnumerationInterface::CreateWrapper),
-      base::Bind(V8cEnumerationInterface::CreateTemplate));
+      base::Bind(V8cEnumerationInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ExceptionObjectInterface::ExceptionObjectInterfaceWrappableType(),
       base::Bind(V8cExceptionObjectInterface::CreateWrapper),
-      base::Bind(V8cExceptionObjectInterface::CreateTemplate));
+      base::Bind(V8cExceptionObjectInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ExceptionsInterface::ExceptionsInterfaceWrappableType(),
       base::Bind(V8cExceptionsInterface::CreateWrapper),
-      base::Bind(V8cExceptionsInterface::CreateTemplate));
+      base::Bind(V8cExceptionsInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ExtendedIDLAttributesInterface::ExtendedIDLAttributesInterfaceWrappableType(),
       base::Bind(V8cExtendedIDLAttributesInterface::CreateWrapper),
-      base::Bind(V8cExtendedIDLAttributesInterface::CreateTemplate));
+      base::Bind(V8cExtendedIDLAttributesInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       GarbageCollectionTestInterface::GarbageCollectionTestInterfaceWrappableType(),
       base::Bind(V8cGarbageCollectionTestInterface::CreateWrapper),
-      base::Bind(V8cGarbageCollectionTestInterface::CreateTemplate));
+      base::Bind(V8cGarbageCollectionTestInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       GlobalInterfaceParent::GlobalInterfaceParentWrappableType(),
       base::Bind(V8cGlobalInterfaceParent::CreateWrapper),
-      base::Bind(V8cGlobalInterfaceParent::CreateTemplate));
+      base::Bind(V8cGlobalInterfaceParent::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ImplementedInterface::ImplementedInterfaceWrappableType(),
       base::Bind(V8cImplementedInterface::CreateWrapper),
-      base::Bind(V8cImplementedInterface::CreateTemplate));
+      base::Bind(V8cImplementedInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       IndexedGetterInterface::IndexedGetterInterfaceWrappableType(),
       base::Bind(V8cIndexedGetterInterface::CreateWrapper),
-      base::Bind(V8cIndexedGetterInterface::CreateTemplate));
+      base::Bind(V8cIndexedGetterInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       InterfaceWithAny::InterfaceWithAnyWrappableType(),
       base::Bind(V8cInterfaceWithAny::CreateWrapper),
-      base::Bind(V8cInterfaceWithAny::CreateTemplate));
+      base::Bind(V8cInterfaceWithAny::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       InterfaceWithAnyDictionary::InterfaceWithAnyDictionaryWrappableType(),
       base::Bind(V8cInterfaceWithAnyDictionary::CreateWrapper),
-      base::Bind(V8cInterfaceWithAnyDictionary::CreateTemplate));
+      base::Bind(V8cInterfaceWithAnyDictionary::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       InterfaceWithDate::InterfaceWithDateWrappableType(),
       base::Bind(V8cInterfaceWithDate::CreateWrapper),
-      base::Bind(V8cInterfaceWithDate::CreateTemplate));
+      base::Bind(V8cInterfaceWithDate::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       InterfaceWithUnsupportedProperties::InterfaceWithUnsupportedPropertiesWrappableType(),
       base::Bind(V8cInterfaceWithUnsupportedProperties::CreateWrapper),
-      base::Bind(V8cInterfaceWithUnsupportedProperties::CreateTemplate));
+      base::Bind(V8cInterfaceWithUnsupportedProperties::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NamedConstructorInterface::NamedConstructorInterfaceWrappableType(),
       base::Bind(V8cNamedConstructorInterface::CreateWrapper),
-      base::Bind(V8cNamedConstructorInterface::CreateTemplate));
+      base::Bind(V8cNamedConstructorInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NamedGetterInterface::NamedGetterInterfaceWrappableType(),
       base::Bind(V8cNamedGetterInterface::CreateWrapper),
-      base::Bind(V8cNamedGetterInterface::CreateTemplate));
+      base::Bind(V8cNamedGetterInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NamedIndexedGetterInterface::NamedIndexedGetterInterfaceWrappableType(),
       base::Bind(V8cNamedIndexedGetterInterface::CreateWrapper),
-      base::Bind(V8cNamedIndexedGetterInterface::CreateTemplate));
+      base::Bind(V8cNamedIndexedGetterInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NestedPutForwardsInterface::NestedPutForwardsInterfaceWrappableType(),
       base::Bind(V8cNestedPutForwardsInterface::CreateWrapper),
-      base::Bind(V8cNestedPutForwardsInterface::CreateTemplate));
+      base::Bind(V8cNestedPutForwardsInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NoConstructorInterface::NoConstructorInterfaceWrappableType(),
       base::Bind(V8cNoConstructorInterface::CreateWrapper),
-      base::Bind(V8cNoConstructorInterface::CreateTemplate));
+      base::Bind(V8cNoConstructorInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NoInterfaceObjectInterface::NoInterfaceObjectInterfaceWrappableType(),
       base::Bind(V8cNoInterfaceObjectInterface::CreateWrapper),
-      base::Bind(V8cNoInterfaceObjectInterface::CreateTemplate));
+      base::Bind(V8cNoInterfaceObjectInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NullableTypesTestInterface::NullableTypesTestInterfaceWrappableType(),
       base::Bind(V8cNullableTypesTestInterface::CreateWrapper),
-      base::Bind(V8cNullableTypesTestInterface::CreateTemplate));
+      base::Bind(V8cNullableTypesTestInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       NumericTypesTestInterface::NumericTypesTestInterfaceWrappableType(),
       base::Bind(V8cNumericTypesTestInterface::CreateWrapper),
-      base::Bind(V8cNumericTypesTestInterface::CreateTemplate));
+      base::Bind(V8cNumericTypesTestInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       ObjectTypeBindingsInterface::ObjectTypeBindingsInterfaceWrappableType(),
       base::Bind(V8cObjectTypeBindingsInterface::CreateWrapper),
-      base::Bind(V8cObjectTypeBindingsInterface::CreateTemplate));
+      base::Bind(V8cObjectTypeBindingsInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       OperationsTestInterface::OperationsTestInterfaceWrappableType(),
       base::Bind(V8cOperationsTestInterface::CreateWrapper),
-      base::Bind(V8cOperationsTestInterface::CreateTemplate));
+      base::Bind(V8cOperationsTestInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       PromiseInterface::PromiseInterfaceWrappableType(),
       base::Bind(V8cPromiseInterface::CreateWrapper),
-      base::Bind(V8cPromiseInterface::CreateTemplate));
+      base::Bind(V8cPromiseInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       PutForwardsInterface::PutForwardsInterfaceWrappableType(),
       base::Bind(V8cPutForwardsInterface::CreateWrapper),
-      base::Bind(V8cPutForwardsInterface::CreateTemplate));
+      base::Bind(V8cPutForwardsInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       SequenceUser::SequenceUserWrappableType(),
       base::Bind(V8cSequenceUser::CreateWrapper),
-      base::Bind(V8cSequenceUser::CreateTemplate));
+      base::Bind(V8cSequenceUser::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       StaticPropertiesInterface::StaticPropertiesInterfaceWrappableType(),
       base::Bind(V8cStaticPropertiesInterface::CreateWrapper),
-      base::Bind(V8cStaticPropertiesInterface::CreateTemplate));
+      base::Bind(V8cStaticPropertiesInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       StringifierAnonymousOperationInterface::StringifierAnonymousOperationInterfaceWrappableType(),
       base::Bind(V8cStringifierAnonymousOperationInterface::CreateWrapper),
-      base::Bind(V8cStringifierAnonymousOperationInterface::CreateTemplate));
+      base::Bind(V8cStringifierAnonymousOperationInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       StringifierAttributeInterface::StringifierAttributeInterfaceWrappableType(),
       base::Bind(V8cStringifierAttributeInterface::CreateWrapper),
-      base::Bind(V8cStringifierAttributeInterface::CreateTemplate));
+      base::Bind(V8cStringifierAttributeInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       StringifierOperationInterface::StringifierOperationInterfaceWrappableType(),
       base::Bind(V8cStringifierOperationInterface::CreateWrapper),
-      base::Bind(V8cStringifierOperationInterface::CreateTemplate));
+      base::Bind(V8cStringifierOperationInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       TargetInterface::TargetInterfaceWrappableType(),
       base::Bind(V8cTargetInterface::CreateWrapper),
-      base::Bind(V8cTargetInterface::CreateTemplate));
+      base::Bind(V8cTargetInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       UnionTypesInterface::UnionTypesInterfaceWrappableType(),
       base::Bind(V8cUnionTypesInterface::CreateWrapper),
-      base::Bind(V8cUnionTypesInterface::CreateTemplate));
+      base::Bind(V8cUnionTypesInterface::GetTemplate));
   wrapper_factory_->RegisterWrappableType(
       Window::WindowWrappableType(),
-      base::Bind(DummyFunctor),
-      base::Bind(V8cWindow::CreateTemplate));
-
+      base::Bind(V8cWindow::CreateWrapper),
+      base::Bind(V8cWindow::GetTemplate));
 }
 
 }  // namespace v8c
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.h
index 64a834e..bd47282 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.h
@@ -40,8 +40,7 @@
 class V8cWindow {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/mozjs45/templates/callback-interface.h.template b/src/cobalt/bindings/mozjs45/templates/callback-interface.h.template
index 81d024c..26e2245 100644
--- a/src/cobalt/bindings/mozjs45/templates/callback-interface.h.template
+++ b/src/cobalt/bindings/mozjs45/templates/callback-interface.h.template
@@ -42,9 +42,11 @@
       bool* had_exception) const override;
 {% endfor %}
 {% endfor %}
+
   JSObject* handle() const { return implementing_object_.GetObject(); }
   const JS::Value& value() const { return implementing_object_.GetValue(); }
   bool WasCollected() const { return implementing_object_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { implementing_object_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
diff --git a/src/cobalt/bindings/testing/garbage_collection_test.cc b/src/cobalt/bindings/testing/garbage_collection_test.cc
index 03a61e8..4fab7d5 100644
--- a/src/cobalt/bindings/testing/garbage_collection_test.cc
+++ b/src/cobalt/bindings/testing/garbage_collection_test.cc
@@ -14,7 +14,6 @@
 
 #include "cobalt/bindings/testing/bindings_test_base.h"
 #include "cobalt/bindings/testing/garbage_collection_test_interface.h"
-
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
@@ -101,6 +100,31 @@
 #endif
 }
 
+TEST_F(GarbageCollectionTest, ReachableScriptValuesKeptAlive) {
+  // Ensure that platform objects attached to |Wrappable|s as script values
+  // survive GC.
+  EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 0);
+
+  const char script[] = R"(
+      const root = new InterfaceWithAny();
+      (() => {
+        const gcti = new GarbageCollectionTestInterface();
+        gcti.bicycle = 7;
+        root.setAny(gcti);
+      })();
+  )";
+  EXPECT_TRUE(EvaluateScript(script));
+
+  EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 1);
+
+  CollectGarbage();
+
+  std::string result;
+  EXPECT_TRUE(EvaluateScript("root.getAny().bicycle;", &result));
+  EXPECT_STREQ("7", result.c_str());
+  EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 1);
+}
+
 TEST_F(GarbageCollectionTest, JSObjectRetainsCustomProperty) {
   // Build a linked-list structure.
   EXPECT_EQ(GarbageCollectionTestInterface::instances().size(), 0);
diff --git a/src/cobalt/bindings/testing/garbage_collection_test_interface.cc b/src/cobalt/bindings/testing/garbage_collection_test_interface.cc
index 75b7da9..ca5aa16 100644
--- a/src/cobalt/bindings/testing/garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/testing/garbage_collection_test_interface.cc
@@ -19,12 +19,18 @@
 namespace cobalt {
 namespace bindings {
 namespace testing {
+
 namespace {
 base::LazyInstance<
     GarbageCollectionTestInterface::GarbageCollectionTestInterfaceVector>
     instances;
 }  // namespace
 
+GarbageCollectionTestInterface::GarbageCollectionTestInterfaceVector&
+GarbageCollectionTestInterface::instances() {
+  return ::cobalt::bindings::testing::instances.Get();
+}
+
 GarbageCollectionTestInterface::GarbageCollectionTestInterface()
     : previous_(NULL) {
   instances().push_back(this);
@@ -58,6 +64,17 @@
   Join(this, next.get());
 }
 
+void GarbageCollectionTestInterface::Join(
+    GarbageCollectionTestInterface* first,
+    GarbageCollectionTestInterface* second) {
+  if (first) {
+    first->next_ = second;
+  }
+  if (second) {
+    second->previous_ = first;
+  }
+}
+
 void GarbageCollectionTestInterface::MakeHead() {
   if (previous_) {
     DCHECK(previous_->next_ == this);
@@ -74,22 +91,6 @@
   next_ = NULL;
 }
 
-void GarbageCollectionTestInterface::Join(
-    GarbageCollectionTestInterface* first,
-    GarbageCollectionTestInterface* second) {
-  if (first) {
-    first->next_ = second;
-  }
-  if (second) {
-    second->previous_ = first;
-  }
-}
-
-GarbageCollectionTestInterface::GarbageCollectionTestInterfaceVector&
-GarbageCollectionTestInterface::instances() {
-  return ::cobalt::bindings::testing::instances.Get();
-}
-
 void GarbageCollectionTestInterface::TraceMembers(script::Tracer* tracer) {
   tracer->Trace(previous_);
   tracer->Trace(next_);
diff --git a/src/cobalt/bindings/testing/garbage_collection_test_interface.h b/src/cobalt/bindings/testing/garbage_collection_test_interface.h
index c65ebe1..26adcde 100644
--- a/src/cobalt/bindings/testing/garbage_collection_test_interface.h
+++ b/src/cobalt/bindings/testing/garbage_collection_test_interface.h
@@ -31,6 +31,8 @@
   typedef std::vector<GarbageCollectionTestInterface*>
       GarbageCollectionTestInterfaceVector;
 
+  static GarbageCollectionTestInterfaceVector& instances();
+
   GarbageCollectionTestInterface();
   ~GarbageCollectionTestInterface();
 
@@ -45,18 +47,16 @@
   void set_next(const scoped_refptr<GarbageCollectionTestInterface>& next);
   scoped_refptr<GarbageCollectionTestInterface> next() { return next_; }
 
-  static GarbageCollectionTestInterfaceVector& instances();
-
   DEFINE_WRAPPABLE_TYPE(GarbageCollectionTestInterface);
   void TraceMembers(script::Tracer* tracer) override;
 
  private:
-  void MakeHead();
-  void MakeTail();
-
   static void Join(GarbageCollectionTestInterface* first,
                    GarbageCollectionTestInterface* second);
 
+  void MakeHead();
+  void MakeTail();
+
   // Raw pointers going upstream, strong pointers going downstream to prevent
   // reference cycles.
   GarbageCollectionTestInterface* previous_;
diff --git a/src/cobalt/bindings/testing/interface_object_test.cc b/src/cobalt/bindings/testing/interface_object_test.cc
index 1096554..e141ddf 100644
--- a/src/cobalt/bindings/testing/interface_object_test.cc
+++ b/src/cobalt/bindings/testing/interface_object_test.cc
@@ -92,15 +92,6 @@
   EXPECT_STREQ("true", result.c_str());
 }
 
-// If [NoInterfaceObject] extended attribute is specified, there should be no
-// constructor property on the prototype.
-TEST_F(NoInterfaceObjectTest, NoConstructorProperty) {
-  std::string result;
-  EXPECT_TRUE(EvaluateScript(
-      "Object.getPrototypeOf(test).hasOwnProperty(\"constructor\");", &result));
-  EXPECT_STREQ("false", result.c_str());
-}
-
 // Interface object for non-callback interface is a function object.
 TEST_F(NoInterfaceObjectTest, NoInterfaceObjectGlobalProperty) {
   std::string result;
diff --git a/src/cobalt/bindings/testing/interface_with_any.h b/src/cobalt/bindings/testing/interface_with_any.h
index 9f126c7..0109870 100644
--- a/src/cobalt/bindings/testing/interface_with_any.h
+++ b/src/cobalt/bindings/testing/interface_with_any.h
@@ -37,13 +37,14 @@
   }
 
   void SetAny(const script::ValueHandleHolder& value) {
-    value_.reset(new script::ValueHandleHolder::Reference(this, value));
+    value_.reset(new script::ValueHandleHolder::TracedReference(value));
   }
 
   DEFINE_WRAPPABLE_TYPE(InterfaceWithAny);
+  void TraceMembers(script::Tracer* tracer) override { tracer->Trace(value_); }
 
  private:
-  scoped_ptr<script::ValueHandleHolder::Reference> value_;
+  scoped_ptr<script::ValueHandleHolder::TracedReference> value_;
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/testing/stack_trace_test.cc b/src/cobalt/bindings/testing/stack_trace_test.cc
index b900b72..05ed0ec 100644
--- a/src/cobalt/bindings/testing/stack_trace_test.cc
+++ b/src/cobalt/bindings/testing/stack_trace_test.cc
@@ -41,27 +41,27 @@
       "    return foo(depth - 1);\n"
       "  }\n"
       "}\n"
-      "foo(4)";
+      "foo(4);";
   EXPECT_TRUE(EvaluateScript(script, &result));
 
   // Expect that bar is on top.
   std::string match_line = "bar @ [object BindingsTestBase]:2";
   size_t position = result.find(match_line);
-  EXPECT_TRUE(position != std::string::npos);
+  EXPECT_TRUE(position != std::string::npos) << result;
   // Expect a foo at line 6.
   match_line = "foo @ [object BindingsTestBase]:6";
   position = result.find(match_line, ++position);
-  EXPECT_TRUE(position != std::string::npos);
+  EXPECT_TRUE(position != std::string::npos) << result;
   // Expect 4 subsequent foos at line 8.
   match_line = "foo @ [object BindingsTestBase]:8";
   for (int i = 0; i < 4; ++i) {
     position = result.find(match_line, ++position);
-    EXPECT_TRUE(position != std::string::npos);
+    EXPECT_TRUE(position != std::string::npos) << result;
   }
   // Expect global code at line 11.
-  match_line = "global code @ [object BindingsTestBase]:11";
+  match_line = " @ [object BindingsTestBase]:11";
   position = result.find(match_line, ++position);
-  EXPECT_TRUE(position != std::string::npos);
+  EXPECT_TRUE(position != std::string::npos) << result;
 }
 
 TEST_F(StackTraceTest, UnnamedFunction) {
@@ -73,14 +73,13 @@
       "  return fun();\n"
       "}\n"
       "foo(function() { return getStackTrace();})";
-  EXPECT_TRUE(EvaluateScript(script, &result));
+  EXPECT_TRUE(EvaluateScript(script, &result)) << result;
 
   std::string match_line = "[object BindingsTestBase]:4";
   size_t position = result.find(match_line);
-  EXPECT_TRUE(position != std::string::npos);
+  EXPECT_TRUE(position != std::string::npos) << result;
 }
 
-#if defined(ENGINE_SUPPORTS_STACK_TRACE_COLUMNS)
 // Test for column numbers in stack trace. Behavior varies somewhat
 // across engines & versions so, don't check actual column values.
 TEST_F(StackTraceTest, GetStackTraceColumns) {
@@ -99,10 +98,9 @@
   EXPECT_TRUE(EvaluateScript(script, &result));
   const std::string expected =
       "bar @ \\[object BindingsTestBase\\]:3:\\d+\n"
-      "global code @ \\[object BindingsTestBase\\]:8:\\d+";
+      " @ \\[object BindingsTestBase\\]:8:\\d+";
   EXPECT_THAT(result, MatchesRegex(expected));
 }
-#endif  // ENGINE_SUPPORTS_STACK_TRACE_COLUMNS
 
 }  // namespace testing
 }  // namespace bindings
diff --git a/src/cobalt/bindings/v8c/code_generator_v8c.py b/src/cobalt/bindings/v8c/code_generator_v8c.py
index 80bdcec..c353d62 100644
--- a/src/cobalt/bindings/v8c/code_generator_v8c.py
+++ b/src/cobalt/bindings/v8c/code_generator_v8c.py
@@ -50,6 +50,19 @@
     templates_dir = os.path.normpath(os.path.join(module_path, 'templates'))
     super(CodeGeneratorV8c, self).__init__(templates_dir, *args, **kwargs)
 
+  def build_interface_context(self, interface, interface_info, definitions):
+    # Due to a V8 internals quirks, named constructor attributes MUST come
+    # first, as V8 will use the key that we use to set them on something else
+    # as the "name" property on the function template value, overriding the
+    # originally selected name from |FunctionTemplate::SetClassName|.  Efforts
+    # to document/modify this behavior in V8 are underway.
+    context = super(CodeGeneratorV8c, self).build_interface_context(
+        interface, interface_info, definitions)
+    context['all_attributes_v8_order_quirk'] = sorted(
+        [a for a in context['attributes'] + context['static_attributes']],
+        key=lambda a: not a.get('is_named_constructor_attribute', False))
+    return context
+
   @property
   def generated_file_prefix(self):
     return 'v8c'
diff --git a/src/cobalt/bindings/v8c/templates/callback-interface.cc.template b/src/cobalt/bindings/v8c/templates/callback-interface.cc.template
index 682f0b8..eeebf75 100644
--- a/src/cobalt/bindings/v8c/templates/callback-interface.cc.template
+++ b/src/cobalt/bindings/v8c/templates/callback-interface.cc.template
@@ -21,6 +21,7 @@
 
 #include "cobalt/script/logging_exception_state.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/v8c_callback_interface.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
 #include "v8/include/v8.h"
@@ -32,6 +33,7 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::FromJSValue;
 using cobalt::script::v8c::GetCallableForCallbackInterface;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::V8cGlobalEnvironment;
 {% endblock using_directives %}
@@ -49,57 +51,56 @@
     {{arg.arg_type}} {{arg.name}},
   {% endfor %}
     bool* had_exception) const {
+  bool success = false;
 {% if overload.type != 'void' %}
   {{overload.type}} cobalt_return_value;
 {% endif %}
 
-  DCHECK(!this->IsEmpty());
   DCHECK(isolate_);
-
-  EntryScope entry_scope(isolate_);
-  v8::Local<v8::Context> context = isolate_->GetCurrentContext();
-
-  v8::MaybeLocal<v8::Object> maybe_implementing_object = this->NewLocal(isolate_)->ToObject(context);
-  v8::Local<v8::Object> implementing_object;
-  if (!maybe_implementing_object.ToLocal(&implementing_object)) {
-    *had_exception = true;
-    return cobalt_return_value;
+  if (this->IsEmpty()) {
+    goto done;
   }
+  {
+    EntryScope entry_scope(isolate_);
+    v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+    v8::TryCatch try_catch(isolate_);
 
-  v8::MaybeLocal<v8::Object> maybe_callable =
-      GetCallableForCallbackInterface(isolate_, implementing_object, "{{overload.idl_name}}");
-  v8::Local<v8::Object> callable;
-  if (!maybe_callable.ToLocal(&callable)) {
-    NOTIMPLEMENTED();
-    *had_exception = true;
-    return cobalt_return_value;
-  }
-  DCHECK(callable->IsCallable());
+    v8::Local<v8::Value> implementing_value = this->NewLocal(isolate_);
+    if (!implementing_value->IsObject()) {
+      LOG(WARNING) << "Implementing object is NULL";
+      goto done;
+    }
 
-  v8::Local<v8::Value> this_value;
-  ToJSValue(isolate_, callback_this, &this_value);
+    v8::Local<v8::Function> callable;
+    if (!GetCallableForCallbackInterface(
+             isolate_, implementing_value.As<v8::Object>(),
+             NewInternalString(isolate_, "{{overload.idl_name}}"))
+             .ToLocal(&callable)) {
+      goto done;
+    }
 
-  const int kNumArguments = {{overload.arguments|length}};
-  v8::Local<v8::Value> argv[kNumArguments];
+    v8::Local<v8::Value> this_value;
+    ToJSValue(isolate_, callback_this, &this_value);
 
+    const int kNumArguments = {{overload.arguments|length}};
+    v8::Local<v8::Value> argv[kNumArguments];
 {% for arg in overload.arguments %}
-  ToJSValue(isolate_, {{arg.name}}, &argv[{{loop.index0}}]);
+    ToJSValue(isolate_, {{arg.name}}, &argv[{{loop.index0}}]);
 {% endfor %}
 
-  v8::MaybeLocal<v8::Value> maybe_return_value =
-      callable->CallAsFunction(isolate_->GetCurrentContext(), this_value, kNumArguments, argv);
-  v8::Local<v8::Value> return_value;
-  if (!maybe_return_value.ToLocal(&return_value)) {
-    *had_exception = true;
-  return cobalt_return_value;
+    v8::Local<v8::Value> return_value;
+    if (!callable->Call(context, this_value, kNumArguments, argv)
+             .ToLocal(&return_value)) {
+      goto done;
+    }
+    LoggingExceptionState exception_state;
+    FromJSValue(isolate_, return_value, 0, &exception_state,
+                &cobalt_return_value);
+    success = !exception_state.is_exception_set();
   }
 
-  LoggingExceptionState exception_state;
-  FromJSValue(isolate_, return_value, 0, &exception_state, &cobalt_return_value);
-  if (exception_state.is_exception_set()) {
-    *had_exception = true;
-  }
-
+done:
+  *had_exception = !success;
 {% if overload.type != 'void' %}
   return cobalt_return_value;
 {% endif %}
diff --git a/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template
index f8cc3d3..2d0da59 100644
--- a/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template
+++ b/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template
@@ -40,6 +40,7 @@
 #include "{{generated_conversion_include}}"
 #include "base/logging.h"
 #include "cobalt/script/exception_state.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "v8/include/v8.h"
 
 using {{fully_qualified_name}};
@@ -82,7 +83,7 @@
 // 3. Return the enumeration value of type E that is equal to S.
 {% for value, idl_value in value_pairs %}
   {{-" else " if not loop.first}} if (
-      v8::String::NewFromUtf8(isolate, "{{idl_value}}", v8::NewStringType::kInternalized).ToLocalChecked()->Equals(value))
+      NewInternalString(isolate, "{{idl_value}}")->Equals(value))
   {
     *out_enum = {{namespace}}::{{value}};
   }
diff --git a/src/cobalt/bindings/v8c/templates/interface.cc.template b/src/cobalt/bindings/v8c/templates/interface.cc.template
index 5759ac8..24464b7 100644
--- a/src/cobalt/bindings/v8c/templates/interface.cc.template
+++ b/src/cobalt/bindings/v8c/templates/interface.cc.template
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -52,7 +54,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -60,10 +61,12 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
 {% endblock using_directives %}
@@ -85,18 +88,210 @@
 {% endblock enumeration_declarations %}
 
 {% block top_level_unnamed_namespace %}
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 {% endblock top_level_unnamed_namespace %}
 
 {% block implementation %}
 
 namespace {
 
+const int kInterfaceUniqueId = {{unique_id}};
+
+{% if named_property_getter %}
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ nonstatic_function_prologue(impl_class) }}
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+{{ call_cobalt_function(impl_class, named_property_getter.type,
+                        named_property_getter.name, ["property_name"],
+                        named_property_getter.raises_exception,
+                        named_property_getter.call_with) }}
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ get_impl_class_instance(impl_class) }}
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ get_impl_class_instance(impl_class) }}
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+{% endif %}
+
+{% if named_property_setter %}
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ nonstatic_function_prologue(impl_class) }}
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<{{named_property_setter.type}}>::ConversionType native_value;
+  FromJSValue(isolate, value, {{named_property_setter.conversion_flags}},
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+{{ call_cobalt_function(impl_class, "void",
+                        named_property_setter.name, ["property_name", "native_value"],
+                        named_property_setter.raises_exception,
+                        named_property_setter.call_with) }}
+  DCHECK(!exception_state.is_exception_set());
+}
+{% endif %}
+
+{% if named_property_deleter %}
+void NamedPropertyDeleterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ nonstatic_function_prologue(impl_class) }}
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+{{ call_cobalt_function(impl_class, "void",
+                        named_property_deleter.name, ["property_name"],
+                        named_property_deleter.raises_exception,
+                        named_property_deleter.call_with) }}
+  DCHECK(!exception_state.is_exception_set());
+}
+{% endif %}
+
+{% if indexed_property_getter %}
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ nonstatic_function_prologue(impl_class) }}
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+{{ call_cobalt_function(impl_class, indexed_property_getter.type,
+                        indexed_property_getter.name, ["index"],
+                        indexed_property_getter.raises_exception,
+                        indexed_property_getter.call_with) }}
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ get_impl_class_instance(impl_class) }}
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+{% endif %}
+
+{% if indexed_property_setter %}
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ nonstatic_function_prologue(impl_class) }}
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<{{indexed_property_setter.type}}>::ConversionType native_value;
+  FromJSValue(isolate, value, {{indexed_property_setter.conversion_flags}},
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+{{ call_cobalt_function(impl_class, "void",
+                        indexed_property_setter.name, ["index", "native_value"],
+                        indexed_property_setter.raises_exception,
+                        indexed_property_setter.call_with) }}
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+{% endif %}
+
+{% if indexed_property_deleter %}
+void IndexedPropertyDeleterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+{{ nonstatic_function_prologue(impl_class) }}
+  if (index >= impl->length()) {
+    return;
+  }
+{{ call_cobalt_function(impl_class, "void",
+                        indexed_property_deleter.name, ["index"],
+                        indexed_property_deleter.raises_exception,
+                        indexed_property_deleter.call_with) }}
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(v8::Boolean::New(isolate, true));
+}
+{% endif %}
+
 {% if constructor %}
 
 {% for overload in constructor.overloads if constructor.overloads|length > 1 %}
@@ -117,29 +312,13 @@
 }
 {% endif %}
 
-{% for constant in constants %}
-void {{constant.idl_name}}AttributeGetter(
-    v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
-  // TODO: Consider just attaching a constant instead.
-  v8::Local<v8::Value> result_value;
-  ToJSValue(info.GetIsolate(), {{constant.value}}, &result_value);
-  info.GetReturnValue().Set(result_value);
-}
-{% endfor %}
-
 {% for attribute in attributes + static_attributes %}
 {% if attribute.conditional %}
 #if defined({{attribute.conditional}})
 {% endif %}
+
 {% if attribute.is_constructor_attribute %}
-void {{attribute.idl_name}}AttributeGetter(
-    v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Isolate* isolate = info.GetIsolate();
-  v8::Local<v8::Object> object = info.This();
-  v8::Local<v8::Object> global_object = info.GetIsolate()->GetCurrentContext()->Global();
-  v8::Local<v8::Object> interface_object = V8c{{attribute.interface_name}}::GetInterfaceObject(isolate, global_object);
-  info.GetReturnValue().Set(interface_object);
-}
+// Nothing for {{attribute}}.  We will just give them the v8::FunctionTemplate.
 {% else %}
 {% if attribute.is_static %}
 void {{attribute.idl_name}}StaticAttributeGetter(
@@ -221,95 +400,404 @@
 {% endif %}
 {% endfor %}
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "{{interface_name}}",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+{% if stringifier %}
+void Stringifier(v8::Local<v8::String> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state(isolate);
+
+  {{ check_if_object_implements_interface() }}
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+
+  // |WrapperPrivate::GetFromObject| can fail if |object| is not a |Wrapper|
+  // object.
+  if (!wrapper_private) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    return;
+  }
+
+  {{impl_class}}* impl =
+      wrapper_private->wrappable<{{impl_class}}>().get();
+  if (!impl) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    NOTREACHED();
+    return;
+  }
+  std::string stringified = impl->{{stringifier.name}}();
+
+  v8::Local<v8::Value> v8_stringified;
+  ToJSValue(isolate, stringified, &v8_stringified);
+
+  info.GetReturnValue().Set(v8_stringified);
+}
+{% endif %}
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+{% if constructor %}
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          {{constructor.length}});
+  function_template->SetLength({{constructor.length}});
+{% else %}
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+{% endif %}
+  function_template->SetClassName(NewInternalString(isolate, "{{interface_name}}"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
+
 {% if parent_interface %}
-  v8::Local<v8::FunctionTemplate> parent_template = {{parent_interface}}::CreateTemplate(isolate);
-  function_template->Inherit(parent_template);
+  {
+    // An interface can be defined to inherit from another interface. If the
+    // identifier of the interface is followed by a U+003A COLON (":") character
+    // and an identifier, then that identifier identifies the inherited
+    // interface. An object that implements an interface that inherits from
+    // another also implements that inherited interface. The object therefore
+    // will also have members that correspond to the interface members from the
+    // inherited interface.
+    v8::Local<v8::FunctionTemplate> parent_template = {{parent_interface}}::GetTemplate(isolate);
+    function_template->Inherit(parent_template);
+  }
 {% elif is_exception_interface %}
-  NOTIMPLEMENTED();
+  {
+    // A spicy hack from Chromium in order to achieve
+    // https://heycam.github.io/webidl/#es-DOMException-specialness
+    // See https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl?l=630&rcl=0f7c2c752bb24ad08c17017e4e68401093fe76a0
+    v8::Local<v8::FunctionTemplate> intrinsic_error_prototype_interface_template =
+        v8::FunctionTemplate::New(isolate);
+    intrinsic_error_prototype_interface_template->RemovePrototype();
+    intrinsic_error_prototype_interface_template->SetIntrinsicDataProperty(
+        NewInternalString(isolate, "prototype"), v8::kErrorPrototype);
+    function_template->Inherit(intrinsic_error_prototype_interface_template);
+  }
 {% endif %}
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
-
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 {% for constant in constants %}
-  prototype_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "{{constant.idl_name}}",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    {{constant.idl_name}}AttributeGetter);
+  {
+    // The name of the property is the identifier of the constant.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "{{constant.idl_name}}");
+
+    // The value of the property is that which is obtained by converting the
+    // constant's IDL value to an ECMAScript value.
+    v8::Local<v8::Value> constant_value;
+    ToJSValue(isolate, {{constant.value}}, &constant_value);
+
+    // The property has attributes { [[Writable]]: false, [[Enumerable]]: true,
+    // [[Configurable]]: false }.
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        v8::ReadOnly | v8::DontDelete);
+
+    // The location of the property is determined as follows:
+{% if is_global_interface %}
+    // If the interface was declared with the [Global] extended attribute, then
+    // the property exists on the single object that implements the interface.
+    instance_template->Set(name, constant_value, attributes);
+
+{% else %}
+    // Otherwise, if the interface has an interface prototype object, then the
+    // property exists on it.
+    prototype_template->Set(name, constant_value, attributes);
+{% endif %}
+
+    // In addition, a property with the same characteristics must exist on the
+    // interface object or the legacy callback interface object, if either of
+    // those objects exists.
+    function_template->Set(name, constant_value, attributes);
+  }
 {% endfor %}
 
-{% for attribute in attributes if not attribute.is_constructor_attribute %}
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+{% for attribute in all_attributes_v8_order_quirk %}
 {% if attribute.conditional %}
 #if defined({{attribute.conditional}})
 {% endif %}
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "{{attribute.idl_name}}",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    {{attribute.idl_name}}AttributeGetter
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "{{attribute.idl_name}}");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = {{ "false" if attribute.is_unforgeable else "true"}};
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+{% if not attribute.is_constructor_attribute %}
+
+    // The location of the property is determined as follows:
+{% if attribute.is_static %}
+    // Operations installed on the interface object must be static methods, so
+    // no need to specify a signature, i.e. no need to do type check against a
+    // holder.
+
+    // If the attribute is a static attribute, then there is a single
+    // corresponding property and it exists on the interface's interface object.
+    function_template->SetNativeDataProperty(
+        name,
+        {{attribute.idl_name}}StaticAttributeGetter,
 {% if attribute.has_setter %}
-    ,{{attribute.idl_name}}AttributeSetter
+        {{attribute.idl_name}}StaticAttributeSetter,
+{% else %}
+        0
 {% endif %}
-  );
+        v8::Local<v8::Value>(),
+        attributes);
+
+{% elif attribute.is_unforgeable or is_global_interface %}
+    // Otherwise, if the attribute is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    instance_template->SetAccessor(
+        name,
+        {{attribute.idl_name}}AttributeGetter,
+{% if attribute.has_setter %}
+        {{attribute.idl_name}}AttributeSetter,
+{% else %}
+        0,
+{% endif %}
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+{% else %}
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        {{attribute.idl_name}}AttributeGetter,
+{% if attribute.has_setter %}
+        {{attribute.idl_name}}AttributeSetter,
+{% else %}
+        0,
+{% endif %}
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+{% endif %}
+
+{% else %} {#- not attribute.is_constructor_attribute #}
+    {
+      v8::Local<v8::String> name = NewInternalString(
+          isolate,
+          "{{attribute.idl_name}}");
+      instance_template->Set(
+          name,
+{% if attribute.interface_name == interface_name %}
+          function_template
+{% else %}
+          // Note that we use "attribute.interface_name", and not
+          // "attribute.idl_name", because of named constructors.
+          V8c{{attribute.interface_name}}::GetTemplate(isolate)
+{% endif %}
+      );
+    }
+
+{% endif %} {#- not attribute.is_constructor_attribute #}
+  }
 {% if attribute.conditional %}
-#endif  // defined({{attribute.conditional}})
+#endif  // {{attribute.conditional}}
 {% endif %}
 {% endfor %}
 
-{% for operation in operations %}
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+{% for operation in operations + static_operations %}
 {% if operation.conditional %}
 #if defined({{operation.conditional}})
 {% endif %}
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, {{operation.idl_name}}Method);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "{{operation.idl_name}}");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = {{ "false" if operation.is_unforgeable else "true"}};
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+{% if operation.is_static %}
+    // If the operation is static, then the property exists on the interface
+    // object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, {{operation.idl_name}}StaticMethod);
     method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "{{operation.idl_name}}",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+    method_template->SetLength({{operation.length}});
+    function_template->Set(
+        NewInternalString(isolate, "{{operation.idl_name}}"),
         method_template);
-  }
-{% if operation.conditional %}
-#endif  // defined({{operation.conditional}})
+
+{% elif operation.is_unforgeable or is_global_interface %}
+    // Otherwise, if the operation is unforgeable on the interface or if the
+    // interface was declared with the [Global] extended attribute, then the
+    // property exists on every object that implements the interface.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, {{operation.idl_name}}Method);
+    method_template->RemovePrototype();
+    method_template->SetLength({{operation.length}});
+    instance_template->Set(
+        NewInternalString(isolate, "{{operation.idl_name}}"),
+        method_template);
+
+{% else %}
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, {{operation.idl_name}}Method);
+    method_template->RemovePrototype();
+    method_template->SetLength({{operation.length}});
+    prototype_template->Set(
+        NewInternalString(isolate, "{{operation.idl_name}}"),
+        method_template);
 {% endif %}
 
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+{% if operation.conditional %}
+#endif  // {{operation.conditional}}
+{% endif %}
 {% endfor %}
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "{{interface_name}}"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = {{unique_id}};
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+{% if named_property_getter %}
+  {
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      {{ "NamedPropertySetterCallback" if named_property_setter else "nullptr" }},
+      NamedPropertyQueryCallback,
+      {{ "NamedPropertyDeleterCallback" if named_property_deleter else "nullptr" }},
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
+  }
+{% endif %}
+
+{% if indexed_property_getter %}
+  {
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      {{ "IndexedPropertySetterCallback" if indexed_property_setter else "nullptr" }},
+      IndexedPropertyDescriptorCallback,
+      {{ "IndexedPropertyDeleterCallback" if indexed_property_deleter else "nullptr" }},
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
+  }
+{% endif %}
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> {{binding_class}}::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+{% if is_global_interface %}
+
+// The global interface is special.  Just give them the global object proxy.
+v8::Local<v8::Object> {{binding_class}}::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>&) {
+  return isolate->GetCurrentContext()->Global();
+}
+
+{% else %}
+
+v8::Local<v8::Object> {{binding_class}}::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -319,14 +807,14 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> {{binding_class}}::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
+{% endif %}
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> {{binding_class}}::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 {% endblock implementation %}
@@ -341,24 +829,11 @@
 void V8cGlobalEnvironment::CreateGlobalObject(
     const scoped_refptr<GlobalInterface>& global_interface,
     EnvironmentSettings* environment_settings) {
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::CreateGlobalObject()");
   // Intentionally not an |EntryScope|, since the context doesn't exist yet.
   v8::Isolate::Scope isolate_scope(isolate_);
   v8::HandleScope handle_scope(isolate_);
-  v8::Local<v8::ObjectTemplate> global_object_template = {{binding_class}}::CreateTemplate(isolate_)->InstanceTemplate();
-
-{% for interface in all_interfaces %}
-{% if interface.conditional %}
-#if defined({{interface.conditional}})
-{% endif %}
-  global_object_template->Set(
-      v8::String::NewFromUtf8(
-          isolate_, "{{interface.name}}",
-          v8::NewStringType::kInternalized).ToLocalChecked(),
-      V8c{{interface.name}}::CreateTemplate(isolate_));
-{% if interface.conditional %}
-#endif  // defined({{interface.conditional}})
-{% endif %}
-{% endfor %}
+  v8::Local<v8::ObjectTemplate> global_object_template = {{binding_class}}::GetTemplate(isolate_)->InstanceTemplate();
 
   v8::Local<v8::Context> context =
       v8::Context::New(isolate_, nullptr, global_object_template);
@@ -370,27 +845,24 @@
   environment_settings_ = environment_settings;
   EvaluateAutomatics();
 
+  v8::Local<v8::Object> global_object = context->Global();
+  new WrapperPrivate(isolate_, global_interface, global_object);
+
+  auto actual_global_object = global_object->GetPrototype()->ToObject();
+  new WrapperPrivate(isolate_, global_interface, actual_global_object);
+
 {% for interface in all_interfaces %}
 {% if interface.conditional %}
 #if defined({{interface.conditional}})
 {% endif %}
-  {# Pass in a dummy CreateProxy for global interface #}
-  {% if interface.name == impl_class %}
-  wrapper_factory_->RegisterWrappableType(
-      {{interface.name}}::{{interface.name}}WrappableType(),
-      base::Bind(DummyFunctor),
-      base::Bind(V8c{{interface.name}}::CreateTemplate));
-  {% else %}
   wrapper_factory_->RegisterWrappableType(
       {{interface.name}}::{{interface.name}}WrappableType(),
       base::Bind(V8c{{interface.name}}::CreateWrapper),
-      base::Bind(V8c{{interface.name}}::CreateTemplate));
-  {% endif %}
+      base::Bind(V8c{{interface.name}}::GetTemplate));
 {% if interface.conditional %}
 #endif  // defined({{interface.conditional}})
 {% endif %}
 {% endfor %}
-
 }
 
 }  // namespace v8c
@@ -447,7 +919,7 @@
 // 3. Return the enumeration value of type E that is equal to S.
 {% for value, idl_value in enumeration.value_pairs %}
 {{-" else " if not loop.first}}
-  if (string == v8::String::NewFromUtf8(isolate, "{{id_value}}", v8::NewStringType::kInternalized).ToLocalChecked()) {
+  if (string == NewInternalString(isolate, "{{id_value}}")) {
     *out_enum = {{impl_class}}::{{value}};
   }
 {% endfor %}
diff --git a/src/cobalt/bindings/v8c/templates/interface.h.template b/src/cobalt/bindings/v8c/templates/interface.h.template
index 5cc385e..aa164fb 100644
--- a/src/cobalt/bindings/v8c/templates/interface.h.template
+++ b/src/cobalt/bindings/v8c/templates/interface.h.template
@@ -26,9 +26,6 @@
 class {{binding_class}} {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-{% if has_interface_object %}
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
-{% endif %}
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 {% endblock implementation %}
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 3cf2f4f..006245e 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -29,6 +29,7 @@
 #include "base/string_util.h"
 #include "base/time.h"
 #include "build/build_config.h"
+#include "cobalt/base/accessibility_caption_settings_changed_event.h"
 #include "cobalt/base/accessibility_settings_changed_event.h"
 #include "cobalt/base/application_event.h"
 #include "cobalt/base/cobalt_paths.h"
@@ -43,6 +44,9 @@
 #include "cobalt/base/on_screen_keyboard_shown_event.h"
 #include "cobalt/base/startup_timer.h"
 #include "cobalt/base/user_log.h"
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+#include "cobalt/base/version_compatibility.h"
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
 #include "cobalt/base/window_size_changed_event.h"
 #include "cobalt/browser/memory_settings/auto_mem_settings.h"
 #include "cobalt/browser/memory_tracker/tool.h"
@@ -53,7 +57,7 @@
 #include "cobalt/script/javascript_engine.h"
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
 #include "cobalt/storage/savegame_fake.h"
-#endif
+#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
 #include "cobalt/system_window/input_event.h"
 #include "cobalt/trace_event/scoped_trace_to_file.h"
 #include "googleurl/src/gurl.h"
@@ -493,7 +497,22 @@
     options.storage_manager_options.savegame_options.factory =
         &storage::SavegameFake::Create;
   }
-#endif
+#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+  constexpr int kDefaultMinCompatibilityVersion = 1;
+  int minimum_version = kDefaultMinCompatibilityVersion;
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+  if (command_line->HasSwitch(browser::switches::kMinCompatibilityVersion)) {
+    std::string switch_value =
+        command_line->GetSwitchValueASCII(switches::kMinCompatibilityVersion);
+    if (!base::StringToInt(switch_value, &minimum_version)) {
+      DLOG(ERROR) << "Invalid min_compatibility_version provided.";
+    }
+  }
+#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(minimum_version);
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
 
   base::optional<std::string> partition_key;
   if (command_line->HasSwitch(browser::switches::kLocalStoragePartitionUrl)) {
@@ -655,6 +674,13 @@
       base::OnScreenKeyboardBlurredEvent::TypeId(),
       on_screen_keyboard_blurred_event_callback_);
 #endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+
+#if SB_HAS(CAPTIONS)
+  event_dispatcher_.AddEventCallback(
+      base::AccessibilityCaptionSettingsChangedEvent::TypeId(),
+      base::Bind(&Application::OnCaptionSettingsChangedEvent,
+                 base::Unretained(this)));
+#endif  // SB_HAS(CAPTIONS)
 #if defined(ENABLE_WEBDRIVER)
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   bool create_webdriver_module =
@@ -809,6 +835,12 @@
     case kSbEventTypeAccessiblitySettingsChanged:
       DispatchEventInternal(new base::AccessibilitySettingsChangedEvent());
       break;
+#if SB_HAS(CAPTIONS)
+    case kSbEventTypeAccessibilityCaptionSettingsChanged:
+      DispatchEventInternal(
+          new base::AccessibilityCaptionSettingsChangedEvent());
+      break;
+#endif  // SB_HAS(CAPTIONS)
     default:
       DLOG(WARNING) << "Unhandled Starboard event of type: "
                     << starboard_event->type;
@@ -946,6 +978,16 @@
 }
 #endif  // SB_HAS(ON_SCREEN_KEYBOARD)
 
+#if SB_HAS(CAPTIONS)
+void Application::OnCaptionSettingsChangedEvent(const base::Event* event) {
+  TRACE_EVENT0("cobalt::browser",
+               "Application::OnCaptionSettingsChangedEvent()");
+  browser_module_->OnCaptionSettingsChanged(
+      base::polymorphic_downcast<
+          const base::AccessibilityCaptionSettingsChangedEvent*>(event));
+}
+#endif  // SB_HAS(CAPTIONS)
+
 void Application::WebModuleRecreated() {
   TRACE_EVENT0("cobalt::browser", "Application::WebModuleRecreated()");
 #if defined(ENABLE_WEBDRIVER)
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 8ba1cdc..1344ac3 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -82,6 +82,10 @@
   void OnOnScreenKeyboardBlurredEvent(const base::Event* event);
 #endif  // SB_HAS(ON_SCREEN_KEYBOARD)
 
+#if SB_HAS(CAPTIONS)
+  void OnCaptionSettingsChangedEvent(const base::Event* event);
+#endif  // SB_HAS(CAPTIONS)
+
   // Called when a navigation occurs in the BrowserModule.
   void WebModuleRecreated();
 
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 49c4b18..d4ed23e 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -241,6 +241,10 @@
       network_module_(&storage_manager_, event_dispatcher_,
                       options_.network_module_options),
       splash_screen_cache_(new SplashScreenCache()),
+#if SB_HAS(ON_SCREEN_KEYBOARD)
+      on_screen_keyboard_bridge_(new OnScreenKeyboardStarboardBridge(
+          base::Bind(&BrowserModule::GetSbWindow, base::Unretained(this)))),
+#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
       web_module_loaded_(true /* manually_reset */,
                          false /* initially_signalled */),
       web_module_recreated_callback_(options_.web_module_recreated_callback),
@@ -265,10 +269,6 @@
           kScreenshotCommandShortHelp, kScreenshotCommandLongHelp)),
 #endif  // defined(ENABLE_SCREENSHOT)
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
-#if SB_HAS(ON_SCREEN_KEYBOARD)
-      on_screen_keyboard_bridge_(new OnScreenKeyboardStarboardBridge(
-          base::Bind(&BrowserModule::GetSbWindow, base::Unretained(this)))),
-#endif  // SB_HAS(ON_SCREEN_KEYBOARD)
       has_resumed_(true, false),
 #if defined(COBALT_CHECK_RENDER_TIMEOUT)
       timeout_polling_thread_(kTimeoutPollingThreadName),
@@ -333,8 +333,8 @@
         base::Bind(&CreateExtensionInterface);
   }
 
-#if defined(ENABLE_DEBUG_CONSOLE) && defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
+#if defined(ENABLE_DEBUG_CONSOLE) && defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   if (command_line->HasSwitch(switches::kInputFuzzer)) {
     OnFuzzerToggle(std::string());
   }
@@ -367,6 +367,10 @@
   lifecycle_observers_.AddObserver(debug_console_.get());
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
+  if (command_line->HasSwitch(switches::kEnableMapToMeshRectanglar)) {
+    options_.web_module_options.enable_map_to_mesh_rectangular = true;
+  }
+
   fallback_splash_screen_url_ = options.fallback_splash_screen_url;
   // Synchronously construct our WebModule object.
   Navigate(url);
@@ -644,10 +648,16 @@
     return;
   }
 
-  if (splash_screen_ && !splash_screen_->ShutdownSignaled()) {
-    splash_screen_->Shutdown();
+  if (splash_screen_) {
+    if (on_screen_keyboard_show_called_) {
+      // Hide the splash screen as quickly as possible.
+      DestroySplashScreen(base::TimeDelta());
+    } else if (!splash_screen_->ShutdownSignaled()) {
+      splash_screen_->Shutdown();
+    }
   }
   if (application_state_ == base::kApplicationStatePreloading) {
+    layout_results.on_rasterized_callback.Run();
     return;
   }
 
@@ -769,7 +779,9 @@
 #if SB_HAS(ON_SCREEN_KEYBOARD)
 void BrowserModule::OnOnScreenKeyboardShown(
     const base::OnScreenKeyboardShownEvent* event) {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   // Only inject shown events to the main WebModule.
+  on_screen_keyboard_show_called_ = true;
   if (web_module_) {
     web_module_->InjectOnScreenKeyboardShownEvent(event->ticket());
   }
@@ -777,6 +789,7 @@
 
 void BrowserModule::OnOnScreenKeyboardHidden(
     const base::OnScreenKeyboardHiddenEvent* event) {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   // Only inject hidden events to the main WebModule.
   if (web_module_) {
     web_module_->InjectOnScreenKeyboardHiddenEvent(event->ticket());
@@ -785,6 +798,7 @@
 
 void BrowserModule::OnOnScreenKeyboardFocused(
     const base::OnScreenKeyboardFocusedEvent* event) {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   // Only inject focused events to the main WebModule.
   if (web_module_) {
     web_module_->InjectOnScreenKeyboardFocusedEvent(event->ticket());
@@ -793,6 +807,7 @@
 
 void BrowserModule::OnOnScreenKeyboardBlurred(
     const base::OnScreenKeyboardBlurredEvent* event) {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   // Only inject blurred events to the main WebModule.
   if (web_module_) {
     web_module_->InjectOnScreenKeyboardBlurredEvent(event->ticket());
@@ -800,6 +815,15 @@
 }
 #endif  // SB_HAS(ON_SCREEN_KEYBOARD)
 
+#if SB_HAS(CAPTIONS)
+void BrowserModule::OnCaptionSettingsChanged(
+    const base::AccessibilityCaptionSettingsChangedEvent* /*event*/) {
+  if (web_module_) {
+    web_module_->InjectCaptionSettingsChangedEvent();
+  }
+}
+#endif  // SB_HAS(CAPTIONS)
+
 #if defined(ENABLE_DEBUG_CONSOLE)
 void BrowserModule::OnFuzzerToggle(const std::string& message) {
   if (MessageLoop::current() != self_message_loop_) {
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index f26099b..4b3a05c 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -25,6 +25,7 @@
 #include "base/threading/thread.h"
 #include "base/timer.h"
 #include "cobalt/account/account_manager.h"
+#include "cobalt/base/accessibility_caption_settings_changed_event.h"
 #include "cobalt/base/application_state.h"
 #include "cobalt/base/message_queue.h"
 #include "cobalt/base/on_screen_keyboard_blurred_event.h"
@@ -181,6 +182,11 @@
       const base::OnScreenKeyboardBlurredEvent* event);
 #endif  // SB_HAS(ON_SCREEN_KEYBOARD)
 
+#if SB_HAS(CAPTIONS)
+  void OnCaptionSettingsChanged(
+      const base::AccessibilityCaptionSettingsChangedEvent* event);
+#endif  // SB_HAS(CAPTIONS)
+
  private:
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   static void CoreDumpHandler(void* browser_module_as_void);
@@ -464,6 +470,9 @@
   // The splash screen cache.
   scoped_ptr<SplashScreenCache> splash_screen_cache_;
 
+  scoped_ptr<dom::OnScreenKeyboardBridge> on_screen_keyboard_bridge_;
+  bool on_screen_keyboard_show_called_ = false;
+
   // Sets up everything to do with web page management, from loading and
   // parsing the web page and all referenced files to laying it out.  The
   // web module will ultimately produce a render tree that can be passed
@@ -518,8 +527,6 @@
   // the splash screen is currently displayed.
   scoped_ptr<SplashScreen> splash_screen_;
 
-  scoped_ptr<dom::OnScreenKeyboardBridge> on_screen_keyboard_bridge_;
-
   // Reset when the browser is paused, signalled to resume.
   base::WaitableEvent has_resumed_;
 
diff --git a/src/cobalt/browser/h5vcc_url_handler.cc b/src/cobalt/browser/h5vcc_url_handler.cc
index d02a4a3..e476110 100644
--- a/src/cobalt/browser/h5vcc_url_handler.cc
+++ b/src/cobalt/browser/h5vcc_url_handler.cc
@@ -57,13 +57,13 @@
     DLOG(WARNING) << "Query parameter " << name << " not found in URL.";
     return "";
   }
-  size_t name_end = query.find("=", name_start);
+  size_t name_end = query.find('=', name_start);
   if (name_end == std::string::npos) {
     DLOG(WARNING) << "Value of " << name << " not found in URL.";
     return "";
   }
   size_t value_start = name_end - name_start + 1;
-  size_t value_end = query.find_first_of("&", value_start);
+  size_t value_end = query.find_first_of('&', value_start);
   if (value_end != std::string::npos) {
     value_end -= value_start;
   }
diff --git a/src/cobalt/browser/lib/cobalt.def b/src/cobalt/browser/lib/cobalt.def
index 2f36ae8..4aa31cc 100644
--- a/src/cobalt/browser/lib/cobalt.def
+++ b/src/cobalt/browser/lib/cobalt.def
@@ -10,6 +10,9 @@
 
     ; From cobalt/network/lib/user_agent.h:
     CbLibUserAgentSetPlatformNameSuffix
+    CbLibUserAgentSetDeviceTypeOverride
+    CbLibUserAgentSetBrandNameOverride
+    CbLibUserAgentSetModelNameOverride
 
     ; From cobalt/browser/lib/exported/main.h:
     CbLibMainSetCallbackRegistrationReadyCallback
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index e7432ef..eb8db8f 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -73,6 +73,11 @@
 const char kMemoryTrackerHelp[] =
     "Enables memory tracking by installing the memory tracker on startup.";
 
+const char kMinCompatibilityVersion[] = "min_compatibility_version";
+const char kMinCompatibilityVersionHelp[] =
+    "The minimum version of Cobalt that will be checked during compatibility "
+    "validations.";
+
 const char kMinLogLevel[] = "min_log_level";
 const char kMinLogLevelHelp[] =
     "Set the minimum logging level: info|warning|error|fatal.";
@@ -165,6 +170,15 @@
 const char kDisableJavaScriptJitHelp[] =
     "Specifies that javascript jit should be disabled.";
 
+const char kEnableMapToMeshRectanglar[] = "enable_map_to_mesh_rectangular";
+const char kEnableMapToMeshRectanglarHelp[] =
+    "If toggled and map-to-mesh is supported on this platform, this allows it "
+    "to accept the 'rectangular' keyword. Useful to get rectangular stereo "
+    "video on platforms that do not support stereoscopy natively, letting the "
+    "client apply a stereo mesh projection (one that differs for each eye).";
+
+// If toggled, framerate statistics will be printed to stdout after each
+// animation completes, or after a maximum number of frames has been collected.
 const char kFPSPrint[] = "fps_stdout";
 const char kFPSPrintHelp[] =
     "If toggled, framerate statistics will be printed to stdout after each "
@@ -310,6 +324,7 @@
         {kFakeMicrophone, kFakeMicrophoneHelp},
         {kIgnoreCertificateErrors, kIgnoreCertificateErrorsHelp},
         {kInputFuzzer, kInputFuzzerHelp}, {kMemoryTracker, kMemoryTrackerHelp},
+        {kMinCompatibilityVersion, kMinCompatibilityVersionHelp},
         {kMinLogLevel, kMinLogLevelHelp},
         {kNullAudioStreamer, kNullAudioStreamerHelp},
         {kNullSavegame, kNullSavegameHelp},
@@ -328,6 +343,7 @@
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
         {kDisableJavaScriptJit, kDisableJavaScriptJitHelp},
+        {kEnableMapToMeshRectanglar, kEnableMapToMeshRectanglarHelp},
         {kFPSPrint, kFPSPrintHelp}, {kFPSOverlay, kFPSOverlayHelp},
         {kHelp, kHelpHelp},
         {kImageCacheSizeInBytes, kImageCacheSizeInBytesHelp},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 1280a2d..a46e11b 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -45,6 +45,8 @@
 extern const char kInputFuzzerHelp[];
 extern const char kMemoryTracker[];
 extern const char kMemoryTrackerHelp[];
+extern const char kMinCompatibilityVersion[];
+extern const char kMinCompatibilityVersionHelp[];
 extern const char kMinLogLevel[];
 extern const char kMinLogLevelHelp[];
 extern const char kNullAudioStreamer[];
@@ -85,6 +87,7 @@
 
 extern const char kDisableJavaScriptJit[];
 extern const char kDisableJavaScriptJitHelp[];
+extern const char kEnableMapToMeshRectanglar[];
 extern const char kFPSPrint[];
 extern const char kFPSPrintHelp[];
 extern const char kFPSOverlay[];
diff --git a/src/cobalt/browser/trace_manager.cc b/src/cobalt/browser/trace_manager.cc
index 5a5769f..3a28d07 100644
--- a/src/cobalt/browser/trace_manager.cc
+++ b/src/cobalt/browser/trace_manager.cc
@@ -90,7 +90,8 @@
 
   if (trace_to_file_) {
     LOG(INFO) << "Ending trace.";
-    LOG(INFO) << "Trace results in file \"" << kOutputTraceFilename << "\"";
+    LOG(INFO) << "Trace results in file \""
+              << trace_to_file_->absolute_output_path().value() << "\"";
     trace_to_file_.reset();
   } else {
     if (IsTracing()) {
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 39431de..dbb2ba4 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -27,6 +27,7 @@
 #include "base/message_loop_proxy.h"
 #include "base/optional.h"
 #include "base/stringprintf.h"
+#include "cobalt/base/c_val.h"
 #include "cobalt/base/language.h"
 #include "cobalt/base/startup_timer.h"
 #include "cobalt/base/tokens.h"
@@ -183,6 +184,8 @@
   // will be called. The event is not directed at a specific element.
   void InjectBeforeUnloadEvent();
 
+  void InjectCaptionSettingsChangedEvent();
+
   // Executes JavaScript in this WebModule. Sets the |result| output parameter
   // and signals |got_result|.
   void ExecuteJavascript(const std::string& script_utf8,
@@ -308,6 +311,9 @@
   // Simple flag used for basic error checking.
   bool is_running_;
 
+  // Whether or not a render tree has been produced but not yet rasterized.
+  base::CVal<bool, base::CValPublic> is_render_tree_rasterization_pending_;
+
   // Object that provides renderer resources like images and fonts.
   render_tree::ResourceProvider* resource_provider_;
   // The type id of resource provider being used by the WebModule. Whenever this
@@ -421,6 +427,9 @@
   base::Closure on_before_unload_fired_but_not_handled_;
 
   bool should_retain_remote_typeface_cache_on_suspend_;
+
+  scoped_refptr<cobalt::dom::captions::SystemCaptionSettings>
+      system_caption_settings_;
 };
 
 class WebModule::Impl::DocumentLoadedObserver : public dom::DocumentObserver {
@@ -445,6 +454,9 @@
 WebModule::Impl::Impl(const ConstructionData& data)
     : name_(data.options.name),
       is_running_(false),
+      is_render_tree_rasterization_pending_(
+          StringPrintf("%s.IsRenderTreeRasterizationPending", name_.c_str()),
+          false, "True when a render tree is produced but not yet rasterized."),
       resource_provider_(data.resource_provider),
       resource_provider_type_id_(data.resource_provider->GetTypeId()) {
   // Currently we rely on a platform to explicitly specify that it supports
@@ -463,7 +475,9 @@
   // testing whether it parses or not via the CSS.supports() Web API.
   css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh =
 #if defined(ENABLE_MAP_TO_MESH)
-      css_parser::Parser::kSupportsMapToMesh;
+      data.options.enable_map_to_mesh_rectangular
+          ? css_parser::Parser::kSupportsMapToMeshRectangular
+          : css_parser::Parser::kSupportsMapToMesh;
 #else
       css_parser::Parser::kDoesNotSupportMapToMesh;
 #endif
@@ -565,6 +579,9 @@
 
   media_session_client_ = media_session::MediaSessionClient::Create();
 
+  system_caption_settings_ =
+      new cobalt::dom::captions::SystemCaptionSettings();
+
   dom::Window::CacheCallback splash_screen_cache_callback =
       CacheUrlContentCallback(data.options.splash_screen_cache);
 
@@ -603,7 +620,8 @@
 #else
       dom::Window::kClockTypeSystemTime,
 #endif
-      splash_screen_cache_callback);
+      splash_screen_cache_callback,
+      system_caption_settings_);
   DCHECK(window_);
 
   window_weak_ = base::AsWeakPtr(window_.get());
@@ -842,19 +860,13 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(is_running_);
 
-  LayoutResults layout_results_with_callback(layout_results.render_tree,
-                                             layout_results.layout_time);
-
-  // Notify the stat tracker that a render tree has been produced.
+  is_render_tree_rasterization_pending_ = true;
   web_module_stat_tracker_->OnRenderTreeProduced();
 
-  // If the stat tracker is expecting to be notified of the rasterization time
-  // of the next render tree, then set a callback for this layout.
-  if (web_module_stat_tracker_->IsRenderTreeRasterizationPending()) {
-    layout_results_with_callback.on_rasterized_callback =
-        base::Bind(&WebModule::Impl::OnRenderTreeRasterized,
-                   base::Unretained(this), base::MessageLoopProxy::current());
-  }
+  LayoutResults layout_results_with_callback(
+      layout_results.render_tree, layout_results.layout_time,
+      base::Bind(&WebModule::Impl::OnRenderTreeRasterized,
+                 base::Unretained(this), base::MessageLoopProxy::current()));
 
 #if defined(ENABLE_DEBUG_CONSOLE)
   debug_overlay_->OnRenderTreeProduced(layout_results_with_callback);
@@ -874,6 +886,7 @@
     const base::TimeTicks& on_rasterize_time) {
   DCHECK(thread_checker_.CalledOnValidThread());
   web_module_stat_tracker_->OnRenderTreeRasterized(on_rasterize_time);
+  is_render_tree_rasterization_pending_ = false;
 }
 
 #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
@@ -1139,6 +1152,11 @@
   }
 }
 
+void WebModule::Impl::InjectCaptionSettingsChangedEvent() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  system_caption_settings_->OnCaptionSettingsChanged();
+}
+
 void WebModule::Impl::PurgeResourceCaches(
     bool should_retain_remote_typeface_cache) {
   image_cache_->Purge();
@@ -1188,6 +1206,7 @@
       image_cache_capacity(32 * 1024 * 1024),
       remote_typeface_cache_capacity(4 * 1024 * 1024),
       mesh_cache_capacity(COBALT_MESH_CACHE_SIZE_IN_BYTES),
+      enable_map_to_mesh_rectangular(false),
       csp_enforcement_mode(dom::kCspEnforcementEnable),
       csp_insecure_allowed_token(0),
       track_event_stats(false),
@@ -1390,6 +1409,16 @@
                                       base::Unretained(impl_.get())));
 }
 
+void WebModule::InjectCaptionSettingsChangedEvent() {
+  TRACE_EVENT0("cobalt::browser",
+               "WebModule::InjectCaptionSettingsChangedEvent()");
+  DCHECK(message_loop());
+  DCHECK(impl_);
+  message_loop()->PostTask(FROM_HERE,
+      base::Bind(&WebModule::Impl::InjectCaptionSettingsChangedEvent,
+                 base::Unretained(impl_.get())));
+}
+
 std::string WebModule::ExecuteJavascript(
     const std::string& script_utf8, const base::SourceLocation& script_location,
     bool* out_succeeded) {
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index c0b94b1..8b712db 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -137,6 +137,9 @@
     // Mesh cache capacity in bytes.
     int mesh_cache_capacity;
 
+    // Whether map-to-mesh for rectangular videos is enabled.
+    bool enable_map_to_mesh_rectangular;
+
     // Content Security Policy enforcement mode for this web module.
     dom::CspEnforcementType csp_enforcement_mode;
 
@@ -263,6 +266,8 @@
   // will be called.
   void InjectBeforeUnloadEvent();
 
+  void InjectCaptionSettingsChangedEvent();
+
   // Executes Javascript code in this web module.  The calling thread will
   // block until the JavaScript has executed and the output results are
   // available.
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc
index 55b4142..601cc5b 100644
--- a/src/cobalt/browser/web_module_stat_tracker.cc
+++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -124,7 +124,6 @@
 
 void WebModuleStatTracker::OnRenderTreeRasterized(
     const base::TimeTicks& on_rasterize_time) {
-  DCHECK(should_track_event_stats_);
   for (const auto& event_stats : event_stats_list_) {
     if (event_stats->is_render_tree_rasterization_pending) {
       event_stats->is_render_tree_rasterization_pending = false;
@@ -134,17 +133,6 @@
   }
 }
 
-bool WebModuleStatTracker::IsRenderTreeRasterizationPending() const {
-  if (should_track_event_stats_) {
-    for (const auto& event_stats : event_stats_list_) {
-      if (event_stats->is_render_tree_rasterization_pending) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
 WebModuleStatTracker::EventStats::EventStats(const std::string& name)
     : produced_render_tree(
           StringPrintf("Event.%s.ProducedRenderTree", name.c_str()), false,
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 411e4df..04ded3e 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-136005
\ No newline at end of file
+140175
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index 83cbb0f..950aad0 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -69,27 +69,17 @@
     #   'standard' -- The default package. It includes all sans-serif, serif,
     #                 and FCC fonts, non-CJK fallback fonts in both 'normal' and
     #                 'bold' weights, 'normal' weight CJK ('bold' weight CJK is
-    #                 synthesized from it), and historic script fonts. This
-    #                 package is ~31.4MB.
-    #   'limited_with_jp' -- A significantly smaller package than 'standard'.
-    #                 This package removes all but 'normal' and 'bold' weighted
+    #                 synthesized from it), historic script fonts, and color
+    #                 emojis. This package is ~38.3MB.
+    #   'limited'  -- A significantly smaller package than 'standard'. This
+    #                 package removes all but 'normal' and 'bold' weighted
     #                 sans-serif and serif, removes the FCC fonts (which must be
     #                 provided by the system or downloaded from the web),
-    #                 removes the 'bold' weighted non-CJK fallback fonts (the
-    #                 'normal' weight is still included and is used to
-    #                 synthesize bold), and replaces standard CJK with low
-    #                 quality CJK. However, higher quality Japanese is still
-    #                 included. Because low quality CJK cannot synthesize bold,
-    #                 bold glyphs are unavailable in Chinese and Korean.
-    #                 Historic script fonts are not included. This package is
-    #                 ~11.5MB.
-    #   'limited'  -- A smaller package than 'limited_with_jp'. The two packages
-    #                 are identical with the exception that 'limited' does not
-    #                 include the higher quality Japanese font; instead it
-    #                 relies on low quality CJK for all CJK characters. Because
-    #                 low quality CJK cannot synthesize bold, bold glyphs are
-    #                 unavailable in Chinese, Japanese, and Korean. This package
-    #                 is ~8.3MB.
+    #                 replaces standard CJK with low quality CJK, removes
+    #                 historic script fonts, and replaces colored emojis with
+    #                 uncolored ones. Because low quality CJK cannot synthesize
+    #                 bold, bold glyphs are unavailable in Chinese, Japanese and
+    #                 Korean. This package is ~8.3MB.
     #   'minimal'  -- The smallest possible font package. It only includes
     #                 Roboto's Basic Latin characters. Everything else must be
     #                 provided by the system or downloaded from the web. This
@@ -126,7 +116,6 @@
     'cobalt_font_package_override_fallback_lang_non_cjk%': -1,
     'cobalt_font_package_override_fallback_lang_cjk%': -1,
     'cobalt_font_package_override_fallback_lang_cjk_low_quality%': -1,
-    'cobalt_font_package_override_fallback_lang_jp%': -1,
     'cobalt_font_package_override_fallback_historic%': -1,
     'cobalt_font_package_override_fallback_color_emoji%': -1,
     'cobalt_font_package_override_fallback_emoji%': -1,
@@ -473,6 +462,7 @@
       'COBALT_BOX_DUMP_ENABLED',
       'COBALT_BUILD_TYPE_DEBUG',
       'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+      'COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS',
       'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
       'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
       'ENABLE_DEBUG_C_VAL',
@@ -492,6 +482,7 @@
       'ALLOCATOR_STATS_TRACKING',
       'COBALT_BUILD_TYPE_DEVEL',
       'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+      'COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS',
       'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
       'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
       'ENABLE_DEBUG_C_VAL',
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni
index 5e55fd2..d695cc9 100644
--- a/src/cobalt/build/config/base.gni
+++ b/src/cobalt/build/config/base.gni
@@ -44,24 +44,16 @@
 #   "standard" -- The default package. It includes all sans-serif, serif, and
 #                 FCC fonts, non-CJK fallback fonts in both 'normal' and 'bold'
 #                 weights, 'normal' weight CJK ('bold' weight CJK is synthesized
-#                 from it), and historic script fonts. This package is ~31.4MB.
-#   "limited_with_jp" -- A significantly smaller package than 'standard'. This
-#                 package removes all but 'normal' and 'bold' weighted
-#                 sans-serif and serif, removes the FCC fonts (which must be
-#                 provided by the system or downloaded from the web), removes
-#                 the 'bold' weighted non-CJK fallback fonts (the 'normal'
-#                 weight is still included and is used to synthesize bold), and
-#                 replaces standard CJK with low quality CJK. However, higher
-#                 quality Japanese is still included. Because low quality CJK
-#                 cannot synthesize bold, bold glyphs are unavailable in Chinese
-#                 and Korean. Historic script fonts are not included. This
-#                 package is ~11.5MB.
-#   "limited"  -- A smaller package than 'limited_with_jp'. The two packages are
-#                 identical with the exception that 'limited' does not include
-#                 the higher quality Japanese font; instead it relies on low
-#                 quality CJK for all CJK characters. Because low quality CJK
+#                 from it), historic script fonts, and color emojis. This
+#                 package is ~38.3MB.
+#   'limited'  -- A significantly smaller package than 'standard'. This package
+#                 removes all but 'normal' and 'bold' weighted sans-serif and
+#                 serif, removes the FCC fonts (which must be provided by the
+#                 system or downloaded from the web), replaces standard CJK with
+#                 low quality CJK, removes historic script fonts, and replaces
+#                 colored emojis with uncolored ones. Because low quality CJK
 #                 cannot synthesize bold, bold glyphs are unavailable in
-#                 Chinese, Japanese, and Korean. This package is ~8.3MB.
+#                 Chinese, Japanese and Korean. This package is ~8.3MB.
 #   "minimal"  -- The smallest possible font package. It only includes Roboto's
 #                 Basic Latin characters. Everything else must be provided by
 #                 the system or downloaded from the web. This package is
@@ -112,9 +104,6 @@
 if (!defined(cobalt_font_package_override_fallback_lang_cjk_low_quality)) {
   cobalt_font_package_override_fallback_lang_cjk_low_quality = -1
 }
-if (!defined(cobalt_font_package_override_fallback_lang_jp)) {
-  cobalt_font_package_override_fallback_lang_jp = -1
-}
 if (!defined(cobalt_font_package_override_fallback_historic)) {
   cobalt_font_package_override_fallback_historic = -1
 }
diff --git a/src/cobalt/content/fonts/README.md b/src/cobalt/content/fonts/README.md
index 7ce8582..749c1bc 100644
--- a/src/cobalt/content/fonts/README.md
+++ b/src/cobalt/content/fonts/README.md
@@ -54,43 +54,19 @@
                   'package_fallback_lang_non_cjk': 2,
                   'package_fallback_lang_cjk': 1,
                   'package_fallback_lang_cjk_low_quality': 0,
-                  'package_fallback_lang_jp': 0,
                   'package_fallback_historic': 1,
                   'package_fallback_color_emoji': 1,
                   'package_fallback_emoji': 0,
                   'package_fallback_symbols': 1,
 
-*  'limited_with_jp' -- A significantly smaller package than 'standard'. This
-                 package removes all but 'normal' and 'bold' weighted sans-serif
-                 and serif, removes the FCC fonts (which must be provided by the
-                 system or downloaded from the web), removes the 'bold' weighted
-                 non-CJK fallback fonts (the 'normal' weight is still included
-                 and is used to synthesize bold), and replaces standard CJK with
-                 low quality CJK. However, higher quality Japanese is still
-                 included. Because low quality CJK cannot synthesize bold, bold
-                 glyphs are unavailable in Chinese and Korean. Historic script
-                 fonts are not included. Uncolored emojis are used in place of
-                 the colored ones. This package is ~11.5MB.
-
-                 Package category values:
-                  'package_named_sans_serif': 2,
-                  'package_named_serif': 0,
-                  'package_named_fcc_fonts': 0,
-                  'package_fallback_lang_non_cjk': 1,
-                  'package_fallback_lang_cjk': 0,
-                  'package_fallback_lang_cjk_low_quality': 1,
-                  'package_fallback_lang_jp': 1,
-                  'package_fallback_historic': 0,
-                  'package_fallback_color_emoji': 0,
-                  'package_fallback_emoji': 1,
-                  'package_fallback_symbols': 1,
-
-*  'limited'  -- A smaller package than 'limited_with_jp'. The two packages are
-                 identical with the exception that 'limited' does not include
-                 the higher quality Japanese font; instead it relies on low
-                 quality CJK for all CJK characters. Because low quality CJK
+*  'limited'  -- A significantly smaller package than 'standard'. This package
+                 removes all but 'normal' and 'bold' weighted sans-serif and
+                 serif, removes the FCC fonts (which must be provided by the
+                 system or downloaded from the web), replaces standard CJK with
+                 low quality CJK, removes historic script fonts, and replaces
+                 colored emojis with uncolored ones. Because low quality CJK
                  cannot synthesize bold, bold glyphs are unavailable in Chinese,
-                 Japanese, and Korean. This package is ~8.3MB.
+                 Japanese and Korean. This package is ~8.3MB.
 
                  Package category values:
                   'package_named_sans_serif': 2,
@@ -99,7 +75,6 @@
                   'package_fallback_lang_non_cjk': 1,
                   'package_fallback_lang_cjk': 0,
                   'package_fallback_lang_cjk_low_quality': 1,
-                  'package_fallback_lang_jp': 0,
                   'package_fallback_historic': 0,
                   'package_fallback_color_emoji': 0,
                   'package_fallback_emoji': 1,
@@ -116,7 +91,6 @@
                   'package_fallback_lang_non_cjk': 0,
                   'package_fallback_lang_cjk': 0,
                   'package_fallback_lang_cjk_low_quality': 0,
-                  'package_fallback_lang_jp': 0,
                   'package_fallback_historic': 0,
                   'package_fallback_color_emoji': 0,
                   'package_fallback_emoji': 0,
@@ -153,10 +127,6 @@
        included when 'package_fallback_lang_cjk' has a value of '0'. This is the
        only category of fonts that is not synthetically boldable.
 
-  *  'package_fallback_lang_jp':
-       Higher quality Japanese language-specific fallback fonts. These should
-       only be included when 'package_fallback_lang_cjk' has a value of '0'.
-
   *  'package_fallback_historic':
        Historic script fallback fonts.
 
@@ -218,8 +188,6 @@
        'package_fallback_lang_cjk'
   *  'cobalt_font_package_override_fallback_lang_cjk_low_quality' ==>
        'package_fallback_lang_cjk_low_quality'
-  *  'cobalt_font_package_override_fallback_lang_jp' ==>
-       'package_fallback_lang_jp'
   *  'cobalt_font_package_override_fallback_color_emoji' ==>
        'package_fallback_color_emoji'
   *  'cobalt_font_package_override_fallback_emoji' ==>
diff --git a/src/cobalt/content/fonts/config/common/fonts.xml b/src/cobalt/content/fonts/config/common/fonts.xml
index 3bfaefd..bfa0cdf 100644
--- a/src/cobalt/content/fonts/config/common/fonts.xml
+++ b/src/cobalt/content/fonts/config/common/fonts.xml
@@ -437,9 +437,6 @@
     <family lang="ko" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
         <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
     </family>
-    <family lang="ja" pages="0,32,34-35,46-159,249-250,254-255,498,512-523,525-527,530-538,540-543,545-547,550,552,554-559,561,563-568,570,572-573,575-579,582-584,586-594,596-608,610-612,614-618,620,622-625,627-628,630-631,633-638,640,642-646,649-655,658,660-664,666,669-678,681,695-696,760-761">
-        <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
-    </family>
     <family pages="0,32-33,35-39,41,43,48,50,496-502,505,3584,4068,4072">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
     </family>
diff --git a/src/cobalt/content/fonts/font_files/NotoSansJP-Regular.otf b/src/cobalt/content/fonts/font_files/NotoSansJP-Regular.otf
deleted file mode 100644
index 40b064a..0000000
--- a/src/cobalt/content/fonts/font_files/NotoSansJP-Regular.otf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index 47f3bc5..f1d197e 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -242,6 +242,7 @@
 %token kPreLineToken                    // pre-line
 %token kPreWrapToken                    // pre-wrap
 %token kPurpleToken                     // purple
+%token kRectangularToken                // rectangular
 %token kRedToken                        // red
 %token kRepeatToken                     // repeat
 %token kRepeatXToken                    // repeat-x
@@ -6790,6 +6791,21 @@
           stereo_mode);
     }
   }
+  // map-to-mesh filter with the rectangular built-in mesh. Does not take FOV
+  // or transforms.
+  |  cobalt_mtm_function_name maybe_whitespace kRectangularToken comma
+        kNoneToken comma kNoneToken maybe_cobalt_mtm_stereo_mode
+        ')' maybe_whitespace {
+    scoped_refptr<cssom::KeywordValue> stereo_mode =
+        MakeScopedRefPtrAndRelease($8);
+
+    if (!parser_impl->supports_map_to_mesh_rectangular()) {
+      YYERROR;
+    } else {
+      $$ = new cssom::MapToMeshFunction(cssom::MapToMeshFunction::kRectangular,
+                                        stereo_mode);
+    }
+  }
   ;
 
 cobalt_map_to_mesh_spec:
diff --git a/src/cobalt/css_parser/parser.cc b/src/cobalt/css_parser/parser.cc
index 5397d14..fad2b67 100644
--- a/src/cobalt/css_parser/parser.cc
+++ b/src/cobalt/css_parser/parser.cc
@@ -176,6 +176,9 @@
   }
 
   bool supports_map_to_mesh() const { return supports_map_to_mesh_; }
+  bool supports_map_to_mesh_rectangular() const {
+    return supports_map_to_mesh_rectangular_;
+  }
 
  private:
   bool Parse();
@@ -218,6 +221,9 @@
 
   // Whether or not we support parsing "filter: map-to-mesh(...)".
   bool supports_map_to_mesh_;
+  // Whether or not we also support parsing
+  // "filter: map-to-mesh(rectangular, ...)".
+  bool supports_map_to_mesh_rectangular_;
 
   static void IncludeInputWithMessage(const std::string& input,
                                       const Parser::OnMessageCallback& callback,
@@ -257,8 +263,10 @@
       css_parser_(css_parser),
       scanner_(input_.c_str(), &string_pool_),
       into_declaration_data_(NULL),
-      supports_map_to_mesh_(supports_map_to_mesh ==
-                            Parser::kSupportsMapToMesh) {}
+      supports_map_to_mesh_(supports_map_to_mesh !=
+                            Parser::kDoesNotSupportMapToMesh),
+      supports_map_to_mesh_rectangular_(
+          supports_map_to_mesh == Parser::kSupportsMapToMeshRectangular) {}
 
 scoped_refptr<cssom::CSSStyleSheet> ParserImpl::ParseStyleSheet() {
   TRACK_MEMORY_SCOPE("CSS");
diff --git a/src/cobalt/css_parser/parser.h b/src/cobalt/css_parser/parser.h
index f556672..35ee0e9 100644
--- a/src/cobalt/css_parser/parser.h
+++ b/src/cobalt/css_parser/parser.h
@@ -27,6 +27,8 @@
  public:
   enum SupportsMapToMeshFlag {
     kSupportsMapToMesh,
+    // Additionally supports map-to-mesh on rectangular video.
+    kSupportsMapToMeshRectangular,
     kDoesNotSupportMapToMesh,
   };
 
diff --git a/src/cobalt/css_parser/scanner.cc b/src/cobalt/css_parser/scanner.cc
index 06f6eaa..9ad950b 100644
--- a/src/cobalt/css_parser/scanner.cc
+++ b/src/cobalt/css_parser/scanner.cc
@@ -2276,6 +2276,10 @@
         *property_value_token = kTransparentToken;
         return true;
       }
+      if (IsEqualToCssIdentifier(name.begin, cssom::kRectangularKeywordName)) {
+        *property_value_token = kRectangularToken;
+        return true;
+      }
       return false;
 
     case 12:
diff --git a/src/cobalt/cssom/compound_selector.h b/src/cobalt/cssom/compound_selector.h
index a9cbf44..90ea872 100644
--- a/src/cobalt/cssom/compound_selector.h
+++ b/src/cobalt/cssom/compound_selector.h
@@ -51,7 +51,7 @@
   // Rest of public methods.
 
   void AppendSelector(scoped_ptr<SimpleSelector> selector);
-  const SimpleSelectors& simple_selectors() { return simple_selectors_; }
+  const SimpleSelectors& simple_selectors() const { return simple_selectors_; }
   PseudoElement* pseudo_element() {
     if (has_pseudo_element_) {
       for (SimpleSelectors::iterator iter = simple_selectors_.begin();
diff --git a/src/cobalt/cssom/keyword_names.cc b/src/cobalt/cssom/keyword_names.cc
index 6ef873c..98f8ac3 100644
--- a/src/cobalt/cssom/keyword_names.cc
+++ b/src/cobalt/cssom/keyword_names.cc
@@ -85,6 +85,7 @@
 const char kPreLineKeywordName[] = "pre-line";
 const char kPreWrapKeywordName[] = "pre-wrap";
 const char kPurpleKeywordName[] = "purple";
+const char kRectangularKeywordName[] = "rectangular";
 const char kRedKeywordName[] = "red";
 const char kRelativeKeywordName[] = "relative";
 const char kRepeatKeywordName[] = "repeat";
diff --git a/src/cobalt/cssom/keyword_names.h b/src/cobalt/cssom/keyword_names.h
index a0956f6..57a0b46 100644
--- a/src/cobalt/cssom/keyword_names.h
+++ b/src/cobalt/cssom/keyword_names.h
@@ -87,6 +87,7 @@
 extern const char kPreLineKeywordName[];
 extern const char kPreWrapKeywordName[];
 extern const char kPurpleKeywordName[];
+extern const char kRectangularKeywordName[];
 extern const char kRedKeywordName[];
 extern const char kRelativeKeywordName[];
 extern const char kRepeatKeywordName[];
diff --git a/src/cobalt/cssom/map_to_mesh_function.cc b/src/cobalt/cssom/map_to_mesh_function.cc
index 800ffa6..6e408f0 100644
--- a/src/cobalt/cssom/map_to_mesh_function.cc
+++ b/src/cobalt/cssom/map_to_mesh_function.cc
@@ -29,6 +29,8 @@
 
   if (mesh_spec().mesh_type() == kEquirectangular) {
     result.append("equirectangular");
+  } else if (mesh_spec().mesh_type() == kRectangular) {
+    result.append("rectangular");
   } else if (mesh_spec().mesh_type() == kUrls) {
     result.append(mesh_spec().mesh_url()->ToString());
 
@@ -44,20 +46,26 @@
     }
   }
 
-  result.append(base::StringPrintf(", %.7grad", horizontal_fov_in_radians()));
-  result.append(base::StringPrintf(" %.7grad, ", vertical_fov_in_radians()));
+  if (mesh_spec().mesh_type() == kRectangular) {
+    // Rectangular filters carry neither FOV nor transforms.
+    result.append(", none, none, ");
+  } else {
+    result.append(base::StringPrintf(", %.7grad", horizontal_fov_in_radians()));
+    result.append(base::StringPrintf(" %.7grad, ", vertical_fov_in_radians()));
 
-  result.append("matrix3d(");
-  for (int col = 0; col <= 3; ++col) {
-    for (int row = 0; row <= 3; ++row) {
-      if (col > 0 || row > 0) {
-        result.append(", ");
+    result.append("matrix3d(");
+    for (int col = 0; col <= 3; ++col) {
+      for (int row = 0; row <= 3; ++row) {
+        if (col > 0 || row > 0) {
+          result.append(", ");
+        }
+        result.append(base::StringPrintf("%.7g", transform()[col][row]));
       }
-      result.append(base::StringPrintf("%.7g", transform()[col][row]));
     }
+
+    result.append("), ");
   }
 
-  result.append("), ");
   result.append(stereo_mode()->ToString());
   result.append(")");
 
diff --git a/src/cobalt/cssom/map_to_mesh_function.h b/src/cobalt/cssom/map_to_mesh_function.h
index 3b61145..e6f7d4e 100644
--- a/src/cobalt/cssom/map_to_mesh_function.h
+++ b/src/cobalt/cssom/map_to_mesh_function.h
@@ -55,6 +55,8 @@
   // Type of the source of the mesh: either a built-in mesh type or a custom
   // mesh.
   enum MeshSpecType {
+    // Built-in rectangular mesh.
+    kRectangular,
     // Built-in equirectangular mesh.
     kEquirectangular,
     // List of custom binary mesh URLs.
@@ -67,7 +69,7 @@
    public:
     explicit MeshSpec(MeshSpecType mesh_type) : mesh_type_(mesh_type) {
       // Check that this is a built-in mesh type.
-      DCHECK_EQ(mesh_type, kEquirectangular);
+      DCHECK(mesh_type == kRectangular || mesh_type == kEquirectangular);
     }
 
     MeshSpec(MeshSpecType mesh_type,
@@ -108,6 +110,7 @@
         transform_(transform),
         stereo_mode_(stereo_mode) {
     DCHECK(mesh_spec_);
+    DCHECK_NE(mesh_spec_->mesh_type(), kRectangular);
     DCHECK(stereo_mode_);
   }
 
@@ -135,6 +138,19 @@
         vertical_fov_in_radians_(vertical_fov_in_radians),
         transform_(transform),
         stereo_mode_(stereo_mode) {
+    DCHECK_NE(spec_type, kRectangular);
+    DCHECK(stereo_mode_);
+  }
+
+  // Alternate constructor for built-in meshes without FOV or transforms.
+  MapToMeshFunction(MeshSpecType spec_type,
+                    const scoped_refptr<KeywordValue>& stereo_mode)
+      : mesh_spec_(new MeshSpec(spec_type)),
+        horizontal_fov_in_radians_(0.0f),
+        vertical_fov_in_radians_(0.0f),
+        transform_(glm::mat4()),
+        stereo_mode_(stereo_mode) {
+    DCHECK_EQ(spec_type, kRectangular);
     DCHECK(stereo_mode_);
   }
 
diff --git a/src/cobalt/cssom/selector_tree.cc b/src/cobalt/cssom/selector_tree.cc
index 9e520f4..011032f 100644
--- a/src/cobalt/cssom/selector_tree.cc
+++ b/src/cobalt/cssom/selector_tree.cc
@@ -14,6 +14,11 @@
 
 #include "cobalt/cssom/selector_tree.h"
 
+#include <set>
+
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+#include "cobalt/base/version_compatibility.h"
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
 #include "cobalt/cssom/complex_selector.h"
 #include "cobalt/cssom/compound_selector.h"
 #include "cobalt/cssom/css_style_rule.h"
@@ -71,6 +76,110 @@
   return owned_nodes_map_[std::make_pair(node, combinator)];
 }
 
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+namespace {
+
+// This uses the old CompoundSelector compare logic that had a bug where 'not'
+// pseudo classes with the same prefix would be treated as matching, regardless
+// of their arguments.
+struct PreVersion16CompoundSelectorLessThan {
+  bool operator()(const CompoundSelector* lhs,
+                  const CompoundSelector* rhs) const {
+    const CompoundSelector::SimpleSelectors& lhs_simple_selectors =
+        lhs->simple_selectors();
+    const CompoundSelector::SimpleSelectors& rhs_simple_selectors =
+        rhs->simple_selectors();
+    if (lhs_simple_selectors.size() < rhs_simple_selectors.size()) {
+      return true;
+    }
+    if (lhs_simple_selectors.size() > rhs_simple_selectors.size()) {
+      return false;
+    }
+
+    for (size_t i = 0; i < lhs_simple_selectors.size(); ++i) {
+      if (lhs_simple_selectors[i]->type() < rhs_simple_selectors[i]->type()) {
+        return true;
+      }
+      if (lhs_simple_selectors[i]->type() > rhs_simple_selectors[i]->type()) {
+        return false;
+      }
+      if (lhs_simple_selectors[i]->prefix() <
+          rhs_simple_selectors[i]->prefix()) {
+        return true;
+      }
+      if (lhs_simple_selectors[i]->prefix() >
+          rhs_simple_selectors[i]->prefix()) {
+        return false;
+      }
+      if (lhs_simple_selectors[i]->text() < rhs_simple_selectors[i]->text()) {
+        return true;
+      }
+      if (lhs_simple_selectors[i]->text() > rhs_simple_selectors[i]->text()) {
+        return false;
+      }
+    }
+
+    return false;
+  }
+};
+
+bool HasNotPseudoClassCompatibilityViolations(
+    const SelectorTree::OwnedNodesMap& owned_nodes_map) {
+  constexpr int kNotPseudoClassCobaltVersionFix = 16;
+  if (base::VersionCompatibility::GetInstance()->GetMinimumVersion() >=
+      kNotPseudoClassCobaltVersionFix) {
+    return false;
+  }
+
+  typedef std::set<CompoundSelector*, PreVersion16CompoundSelectorLessThan>
+      PreVersion16CompoundSelectorLessThanSet;
+
+  // Walk all of the owned nodes looking for multiple compound selectors within
+  // them that are treated as duplicates using the pre-version 16 compound
+  // selector less than logic.
+  PreVersion16CompoundSelectorLessThanSet unsupported_usage_set;
+  for (const auto& owned_nodes_iterator : owned_nodes_map) {
+    PreVersion16CompoundSelectorLessThanSet encountered_set;
+    for (const auto& node_info : owned_nodes_iterator.second) {
+      if (!encountered_set.insert(node_info.first).second) {
+        unsupported_usage_set.insert(node_info.first);
+      }
+    }
+  }
+
+  // Log the cases of unsupported usage.
+  for (const auto& unsupported_usage_iterator : unsupported_usage_set) {
+    const std::string kUnsupportedNotUsageString =
+        "Unsupported :not() pseudo class usage found. Multiple negation pseudo "
+        "classes with the same prefix but different arguments are not "
+        "supported in versions of Cobalt <= 15. Offending selectors: '";
+
+    std::string selectors_string;
+    const CompoundSelector::SimpleSelectors& simple_selectors =
+        unsupported_usage_iterator->simple_selectors();
+    for (const auto& simple_selector : simple_selectors) {
+      selectors_string += simple_selector->prefix().c_str();
+      if (simple_selector->type() == kPseudoClass &&
+          simple_selector->GetContainedCompoundSelector() != NULL) {
+        selectors_string += "not(...)";
+      } else {
+        selectors_string += simple_selector->text().c_str();
+      }
+    }
+
+    base::VersionCompatibility::GetInstance()->ReportViolation(
+        kUnsupportedNotUsageString + selectors_string);
+  }
+  return !unsupported_usage_set.empty();
+}
+
+}  // namespace
+
+bool SelectorTree::ValidateVersionCompatibility() const {
+  return !HasNotPseudoClassCompatibilityViolations(owned_nodes_map_);
+}
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
 SelectorTree::Node* SelectorTree::GetOrCreateNodeForComplexSelector(
     ComplexSelector* complex_selector) {
   CompoundSelector* selector = complex_selector->first_selector();
diff --git a/src/cobalt/cssom/selector_tree.h b/src/cobalt/cssom/selector_tree.h
index 1192d3b..10f1545 100644
--- a/src/cobalt/cssom/selector_tree.h
+++ b/src/cobalt/cssom/selector_tree.h
@@ -140,6 +140,9 @@
     }
   };
 
+  typedef std::map<std::pair<const Node*, CombinatorType>, OwnedNodes>
+      OwnedNodesMap;
+
   struct PseudoClassNode {
     PseudoClassType pseudo_class_type;
     CombinatorType combinator_type;
@@ -265,6 +268,12 @@
   // Used by unit tests only.
   const OwnedNodes& children(const Node* node, CombinatorType combinator);
 
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+  // Validates the selector tree's compatibility against pre-selected versions
+  // of Cobalt. Returns true if there are no version compatibility violations.
+  bool ValidateVersionCompatibility() const;
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
  private:
   // Gets or creates node for complex selector, starting from root.
   Node* GetOrCreateNodeForComplexSelector(ComplexSelector* selector);
@@ -283,7 +292,7 @@
   // type.  It is only used when modifying the SelectorTree and is not used
   // during rule matching.  So we store it externally to the Node to minimize
   // the size of Node structure.
-  std::map<std::pair<const Node*, CombinatorType>, OwnedNodes> owned_nodes_map_;
+  OwnedNodesMap owned_nodes_map_;
 
   bool has_sibling_combinators_;
 
diff --git a/src/cobalt/cssom/selector_tree_test.cc b/src/cobalt/cssom/selector_tree_test.cc
index 2e909ce..304f5c9 100644
--- a/src/cobalt/cssom/selector_tree_test.cc
+++ b/src/cobalt/cssom/selector_tree_test.cc
@@ -12,9 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/cssom/selector_tree.h"
+
+#include "cobalt/base/version_compatibility.h"
 #include "cobalt/css_parser/parser.h"
 #include "cobalt/cssom/css_style_rule.h"
-#include "cobalt/cssom/selector_tree.h"
 #include "cobalt/cssom/specificity.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -48,6 +50,12 @@
                                           "[object SelectorTreeTest]", 1, 1))
           ->AsCSSStyleRule();
   selector_tree.AppendRule(css_style_rule_1);
+
+  // Verify that ValidateVersionCompatibility does not report a usage error
+  // when the minimum compatibility version is 1.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
+  EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
+
   ASSERT_EQ(
       0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
   ASSERT_EQ(1,
@@ -88,6 +96,12 @@
           ->AsCSSStyleRule();
   selector_tree.AppendRule(css_style_rule_1);
   selector_tree.AppendRule(css_style_rule_2);
+
+  // Verify that ValidateVersionCompatibility does not report a usage error
+  // when the minimum compatibility version is 1.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
+  EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
+
   ASSERT_EQ(
       0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
   ASSERT_EQ(1,
@@ -119,6 +133,11 @@
   selector_tree.AppendRule(css_style_rule_1);
   selector_tree.AppendRule(css_style_rule_2);
 
+  // Verify that ValidateVersionCompatibility does not report a usage error
+  // when the minimum compatibility version is 1.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
+  EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
+
   ASSERT_EQ(
       0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
   ASSERT_EQ(1,
@@ -160,6 +179,11 @@
   selector_tree.AppendRule(css_style_rule_1);
   selector_tree.AppendRule(css_style_rule_2);
 
+  // Verify that ValidateVersionCompatibility does not report a usage error
+  // when the minimum compatibility version is 1.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
+  EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
+
   ASSERT_EQ(
       0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
   ASSERT_EQ(1,
@@ -212,6 +236,16 @@
   selector_tree.AppendRule(css_style_rule_1);
   selector_tree.AppendRule(css_style_rule_2);
 
+  // Verify that ValidateVersionCompatibility does not report a usage error
+  // when the minimum compatibility version is 16.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(16);
+  EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
+
+  // Verify that ValidateVersionCompatibility reports a usage error when the
+  // minimum compatibility version is 1.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
+  EXPECT_FALSE(selector_tree.ValidateVersionCompatibility());
+
   ASSERT_EQ(
       0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
   ASSERT_EQ(2,
@@ -258,6 +292,11 @@
   selector_tree.AppendRule(css_style_rule_1);
   selector_tree.AppendRule(css_style_rule_2);
 
+  // Verify that ValidateVersionCompatibility does not report a usage error
+  // when the minimum compatibility version is 1.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
+  EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
+
   ASSERT_EQ(
       0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
   ASSERT_EQ(2,
@@ -303,6 +342,11 @@
   selector_tree.AppendRule(css_style_rule_1);
   selector_tree.AppendRule(css_style_rule_2);
 
+  // Verify that ValidateVersionCompatibility does not report a usage error
+  // when the minimum compatibility version is 1.
+  base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
+  EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
+
   ASSERT_EQ(
       0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
   ASSERT_EQ(1,
diff --git a/src/cobalt/debug/javascript_debugger_component.h b/src/cobalt/debug/javascript_debugger_component.h
index 6d14fc1..5b3b1a6 100644
--- a/src/cobalt/debug/javascript_debugger_component.h
+++ b/src/cobalt/debug/javascript_debugger_component.h
@@ -65,7 +65,7 @@
 
   // Script location, corresponding to physical breakpoint, etc.
   struct ScriptLocation {
-    ScriptLocation(const std::string script_id, int line_number,
+    ScriptLocation(const std::string& script_id, int line_number,
                    int column_number)
         : script_id(script_id),
           line_number(line_number),
diff --git a/src/cobalt/dom/captions/system_caption_settings.cc b/src/cobalt/dom/captions/system_caption_settings.cc
index 850e4ca..2535fa9 100644
--- a/src/cobalt/dom/captions/system_caption_settings.cc
+++ b/src/cobalt/dom/captions/system_caption_settings.cc
@@ -17,6 +17,8 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 
+#include "cobalt/base/accessibility_caption_settings_changed_event.h"
+#include "cobalt/base/event_dispatcher.h"
 #include "cobalt/dom/captions/caption_character_edge_style.h"
 #include "cobalt/dom/captions/caption_color.h"
 #include "cobalt/dom/captions/caption_font_family.h"
@@ -179,7 +181,10 @@
 
 #endif  // SB_HAS(CAPTIONS)
 
-SystemCaptionSettings::SystemCaptionSettings() {}
+void SystemCaptionSettings::OnCaptionSettingsChanged() {
+  DispatchEventAndRunCallback(base::Tokens::change(),
+                              base::Closure(base::Bind(base::DoNothing)));
+}
 
 base::optional<std::string> SystemCaptionSettings::background_color() {
 #if SB_HAS(CAPTIONS)
diff --git a/src/cobalt/dom/captions/system_caption_settings.h b/src/cobalt/dom/captions/system_caption_settings.h
index 71c9753..2fe541d 100644
--- a/src/cobalt/dom/captions/system_caption_settings.h
+++ b/src/cobalt/dom/captions/system_caption_settings.h
@@ -18,6 +18,8 @@
 
 #include <string>
 
+#include "cobalt/base/event.h"
+#include "cobalt/base/event_dispatcher.h"
 #include "cobalt/dom/captions/caption_character_edge_style.h"
 #include "cobalt/dom/captions/caption_color.h"
 #include "cobalt/dom/captions/caption_font_family.h"
@@ -32,7 +34,7 @@
 
 class SystemCaptionSettings : public EventTarget {
  public:
-  SystemCaptionSettings();
+  SystemCaptionSettings() {}
 
   base::optional<std::string> background_color();
   CaptionState background_color_state();
@@ -74,6 +76,8 @@
   const EventListenerScriptValue* onchanged() const;
   void set_onchanged(const EventListenerScriptValue& event_listener);
 
+  void OnCaptionSettingsChanged();
+
  private:
   // TODO: Delete these functions and their implementations if nullable
   // enums work with our IDL generation code. Make the implementations of our
diff --git a/src/cobalt/dom/csp_delegate.cc b/src/cobalt/dom/csp_delegate.cc
index b9afec6..743b0ae 100644
--- a/src/cobalt/dom/csp_delegate.cc
+++ b/src/cobalt/dom/csp_delegate.cc
@@ -113,6 +113,10 @@
 bool CspDelegateSecure::AllowInline(ResourceType type,
                                     const base::SourceLocation& location,
                                     const std::string& content) const {
+  // If CSP is not provided, allow inline script.
+  if (!was_header_received_) {
+    return true;
+  }
   bool can_load = false;
   if (type == kScript) {
     can_load = csp_->AllowInlineScript(location.file_path, location.line_number,
@@ -128,6 +132,8 @@
 
 bool CspDelegateSecure::AllowEval(std::string* eval_disabled_message) const {
   bool allow_eval =
+      // If CSP is not provided, allow eval() function.
+      !was_header_received_ ||
       csp_->AllowEval(csp::ContentSecurityPolicy::kSuppressReport);
   if (!allow_eval && eval_disabled_message) {
     *eval_disabled_message = csp_->disable_eval_error_message();
@@ -140,21 +146,21 @@
 }
 
 bool CspDelegateSecure::OnReceiveHeaders(const csp::ResponseHeaders& headers) {
-  if (headers.content_security_policy().empty()) {
+  was_header_received_ = !headers.content_security_policy().empty();
+  if (was_header_received_) {
+    csp_->OnReceiveHeaders(headers);
+  } else {
     // Didn't find Content-Security-Policy header.
     if (!headers.content_security_policy_report_only().empty()) {
       DLOG(INFO)
           << "Content-Security-Policy-Report-Only headers were "
              "received, but Content-Security-Policy headers are required.";
     }
-    return false;
   }
-  csp_->OnReceiveHeaders(headers);
-  was_header_received_ = true;
   if (!policy_changed_callback_.is_null()) {
     policy_changed_callback_.Run();
   }
-  return true;
+  return was_header_received_;
 }
 
 void CspDelegateSecure::OnReceiveHeader(const std::string& header,
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index 26bfaff..545505f 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -898,6 +898,12 @@
       AppendRulesFromCSSStyleSheetToSelectorTree(css_style_sheet,
                                                  selector_tree_.get());
     }
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+    // Now that the selector tree is fully updated, validate its version
+    // compatibility.
+    selector_tree_->ValidateVersionCompatibility();
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
     scoped_refptr<HTMLHtmlElement> current_html = html();
     if (current_html) {
       current_html->InvalidateMatchingRulesRecursively();
diff --git a/src/cobalt/dom/html_video_element.cc b/src/cobalt/dom/html_video_element.cc
index 8b63f37..dad9259 100644
--- a/src/cobalt/dom/html_video_element.cc
+++ b/src/cobalt/dom/html_video_element.cc
@@ -22,10 +22,10 @@
 namespace dom {
 
 #if defined(COBALT_MEDIA_SOURCE_2016)
-using media::ShellVideoFrameProvider;
+using media::VideoFrameProvider;
 using media::WebMediaPlayer;
 #else   // defined(COBALT_MEDIA_SOURCE_2016)
-using ::media::ShellVideoFrameProvider;
+using VideoFrameProvider = ::media::ShellVideoFrameProvider;
 using ::media::WebMediaPlayer;
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
@@ -87,8 +87,7 @@
       0.);  // total_frame_delay
 }
 
-scoped_refptr<ShellVideoFrameProvider>
-HTMLVideoElement::GetVideoFrameProvider() {
+scoped_refptr<VideoFrameProvider> HTMLVideoElement::GetVideoFrameProvider() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return player() ? player()->GetVideoFrameProvider() : NULL;
 }
diff --git a/src/cobalt/dom/html_video_element.h b/src/cobalt/dom/html_video_element.h
index 6e86996..fbfb27e 100644
--- a/src/cobalt/dom/html_video_element.h
+++ b/src/cobalt/dom/html_video_element.h
@@ -33,9 +33,9 @@
 class HTMLVideoElement : public HTMLMediaElement {
  public:
 #if defined(COBALT_MEDIA_SOURCE_2016)
-  typedef media::ShellVideoFrameProvider ShellVideoFrameProvider;
+  typedef media::VideoFrameProvider VideoFrameProvider;
 #else   // defined(COBALT_MEDIA_SOURCE_2016)
-  typedef ::media::ShellVideoFrameProvider ShellVideoFrameProvider;
+  typedef ::media::ShellVideoFrameProvider VideoFrameProvider;
 #endif  // defined(WebMediaPlayerDelegate)
 
   static const char kTagName[];
@@ -57,10 +57,7 @@
   // From HTMLElement
   scoped_refptr<HTMLVideoElement> AsHTMLVideoElement() override { return this; }
 
-  // TODO: ShellVideoFrameProvider is guaranteed to be long live and
-  // thread safe. However, it is actually a singleton internally. We should find
-  // a better way to support concurrent video playbacks.
-  scoped_refptr<ShellVideoFrameProvider> GetVideoFrameProvider();
+  scoped_refptr<VideoFrameProvider> GetVideoFrameProvider();
 
   WebMediaPlayer::SetBoundsCB GetSetBoundsCB();
 
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index 29ebdb4..49b810f 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -31,15 +31,16 @@
 namespace dom {
 
 Navigator::Navigator(const std::string& user_agent, const std::string& language,
-                     scoped_refptr<MediaSession> media_session,
-                     script::ScriptValueFactory* script_value_factory)
+    scoped_refptr<MediaSession> media_session,
+    scoped_refptr<cobalt::dom::captions::SystemCaptionSettings> captions,
+    script::ScriptValueFactory* script_value_factory)
     : user_agent_(user_agent),
       language_(language),
       mime_types_(new MimeTypeArray()),
       plugins_(new PluginArray()),
       media_session_(media_session),
       media_devices_(new media_capture::MediaDevices(script_value_factory)),
-      system_caption_settings_(new captions::SystemCaptionSettings()),
+      system_caption_settings_(captions),
       script_value_factory_(script_value_factory) {}
 
 const std::string& Navigator::language() const { return language_; }
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index ffdec86..f09f26f 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -40,8 +40,9 @@
 class Navigator : public script::Wrappable {
  public:
   Navigator(const std::string& user_agent, const std::string& language,
-            scoped_refptr<cobalt::media_session::MediaSession> media_session,
-            script::ScriptValueFactory* script_value_factory);
+      scoped_refptr<cobalt::media_session::MediaSession> media_session,
+      scoped_refptr<cobalt::dom::captions::SystemCaptionSettings> captions,
+      script::ScriptValueFactory* script_value_factory);
 
   // Web API: NavigatorID
   const std::string& user_agent() const;
diff --git a/src/cobalt/dom/on_screen_keyboard.cc b/src/cobalt/dom/on_screen_keyboard.cc
index 0e33eca..aa20b06 100644
--- a/src/cobalt/dom/on_screen_keyboard.cc
+++ b/src/cobalt/dom/on_screen_keyboard.cc
@@ -21,6 +21,7 @@
 
 namespace cobalt {
 namespace dom {
+
 OnScreenKeyboard::OnScreenKeyboard(
     OnScreenKeyboardBridge* bridge,
     script::ScriptValueFactory* script_value_factory)
@@ -33,12 +34,11 @@
 scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Show() {
   scoped_ptr<VoidPromiseValue> promise =
       script_value_factory_->CreateBasicPromise<void>();
-  VoidPromiseValue::StrongReference promise_reference(*promise);
   int ticket = next_ticket_++;
   bool is_emplaced =
       ticket_to_show_promise_map_
-          .emplace(ticket, std::unique_ptr<VoidPromiseValue::StrongReference>(
-                               new VoidPromiseValue::StrongReference(*promise)))
+          .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>(
+                               new VoidPromiseValue::TracedReference(*promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Show(data_.c_str(), ticket);
@@ -48,12 +48,11 @@
 scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Hide() {
   scoped_ptr<VoidPromiseValue> promise =
       script_value_factory_->CreateBasicPromise<void>();
-  VoidPromiseValue::StrongReference promise_reference(*promise);
   int ticket = next_ticket_++;
   bool is_emplaced =
       ticket_to_hide_promise_map_
-          .emplace(ticket, std::unique_ptr<VoidPromiseValue::StrongReference>(
-                               new VoidPromiseValue::StrongReference(*promise)))
+          .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>(
+                               new VoidPromiseValue::TracedReference(*promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Hide(ticket);
@@ -63,12 +62,11 @@
 scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Focus() {
   scoped_ptr<VoidPromiseValue> promise =
       script_value_factory_->CreateBasicPromise<void>();
-  VoidPromiseValue::StrongReference promise_reference(*promise);
   int ticket = next_ticket_++;
   bool is_emplaced =
       ticket_to_focus_promise_map_
-          .emplace(ticket, std::unique_ptr<VoidPromiseValue::StrongReference>(
-                               new VoidPromiseValue::StrongReference(*promise)))
+          .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>(
+                               new VoidPromiseValue::TracedReference(*promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Focus(ticket);
@@ -78,12 +76,11 @@
 scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Blur() {
   scoped_ptr<VoidPromiseValue> promise =
       script_value_factory_->CreateBasicPromise<void>();
-  VoidPromiseValue::StrongReference promise_reference(*promise);
   int ticket = next_ticket_++;
   bool is_emplaced =
       ticket_to_blur_promise_map_
-          .emplace(ticket, std::unique_ptr<VoidPromiseValue::StrongReference>(
-                               new VoidPromiseValue::StrongReference(*promise)))
+          .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>(
+                               new VoidPromiseValue::TracedReference(*promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Blur(ticket);
@@ -198,5 +195,14 @@
   DispatchEvent(new dom::Event(base::Tokens::blur()));
 }
 
+void OnScreenKeyboard::TraceMembers(script::Tracer* tracer) {
+  EventTarget::TraceMembers(tracer);
+
+  tracer->TraceValues(ticket_to_hide_promise_map_);
+  tracer->TraceValues(ticket_to_show_promise_map_);
+  tracer->TraceValues(ticket_to_focus_promise_map_);
+  tracer->TraceValues(ticket_to_blur_promise_map_);
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/on_screen_keyboard.h b/src/cobalt/dom/on_screen_keyboard.h
index 2f436f9..4159a4a 100644
--- a/src/cobalt/dom/on_screen_keyboard.h
+++ b/src/cobalt/dom/on_screen_keyboard.h
@@ -38,7 +38,7 @@
   typedef script::ScriptValue<script::Promise<void>> VoidPromiseValue;
 
   typedef std::unordered_map<int,
-                             std::unique_ptr<VoidPromiseValue::StrongReference>>
+                             std::unique_ptr<VoidPromiseValue::TracedReference>>
       TicketToPromiseMap;
 
   OnScreenKeyboard(OnScreenKeyboardBridge* bridge,
@@ -87,6 +87,7 @@
   void DispatchBlurEvent(int ticket);
 
   DEFINE_WRAPPABLE_TYPE(OnScreenKeyboard);
+  void TraceMembers(script::Tracer* tracer) override;
 
  private:
   ~OnScreenKeyboard() override {}
diff --git a/src/cobalt/dom/rule_matching.cc b/src/cobalt/dom/rule_matching.cc
index 7e8f67b..f764718 100644
--- a/src/cobalt/dom/rule_matching.cc
+++ b/src/cobalt/dom/rule_matching.cc
@@ -434,7 +434,7 @@
 void ForEachChildOnNodes(
     const NodeSet& node_set, cssom::CombinatorType combinator_type,
     HTMLElement* element,
-    base::Callback<void(HTMLElement* element, const SelectorTree::Node*)>
+    const base::Callback<void(HTMLElement* element, const SelectorTree::Node*)>&
         callback) {
   // Gathering Phase: Generate candidate nodes from the node set.
   SelectorTree::NodeSet<kRuleMatchingNodeSetSize> candidate_nodes;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 0a5f9ce..8da7ca4 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -119,7 +119,8 @@
                const scoped_refptr<MediaSession>& media_session,
                int csp_insecure_allowed_token, int dom_max_element_depth,
                float video_playback_rate_multiplier, ClockType clock_type,
-               const CacheCallback& splash_screen_cache_callback)
+               const CacheCallback& splash_screen_cache_callback,
+               const scoped_refptr<captions::SystemCaptionSettings>& captions)
     : width_(width),
       height_(height),
       device_pixel_ratio_(device_pixel_ratio),
@@ -159,7 +160,7 @@
       document_loader_(NULL),
       history_(new History()),
       navigator_(new Navigator(user_agent, language, media_session,
-                               script_value_factory)),
+                               captions, script_value_factory)),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           relay_on_load_event_(new RelayLoadEvent(this))),
       console_(new Console(execution_state)),
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 0f3079f..1afd941 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -27,6 +27,7 @@
 #include "cobalt/cssom/css_parser.h"
 #include "cobalt/cssom/css_style_declaration.h"
 #include "cobalt/dom/animation_frame_request_callback_list.h"
+#include "cobalt/dom/captions/system_caption_settings.h"
 #include "cobalt/dom/crypto.h"
 #include "cobalt/dom/csp_delegate_type.h"
 #include "cobalt/dom/dom_stat_tracker.h"
@@ -155,7 +156,8 @@
       int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0,
       float video_playback_rate_multiplier = 1.f,
       ClockType clock_type = kClockTypeSystemTime,
-      const CacheCallback& splash_screen_cache_callback = CacheCallback());
+      const CacheCallback& splash_screen_cache_callback = CacheCallback(),
+      const scoped_refptr<captions::SystemCaptionSettings>& captions = nullptr);
 
   // Web API: Window
   //
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index 8502790..7fe2d0e 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -43,7 +43,7 @@
 #include "cobalt/render_tree/image.h"
 #include "cobalt/web_animations/keyframe_effect_read_only.h"
 #if defined(COBALT_MEDIA_SOURCE_2016)
-#include "cobalt/media/base/shell_video_frame_provider.h"
+#include "cobalt/media/base/video_frame_provider.h"
 #else  // defined(COBALT_MEDIA_SOURCE_2016)
 #include "media/base/shell_video_frame_provider.h"
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
@@ -53,17 +53,16 @@
 namespace layout {
 
 #if defined(COBALT_MEDIA_SOURCE_2016)
-using media::ShellVideoFrameProvider;
-using media::VideoFrame;
+using media::VideoFrameProvider;
 #else   // defined(COBALT_MEDIA_SOURCE_2016)
-using ::media::ShellVideoFrameProvider;
+using VideoFrameProvider = ::media::ShellVideoFrameProvider;
 using ::media::VideoFrame;
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
 namespace {
 
 scoped_refptr<render_tree::Image> GetVideoFrame(
-    const scoped_refptr<ShellVideoFrameProvider>& frame_provider,
+    const scoped_refptr<VideoFrameProvider>& frame_provider,
     render_tree::ResourceProvider* resource_provider) {
   TRACE_EVENT0("cobalt::layout", "GetVideoFrame()");
   SbDecodeTarget decode_target = frame_provider->GetCurrentSbDecodeTarget();
@@ -76,13 +75,14 @@
 #endif  // SB_HAS(GRAPHICS)
   } else {
     DCHECK(frame_provider);
+#if !defined(COBALT_MEDIA_SOURCE_2016)
     scoped_refptr<VideoFrame> video_frame = frame_provider->GetCurrentFrame();
     if (video_frame && video_frame->texture_id()) {
       scoped_refptr<render_tree::Image> image =
           reinterpret_cast<render_tree::Image*>(video_frame->texture_id());
       return image;
     }
-
+#endif  // !defined(COBALT_MEDIA_SOURCE_2016)
     return NULL;
   }
 }
@@ -316,8 +316,8 @@
   //   https://www.w3.org/TR/CSS21/visuren.html#propdef-unicode-bidi
   //   https://www.w3.org/TR/css3-text/#line-break-details
   int32 text_position =
-      (*paragraph_)->AppendCodePoint(
-          Paragraph::kObjectReplacementCharacterCodePoint);
+      (*paragraph_)
+          ->AppendCodePoint(Paragraph::kObjectReplacementCharacterCodePoint);
 
   render_tree::ResourceProvider* resource_provider =
       *video_element->node_document()
@@ -328,11 +328,10 @@
   // or not.
   base::optional<bool> is_punch_out;
   if (video_element->GetVideoFrameProvider()) {
-    ShellVideoFrameProvider::OutputMode output_mode =
+    VideoFrameProvider::OutputMode output_mode =
         video_element->GetVideoFrameProvider()->GetOutputMode();
-    if (output_mode != ShellVideoFrameProvider::kOutputModeInvalid) {
-      is_punch_out =
-          output_mode == ShellVideoFrameProvider::kOutputModePunchOut;
+    if (output_mode != VideoFrameProvider::kOutputModeInvalid) {
+      is_punch_out = output_mode == VideoFrameProvider::kOutputModePunchOut;
     }
   }
 
@@ -543,8 +542,9 @@
       //   https://www.w3.org/TR/CSS21/visuren.html#propdef-unicode-bidi
       //   https://www.w3.org/TR/css3-text/#line-break-details
       int32 text_position =
-          (*paragraph_)->AppendCodePoint(
-              Paragraph::kObjectReplacementCharacterCodePoint);
+          (*paragraph_)
+              ->AppendCodePoint(
+                  Paragraph::kObjectReplacementCharacterCodePoint);
       scoped_refptr<Paragraph> prior_paragraph = *paragraph_;
 
       // The inline block creates a new paragraph, which the old paragraph
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index 0af0b8e..0e3f920 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -207,18 +207,6 @@
 }
 
 namespace {
-void AddLetterboxFillRects(const LetterboxDimensions& dimensions,
-                           CompositionNode::Builder* composition_node_builder) {
-  const render_tree::ColorRGBA kSolidBlack(0, 0, 0, 1);
-
-  for (uint32 i = 0; i < dimensions.fill_rects.size(); ++i) {
-    const math::RectF& fill_rect = dimensions.fill_rects[i];
-    composition_node_builder->AddChild(new RectNode(
-        fill_rect,
-        scoped_ptr<render_tree::Brush>(new SolidColorBrush(kSolidBlack))));
-  }
-}
-
 void AddLetterboxedImageToRenderTree(
     const LetterboxDimensions& dimensions,
     const scoped_refptr<render_tree::Image>& image,
@@ -227,8 +215,6 @@
     ImageNode::Builder image_builder(image, *dimensions.image_rect);
     composition_node_builder->AddChild(new ImageNode(image_builder));
   }
-
-  AddLetterboxFillRects(dimensions, composition_node_builder);
 }
 
 void AddLetterboxedPunchThroughVideoNodeToRenderTree(
@@ -240,7 +226,6 @@
                                            set_bounds_cb);
     border_node_builder->AddChild(new PunchThroughVideoNode(builder));
   }
-  AddLetterboxFillRects(dimensions, border_node_builder);
 }
 
 void AnimateVideoImage(const ReplacedBox::ReplaceImageCB& replace_image_cb,
@@ -255,9 +240,9 @@
   }
 }
 
-// Animates an image, and additionally adds letterbox rectangles as well
-// according to the aspect ratio of the resulting animated image versus the
-// aspect ratio of the destination box size.
+// Animates an image, and letterboxes the image as well according to the aspect
+// ratio of the resulting animated image versus the aspect ratio of the
+// destination box size.
 void AnimateVideoWithLetterboxing(
     const ReplacedBox::ReplaceImageCB& replace_image_cb,
     math::SizeF destination_size,
@@ -324,7 +309,8 @@
       cssom::MapToMeshFunction::ExtractFromFilterList(
           computed_style()->filter());
 
-  if (mtm_filter_function) {
+  if (mtm_filter_function && mtm_filter_function->mesh_spec().mesh_type() !=
+                                 cssom::MapToMeshFunction::kRectangular) {
     DCHECK(!*is_video_punched_out_)
         << "We currently do not support punched out video with map-to-mesh "
            "filters.";
@@ -338,11 +324,18 @@
       animate_node_builder.Add(
           image_node, base::Bind(&AnimateVideoImage, replace_image_cb_));
 
+      render_tree::StereoMode stereo_mode = render_tree::kMono;
+
+      if (mtm_filter_function) {
+        // For rectangular stereo.
+        stereo_mode = ReadStereoMode(mtm_filter_function->stereo_mode());
+      }
+
       // Attach an empty map to mesh filter node to signal the need for an
       // external mesh.
-      border_node_builder->AddChild(
-          new FilterNode(MapToMeshFilter(render_tree::kMono),
-                         new AnimateNode(animate_node_builder, image_node)));
+      border_node_builder->AddChild(new FilterNode(
+          MapToMeshFilter(stereo_mode, render_tree::kRectangular),
+          new AnimateNode(animate_node_builder, image_node)));
       return;
     }
 #endif
@@ -611,6 +604,7 @@
   render_tree::StereoMode stereo_mode =
       ReadStereoMode(stereo_mode_keyword_value);
 
+  scoped_refptr<render_tree::Node> filter_node;
   // Fetch either the embedded equirectangular mesh or a custom one depending
   // on the spec.
   MapToMeshFilter::Builder builder;
@@ -685,10 +679,10 @@
             loader::mesh::MeshProjection::kLeftEyeOrMonoCollection),
         mesh_projection->GetMesh(
             loader::mesh::MeshProjection::kRightEyeCollection));
-  }
 
-  scoped_refptr<render_tree::Node> filter_node =
-      new FilterNode(MapToMeshFilter(stereo_mode, builder), animate_node);
+    filter_node =
+        new FilterNode(MapToMeshFilter(stereo_mode, builder), animate_node);
+  }
 
 #if !SB_HAS(VIRTUAL_REALITY)
   // Attach a 3D camera to the map-to-mesh node, so the rendering of its
diff --git a/src/cobalt/loader/cors_preflight_cache.cc b/src/cobalt/loader/cors_preflight_cache.cc
index 860ba27..32d53b3 100644
--- a/src/cobalt/loader/cors_preflight_cache.cc
+++ b/src/cobalt/loader/cors_preflight_cache.cc
@@ -61,7 +61,7 @@
   if (methods_vec.size() == 1 && methods_vec.at(0) == "*") {
     new_entry->allow_all_methods = true;
   } else {
-    for (auto method : methods_vec) {
+    for (const auto& method : methods_vec) {
       net::URLFetcher::RequestType request_type;
       if (MethodNameToRequestType(method, &request_type)) {
         new_entry->methods.insert(request_type);
@@ -74,7 +74,7 @@
   }
   // TODO: Consider change this function to use std::copy with std::inserter.
   // Currently compilers on some machines do not support it.
-  for (auto headername : headernames_vec) {
+  for (const auto& headername : headernames_vec) {
     new_entry->headernames.insert(headername);
   }
 
@@ -124,7 +124,7 @@
   // name("Authentication") and last preflight allowed * headers.
   if (entry_ptr->allow_all_headers_except_non_wildcard) {
     bool has_auth_header = false;
-    for (auto header : unsafe_headernames) {
+    for (const auto& header : unsafe_headernames) {
       if (SbStringCompareNoCase(header.c_str(), kAuthorization)) {
         has_auth_header = true;
         break;
@@ -137,7 +137,7 @@
   }
   // If last preflight does not allow arbitrary header, then match each header
   // with allowed headers.
-  for (auto unsafe_headername : unsafe_headernames) {
+  for (const auto& unsafe_headername : unsafe_headernames) {
     if (entry_ptr->headernames.find(unsafe_headername) ==
         entry_ptr->headernames.end()) {
       return false;
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h
index 927b03d..4dd622f 100644
--- a/src/cobalt/media/base/pipeline.h
+++ b/src/cobalt/media/base/pipeline.h
@@ -26,6 +26,7 @@
 #include "cobalt/media/base/media_export.h"
 #include "cobalt/media/base/pipeline_status.h"
 #include "cobalt/media/base/ranges.h"
+#include "cobalt/media/base/video_frame_provider.h"
 #include "starboard/drm.h"
 #include "starboard/window.h"
 #include "ui/gfx/rect.h"
@@ -75,7 +76,7 @@
   static scoped_refptr<Pipeline> Create(
       PipelineWindow window,
       const scoped_refptr<base::MessageLoopProxy>& message_loop,
-      MediaLog* media_log);
+      MediaLog* media_log, VideoFrameProvider* video_frame_provider);
 
   virtual ~Pipeline() {}
 
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index afd247a..85fbc6b 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -77,7 +77,8 @@
   // Constructs a media pipeline that will execute on |message_loop|.
   SbPlayerPipeline(PipelineWindow window,
                    const scoped_refptr<base::MessageLoopProxy>& message_loop,
-                   MediaLog* media_log);
+                   MediaLog* media_log,
+                   VideoFrameProvider* video_frame_provider);
   ~SbPlayerPipeline() override;
 
   void Suspend() override;
@@ -242,13 +243,15 @@
   bool stopped_;
   bool ended_;
 
+  VideoFrameProvider* video_frame_provider_;
+
   DISALLOW_COPY_AND_ASSIGN(SbPlayerPipeline);
 };
 
 SbPlayerPipeline::SbPlayerPipeline(
     PipelineWindow window,
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
-    MediaLog* media_log)
+    MediaLog* media_log, VideoFrameProvider* video_frame_provider)
     : window_(window),
       message_loop_(message_loop),
       natural_size_(0, 0),
@@ -262,7 +265,8 @@
       set_bounds_helper_(new SbPlayerSetBoundsHelper),
       suspended_(false),
       stopped_(false),
-      ended_(false) {}
+      ended_(false),
+      video_frame_provider_(video_frame_provider) {}
 
 SbPlayerPipeline::~SbPlayerPipeline() { DCHECK(!player_); }
 
@@ -745,7 +749,7 @@
     player_.reset(new StarboardPlayer(
         message_loop_, source_url, window_, this, set_bounds_helper_.get(),
         *decode_to_texture_output_mode_,
-        on_encrypted_media_init_data_encountered_cb_));
+        on_encrypted_media_init_data_encountered_cb_, video_frame_provider_));
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
   }
@@ -819,7 +823,8 @@
     base::AutoLock auto_lock(lock_);
     player_.reset(new StarboardPlayer(
         message_loop_, audio_config, video_config, window_, drm_system, this,
-        set_bounds_helper_.get(), *decode_to_texture_output_mode_));
+        set_bounds_helper_.get(), *decode_to_texture_output_mode_,
+        video_frame_provider_));
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
   }
@@ -880,6 +885,15 @@
 
   DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
   DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
+
+#if SB_API_VERSION < SB_AUDIOLESS_VIDEO_API_VERSION
+  if (audio_stream == NULL) {
+    LOG(INFO) << "The video has to contain an audio track.";
+    ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
+    return;
+  }
+#endif  // SB_API_VERSION < SB_AUDIOLESS_VIDEO_API_VERSION
+
   if (video_stream == NULL) {
     LOG(INFO) << "The video has to contain a video track.";
     ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
@@ -1156,8 +1170,9 @@
 scoped_refptr<Pipeline> Pipeline::Create(
     PipelineWindow window,
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
-    MediaLog* media_log) {
-  return new SbPlayerPipeline(window, message_loop, media_log);
+    MediaLog* media_log, VideoFrameProvider* video_frame_provider) {
+  return new SbPlayerPipeline(window, message_loop, media_log,
+                              video_frame_provider);
 }
 
 }  // namespace media
diff --git a/src/cobalt/media/base/shell_audio_bus.cc b/src/cobalt/media/base/shell_audio_bus.cc
index aea8fb0..aa13966 100644
--- a/src/cobalt/media/base/shell_audio_bus.cc
+++ b/src/cobalt/media/base/shell_audio_bus.cc
@@ -53,6 +53,29 @@
   }
 }
 
+void Sum(const float* source, float* destination, size_t size) {
+  for (int i = 0; i < size; ++i) {
+    *destination += *source;
+    ++source;
+    ++destination;
+  }
+}
+
+void Sum(const int16* source, int16* destination, size_t size) {
+  for (int i = 0; i < size; ++i) {
+    int sample_in_int32 = *destination + static_cast<int>(*source);
+    if (sample_in_int32 > std::numeric_limits<int16>::max()) {
+      *destination = std::numeric_limits<int16>::max();
+    } else if (sample_in_int32 < std::numeric_limits<int16>::min()) {
+      *destination = std::numeric_limits<int16>::min();
+    } else {
+      *destination = static_cast<int16>(sample_in_int32);
+    }
+    ++source;
+    ++destination;
+  }
+}
+
 }  // namespace
 
 ShellAudioBus::ShellAudioBus(size_t channels, size_t frames,
@@ -148,6 +171,17 @@
   return channel_data_[channel];
 }
 
+uint8* ShellAudioBus::interleaved_data() {
+  DCHECK_EQ(storage_type_, kInterleaved);
+  return channel_data_[0];
+}
+
+uint8* ShellAudioBus::planar_data(size_t channel) {
+  DCHECK_LT(channel, channels_);
+  DCHECK_EQ(storage_type_, kPlanar);
+  return channel_data_[channel];
+}
+
 void ShellAudioBus::ZeroFrames(size_t start_frame, size_t end_frame) {
   DCHECK_LE(start_frame, end_frame);
   DCHECK_LE(end_frame, frames_);
@@ -223,84 +257,103 @@
   }
 }
 
-template <typename SourceSampleType, typename DestSampleType,
-          StorageType SourceStorageType, StorageType DestStorageType>
-void ShellAudioBus::MixForTypes(const ShellAudioBus& source) {
+template <StorageType SourceStorageType, StorageType DestStorageType>
+void ShellAudioBus::MixFloatSamples(const ShellAudioBus& source) {
   const size_t frames = std::min(frames_, source.frames_);
 
+  if (SourceStorageType == DestStorageType) {
+    if (SourceStorageType == kInterleaved) {
+      Sum(reinterpret_cast<const float*>(source.interleaved_data()),
+          reinterpret_cast<float*>(interleaved_data()), frames * channels_);
+      return;
+    }
+    for (size_t channel = 0; channel < channels_; ++channel) {
+      Sum(reinterpret_cast<const float*>(source.planar_data(channel)),
+          reinterpret_cast<float*>(planar_data(channel)), frames);
+    }
+    return;
+  }
+
   for (size_t channel = 0; channel < channels_; ++channel) {
     for (size_t frame = 0; frame < frames; ++frame) {
-      *reinterpret_cast<DestSampleType*>(
-          GetSamplePtrForType<DestSampleType, DestStorageType>(channel,
-                                                               frame)) +=
-          source.GetSampleForType<SourceSampleType, SourceStorageType>(channel,
-                                                                       frame);
+      *reinterpret_cast<float*>(
+          GetSamplePtrForType<float, DestStorageType>(channel, frame)) +=
+          source.GetSampleForType<float, SourceStorageType>(channel, frame);
+    }
+  }
+}
+
+template <StorageType SourceStorageType, StorageType DestStorageType>
+void ShellAudioBus::MixInt16Samples(const ShellAudioBus& source) {
+  const size_t frames = std::min(frames_, source.frames_);
+
+  if (SourceStorageType == DestStorageType) {
+    if (SourceStorageType == kInterleaved) {
+      Sum(reinterpret_cast<const int16*>(source.interleaved_data()),
+          reinterpret_cast<int16*>(interleaved_data()), frames * channels_);
+      return;
+    }
+    for (size_t channel = 0; channel < channels_; ++channel) {
+      Sum(reinterpret_cast<const int16*>(source.planar_data(channel)),
+          reinterpret_cast<int16*>(planar_data(channel)), frames);
+    }
+    return;
+  }
+
+  for (size_t channel = 0; channel < channels_; ++channel) {
+    for (size_t frame = 0; frame < frames; ++frame) {
+      auto& dest_sample = *reinterpret_cast<int16*>(
+          GetSamplePtrForType<int16, DestStorageType>(channel, frame));
+      int source_sample =
+          source.GetSampleForType<int16, SourceStorageType>(channel, frame);
+      if (dest_sample + source_sample > std::numeric_limits<int16>::max()) {
+        dest_sample = std::numeric_limits<int16>::max();
+      } else if (dest_sample + source_sample <
+                 std::numeric_limits<int16>::min()) {
+        dest_sample = std::numeric_limits<int16>::min();
+      } else {
+        dest_sample += source_sample;
+      }
     }
   }
 }
 
 void ShellAudioBus::Mix(const ShellAudioBus& source) {
   DCHECK_EQ(channels_, source.channels_);
+  DCHECK_EQ(sample_type_, source.sample_type_);
 
-  if (channels_ != source.channels_) {
+  if (channels_ != source.channels_ || sample_type_ != source.sample_type_) {
     ZeroAllFrames();
     return;
   }
 
   // Profiling has identified this area of code as hot, so instead of calling
-  // GetSamplePtr, which branches each time it is called, we branch once
-  // before we loop and inline the branch of the function we want.
-  if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
-      source.storage_type_ == kInterleaved && storage_type_ == kInterleaved) {
-    MixForTypes<int16, int16, kInterleaved, kInterleaved>(source);
-  } else if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
-             source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
-    MixForTypes<int16, int16, kInterleaved, kPlanar>(source);
-  } else if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
-             source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
-    MixForTypes<int16, int16, kPlanar, kInterleaved>(source);
-  } else if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
-             source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
-    MixForTypes<int16, int16, kPlanar, kPlanar>(source);
-  } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kInterleaved &&
+  // GetSamplePtr, which branches each time it is called, we branch once before
+  // we loop and inline the branch of the function we want.
+  if (source.sample_type_ == kInt16 && source.storage_type_ == kInterleaved &&
+      storage_type_ == kInterleaved) {
+    MixInt16Samples<kInterleaved, kInterleaved>(source);
+  } else if (sample_type_ == kInt16 && source.storage_type_ == kInterleaved &&
+             storage_type_ == kPlanar) {
+    MixInt16Samples<kInterleaved, kPlanar>(source);
+  } else if (sample_type_ == kInt16 && source.storage_type_ == kPlanar &&
              storage_type_ == kInterleaved) {
-    MixForTypes<int16, float, kInterleaved, kInterleaved>(source);
-  } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
-    MixForTypes<int16, float, kInterleaved, kPlanar>(source);
-  } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
-    MixForTypes<int16, float, kPlanar, kInterleaved>(source);
-  } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
-    MixForTypes<int16, float, kPlanar, kPlanar>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
-             source.storage_type_ == kInterleaved &&
+    MixInt16Samples<kPlanar, kInterleaved>(source);
+  } else if (sample_type_ == kInt16 && source.storage_type_ == kPlanar &&
+             storage_type_ == kPlanar) {
+    MixInt16Samples<kPlanar, kPlanar>(source);
+  } else if (sample_type_ == kFloat32 && source.storage_type_ == kInterleaved &&
              storage_type_ == kInterleaved) {
-    MixForTypes<float, int16, kInterleaved, kInterleaved>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
-             source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
-    MixForTypes<float, int16, kInterleaved, kPlanar>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
-             source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
-    MixForTypes<float, int16, kPlanar, kInterleaved>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
-             source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
-    MixForTypes<float, int16, kPlanar, kPlanar>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kInterleaved &&
+    MixFloatSamples<kInterleaved, kInterleaved>(source);
+  } else if (sample_type_ == kFloat32 && source.storage_type_ == kInterleaved &&
+             storage_type_ == kPlanar) {
+    MixFloatSamples<kInterleaved, kPlanar>(source);
+  } else if (sample_type_ == kFloat32 && source.storage_type_ == kPlanar &&
              storage_type_ == kInterleaved) {
-    MixForTypes<float, float, kInterleaved, kInterleaved>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
-    MixForTypes<float, float, kInterleaved, kPlanar>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
-    MixForTypes<float, float, kPlanar, kInterleaved>(source);
-  } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
-             source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
-    MixForTypes<float, float, kPlanar, kPlanar>(source);
+    MixFloatSamples<kPlanar, kInterleaved>(source);
+  } else if (sample_type_ == kFloat32 && source.storage_type_ == kPlanar &&
+             storage_type_ == kPlanar) {
+    MixFloatSamples<kPlanar, kPlanar>(source);
   } else {
     NOTREACHED();
   }
diff --git a/src/cobalt/media/base/shell_audio_bus.h b/src/cobalt/media/base/shell_audio_bus.h
index c9ef550..833d2dc 100644
--- a/src/cobalt/media/base/shell_audio_bus.h
+++ b/src/cobalt/media/base/shell_audio_bus.h
@@ -63,6 +63,8 @@
   size_t GetSampleSizeInBytes() const;
   const uint8* interleaved_data() const;
   const uint8* planar_data(size_t channel) const;
+  uint8* interleaved_data();
+  uint8* planar_data(size_t channel);
 
   int16 GetInt16Sample(size_t channel, size_t frame) const {
     DCHECK_EQ(sample_type_, kInt16);
@@ -130,9 +132,11 @@
         GetSamplePtrForType<SampleTypeName, T>(channel, frame));
   }
 
-  template <typename SourceSampleType, typename DestSampleType,
-            StorageType SourceStorageType, StorageType DestStorageType>
-  void MixForTypes(const ShellAudioBus& source);
+  template <StorageType SourceStorageType, StorageType DestStorageType>
+  void MixFloatSamples(const ShellAudioBus& source);
+
+  template <StorageType SourceStorageType, StorageType DestStorageType>
+  void MixInt16Samples(const ShellAudioBus& source);
 
  private:
   void SetFloat32Sample(size_t channel, size_t frame, float sample) {
diff --git a/src/cobalt/media/base/shell_media_platform.h b/src/cobalt/media/base/shell_media_platform.h
index 1e6c711..30f502f 100644
--- a/src/cobalt/media/base/shell_media_platform.h
+++ b/src/cobalt/media/base/shell_media_platform.h
@@ -21,7 +21,6 @@
 #include "cobalt/media/base/decoder_buffer.h"
 #include "cobalt/media/base/limits.h"
 #include "cobalt/media/base/media_export.h"
-#include "cobalt/media/base/shell_video_frame_provider.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "starboard/decode_target.h"
 
@@ -59,10 +58,6 @@
   virtual size_t GetSourceBufferStreamAudioMemoryLimit() const = 0;
   virtual size_t GetSourceBufferStreamVideoMemoryLimit() const = 0;
 
-  virtual scoped_refptr<ShellVideoFrameProvider> GetVideoFrameProvider() {
-    return NULL;
-  }
-
   virtual SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() {
     return NULL;
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index f85526a..5bed9c5 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -76,7 +76,8 @@
     const std::string& url, SbWindow window, Host* host,
     SbPlayerSetBoundsHelper* set_bounds_helper, bool prefer_decode_to_texture,
     const OnEncryptedMediaInitDataEncounteredCB&
-        on_encrypted_media_init_data_encountered_cb)
+        on_encrypted_media_init_data_encountered_cb,
+    VideoFrameProvider* const video_frame_provider)
     : url_(url),
       message_loop_(message_loop),
       callback_helper_(
@@ -93,7 +94,8 @@
       seek_pending_(false),
       state_(kPlaying),
       on_encrypted_media_init_data_encountered_cb_(
-          on_encrypted_media_init_data_encountered_cb) {
+          on_encrypted_media_init_data_encountered_cb),
+      video_frame_provider_(video_frame_provider) {
   DCHECK(host_);
   DCHECK(set_bounds_helper_);
 
@@ -113,7 +115,8 @@
     const AudioDecoderConfig& audio_config,
     const VideoDecoderConfig& video_config, SbWindow window,
     SbDrmSystem drm_system, Host* host,
-    SbPlayerSetBoundsHelper* set_bounds_helper, bool prefer_decode_to_texture)
+    SbPlayerSetBoundsHelper* set_bounds_helper, bool prefer_decode_to_texture,
+    VideoFrameProvider* const video_frame_provider)
     : message_loop_(message_loop),
       callback_helper_(
           new CallbackHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this))),
@@ -129,10 +132,12 @@
       volume_(1.0),
       playback_rate_(0.0),
       seek_pending_(false),
-      state_(kPlaying) {
+      state_(kPlaying),
+      video_frame_provider_(video_frame_provider) {
   DCHECK(video_config.IsValidConfig());
   DCHECK(host_);
   DCHECK(set_bounds_helper_);
+  DCHECK(video_frame_provider_);
 
   output_mode_ = ComputeSbPlayerOutputMode(
       MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system,
@@ -153,11 +158,8 @@
   callback_helper_->ResetPlayer();
   set_bounds_helper_->SetPlayer(NULL);
 
-  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
-      ShellVideoFrameProvider::kOutputModeInvalid);
-  ShellMediaPlatform::Instance()
-      ->GetVideoFrameProvider()
-      ->ResetGetCurrentSbDecodeTargetFunction();
+  video_frame_provider_->SetOutputMode(VideoFrameProvider::kOutputModeInvalid);
+  video_frame_provider_->ResetGetCurrentSbDecodeTargetFunction();
 
   if (SbPlayerIsValid(player_)) {
     SbPlayerDestroy(player_);
@@ -423,11 +425,8 @@
   preroll_timestamp_ = SbMediaTimeToTimeDelta(info.current_media_pts);
 
   set_bounds_helper_->SetPlayer(NULL);
-  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
-      ShellVideoFrameProvider::kOutputModeInvalid);
-  ShellMediaPlatform::Instance()
-      ->GetVideoFrameProvider()
-      ->ResetGetCurrentSbDecodeTargetFunction();
+  video_frame_provider_->SetOutputMode(VideoFrameProvider::kOutputModeInvalid);
+  video_frame_provider_->ResetGetCurrentSbDecodeTargetFunction();
 
   SbPlayerDestroy(player_);
 
@@ -459,19 +458,19 @@
 }
 
 namespace {
-ShellVideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
+VideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
     SbPlayerOutputMode output_mode) {
   switch (output_mode) {
     case kSbPlayerOutputModeDecodeToTexture:
-      return ShellVideoFrameProvider::kOutputModeDecodeToTexture;
+      return VideoFrameProvider::kOutputModeDecodeToTexture;
     case kSbPlayerOutputModePunchOut:
-      return ShellVideoFrameProvider::kOutputModePunchOut;
+      return VideoFrameProvider::kOutputModePunchOut;
     case kSbPlayerOutputModeInvalid:
-      return ShellVideoFrameProvider::kOutputModeInvalid;
+      return VideoFrameProvider::kOutputModeInvalid;
   }
 
   NOTREACHED();
-  return ShellVideoFrameProvider::kOutputModeInvalid;
+  return VideoFrameProvider::kOutputModeInvalid;
 }
 
 }  // namespace
@@ -503,13 +502,10 @@
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
     // If the player is setup to decode to texture, then provide Cobalt with
     // a method of querying that texture.
-    ShellMediaPlatform::Instance()
-        ->GetVideoFrameProvider()
-        ->SetGetCurrentSbDecodeTargetFunction(
-            base::Bind(&StarboardPlayer::GetCurrentSbDecodeTarget,
-                       base::Unretained(this)));
+    video_frame_provider_->SetGetCurrentSbDecodeTargetFunction(base::Bind(
+        &StarboardPlayer::GetCurrentSbDecodeTarget, base::Unretained(this)));
   }
-  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+  video_frame_provider_->SetOutputMode(
       ToVideoFrameProviderOutputMode(output_mode_));
 
   set_bounds_helper_->SetPlayer(this);
@@ -552,13 +548,10 @@
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
     // If the player is setup to decode to texture, then provide Cobalt with
     // a method of querying that texture.
-    ShellMediaPlatform::Instance()
-        ->GetVideoFrameProvider()
-        ->SetGetCurrentSbDecodeTargetFunction(
-            base::Bind(&StarboardPlayer::GetCurrentSbDecodeTarget,
-                       base::Unretained(this)));
+    video_frame_provider_->SetGetCurrentSbDecodeTargetFunction(base::Bind(
+        &StarboardPlayer::GetCurrentSbDecodeTarget, base::Unretained(this)));
   }
-  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+  video_frame_provider_->SetOutputMode(
       ToVideoFrameProviderOutputMode(output_mode_));
 
   set_bounds_helper_->SetPlayer(this);
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index d1708f3..43522a4 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -29,6 +29,7 @@
 #include "cobalt/media/base/demuxer_stream.h"
 #include "cobalt/media/base/sbplayer_set_bounds_helper.h"
 #include "cobalt/media/base/video_decoder_config.h"
+#include "cobalt/media/base/video_frame_provider.h"
 #include "starboard/media.h"
 #include "starboard/player.h"
 
@@ -58,14 +59,16 @@
                   SbPlayerSetBoundsHelper* set_bounds_helper,
                   bool prefer_decode_to_texture,
                   const OnEncryptedMediaInitDataEncounteredCB&
-                      encrypted_media_init_data_encountered_cb);
+                      encrypted_media_init_data_encountered_cb,
+                  VideoFrameProvider* const video_frame_provider);
 #else   // SB_HAS(PLAYER_WITH_URL)
   StarboardPlayer(const scoped_refptr<base::MessageLoopProxy>& message_loop,
                   const AudioDecoderConfig& audio_config,
                   const VideoDecoderConfig& video_config, SbWindow window,
                   SbDrmSystem drm_system, Host* host,
                   SbPlayerSetBoundsHelper* set_bounds_helper,
-                  bool prefer_decode_to_texture);
+                  bool prefer_decode_to_texture,
+                  VideoFrameProvider* const video_frame_provider);
 #endif  // SB_HAS(PLAYER_WITH_URL)
   ~StarboardPlayer();
 
@@ -220,6 +223,8 @@
 
   // Keep track of the output mode we are supposed to output to.
   SbPlayerOutputMode output_mode_;
+
+  VideoFrameProvider* const video_frame_provider_;
 };
 
 }  // namespace media
diff --git a/src/cobalt/media/base/shell_video_frame_provider.h b/src/cobalt/media/base/video_frame_provider.h
similarity index 67%
rename from src/cobalt/media/base/shell_video_frame_provider.h
rename to src/cobalt/media/base/video_frame_provider.h
index e2b1569..e2581b0 100644
--- a/src/cobalt/media/base/shell_video_frame_provider.h
+++ b/src/cobalt/media/base/video_frame_provider.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_BASE_SHELL_VIDEO_FRAME_PROVIDER_H_
-#define COBALT_MEDIA_BASE_SHELL_VIDEO_FRAME_PROVIDER_H_
+#ifndef COBALT_MEDIA_BASE_VIDEO_FRAME_PROVIDER_H_
+#define COBALT_MEDIA_BASE_VIDEO_FRAME_PROVIDER_H_
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
@@ -24,22 +24,13 @@
 namespace cobalt {
 namespace media {
 
-// TODO: The following class is tentative to make the new media stack work.
-//       We should consider remove VideoFrame as it is no longer useful.
-class VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
- public:
-  uintptr_t texture_id() const { return 0; }
-  base::TimeDelta GetTimestamp() const { return base::TimeDelta(); }
-};
-
-// TODO: Remove Shell prefix.
-// The ShellVideoFrameProvider manages the backlog for video frames. It has the
+// The VideoFrameProvider manages the backlog for video frames. It has the
 // following functionalities:
 // 1. It caches the video frames ready to be displayed.
 // 2. It decides which frame to be displayed at the current time.
 // 3. It removes frames that will no longer be displayed.
-class ShellVideoFrameProvider
-    : public base::RefCountedThreadSafe<ShellVideoFrameProvider> {
+class VideoFrameProvider
+    : public base::RefCountedThreadSafe<VideoFrameProvider> {
  public:
   enum OutputMode {
     kOutputModePunchOut,
@@ -47,26 +38,24 @@
     kOutputModeInvalid,
   };
 
-  ShellVideoFrameProvider() : output_mode_(kOutputModeInvalid) {}
+  VideoFrameProvider() : output_mode_(kOutputModeInvalid) {}
 
   typedef base::Callback<SbDecodeTarget()> GetCurrentSbDecodeTargetFunction;
 
-  scoped_refptr<VideoFrame> GetCurrentFrame() { return NULL; }
-
   void SetOutputMode(OutputMode output_mode) {
     base::AutoLock auto_lock(lock_);
     output_mode_ = output_mode;
   }
 
-  ShellVideoFrameProvider::OutputMode GetOutputMode() const {
+  VideoFrameProvider::OutputMode GetOutputMode() const {
     base::AutoLock auto_lock(lock_);
     return output_mode_;
   }
 
   // For Starboard platforms that have a decode-to-texture player, we enable
-  // this ShellVideoFrameProvider to act as a bridge for Cobalt code to query
+  // this VideoFrameProvider to act as a bridge for Cobalt code to query
   // for the current SbDecodeTarget.  In effect, we bypass all of
-  // ShellVideoFrameProvider's logic in this case, instead relying on the
+  // VideoFrameProvider's logic in this case, instead relying on the
   // Starboard implementation to provide us with the current video frame when
   // needed.
   void SetGetCurrentSbDecodeTargetFunction(
@@ -95,10 +84,10 @@
   OutputMode output_mode_;
   GetCurrentSbDecodeTargetFunction get_current_sb_decode_target_function_;
 
-  DISALLOW_COPY_AND_ASSIGN(ShellVideoFrameProvider);
+  DISALLOW_COPY_AND_ASSIGN(VideoFrameProvider);
 };
 
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_BASE_SHELL_VIDEO_FRAME_PROVIDER_H_
+#endif  // COBALT_MEDIA_BASE_VIDEO_FRAME_PROVIDER_H_
diff --git a/src/cobalt/media/media2.gyp b/src/cobalt/media/media2.gyp
index 82cf946..8a5e99d 100644
--- a/src/cobalt/media/media2.gyp
+++ b/src/cobalt/media/media2.gyp
@@ -106,6 +106,7 @@
         'base/video_codecs.h',
         'base/video_decoder_config.cc',
         'base/video_decoder_config.h',
+        'base/video_frame_provider.h',
         'base/video_util.cc',
         'base/video_util.h',
         'filters/chunk_demuxer.cc',
diff --git a/src/cobalt/media/media_module_starboard.cc b/src/cobalt/media/media_module_starboard.cc
index ddaf1fb..d0c01cd 100644
--- a/src/cobalt/media/media_module_starboard.cc
+++ b/src/cobalt/media/media_module_starboard.cc
@@ -81,8 +81,7 @@
       window = system_window_->GetSbWindow();
     }
     return make_scoped_ptr<WebMediaPlayer>(new media::WebMediaPlayerImpl(
-        window, client, this, &decoder_buffer_allocator_,
-        media_platform_.GetVideoFrameProvider(), new media::MediaLog));
+        window, client, this, &decoder_buffer_allocator_, new media::MediaLog));
 #else   // defined(COBALT_MEDIA_SOURCE_2016)
     scoped_ptr<MessageLoopFactory> message_loop_factory(new MessageLoopFactory);
     scoped_refptr<base::MessageLoopProxy> pipeline_message_loop =
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index fbe7df1..1c4055c 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -19,7 +19,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time.h"
 #include "cobalt/media/base/ranges.h"
-#include "cobalt/media/base/shell_video_frame_provider.h"
+#include "cobalt/media/base/video_frame_provider.h"
 #include "cobalt/media/filters/chunk_demuxer.h"
 #include "cobalt/media/player/buffered_data_source.h"
 #include "googleurl/src/gurl.h"
@@ -145,7 +145,7 @@
   virtual unsigned GetAudioDecodedByteCount() const = 0;
   virtual unsigned GetVideoDecodedByteCount() const = 0;
 
-  virtual scoped_refptr<ShellVideoFrameProvider> GetVideoFrameProvider() {
+  virtual scoped_refptr<VideoFrameProvider> GetVideoFrameProvider() {
     return NULL;
   }
 
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index aa4e26b..cd232c7 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -107,7 +107,6 @@
     PipelineWindow window, WebMediaPlayerClient* client,
     WebMediaPlayerDelegate* delegate,
     DecoderBuffer::Allocator* buffer_allocator,
-    const scoped_refptr<ShellVideoFrameProvider>& video_frame_provider,
     const scoped_refptr<MediaLog>& media_log)
     : pipeline_thread_("media_pipeline"),
       network_state_(WebMediaPlayer::kNetworkStateEmpty),
@@ -116,7 +115,6 @@
       client_(client),
       delegate_(delegate),
       buffer_allocator_(buffer_allocator),
-      video_frame_provider_(video_frame_provider),
       proxy_(new WebMediaPlayerProxy(main_loop_->message_loop_proxy(), this)),
       media_log_(media_log),
       incremented_externally_allocated_memory_(false),
@@ -126,6 +124,8 @@
       drm_system_(NULL) {
   TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::WebMediaPlayerImpl");
 
+  video_frame_provider_ = new VideoFrameProvider();
+
   DLOG_IF(ERROR, s_instance)
       << "More than one WebMediaPlayerImpl has been created.";
   s_instance = this;
@@ -136,7 +136,7 @@
 
   pipeline_thread_.Start();
   pipeline_ = Pipeline::Create(window, pipeline_thread_.message_loop_proxy(),
-                               media_log_);
+                               media_log_, video_frame_provider_);
 
   // Also we want to be notified of |main_loop_| destruction.
   main_loop_->AddDestructionObserver(this);
@@ -561,8 +561,7 @@
   return stats.video_bytes_decoded;
 }
 
-scoped_refptr<ShellVideoFrameProvider>
-WebMediaPlayerImpl::GetVideoFrameProvider() {
+scoped_refptr<VideoFrameProvider> WebMediaPlayerImpl::GetVideoFrameProvider() {
   return video_frame_provider_;
 }
 
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index 0876e89..f9e62c1 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -63,6 +63,7 @@
 #include "cobalt/media/base/eme_constants.h"
 #include "cobalt/media/base/pipeline.h"
 #include "cobalt/media/base/ranges.h"
+#include "cobalt/media/base/video_frame_provider.h"
 #include "cobalt/media/player/web_media_player.h"
 #include "cobalt/media/player/web_media_player_delegate.h"
 #include "googleurl/src/gurl.h"
@@ -108,7 +109,6 @@
       PipelineWindow window, WebMediaPlayerClient* client,
       WebMediaPlayerDelegate* delegate,
       DecoderBuffer::Allocator* buffer_allocator,
-      const scoped_refptr<ShellVideoFrameProvider>& video_frame_provider,
       const scoped_refptr<MediaLog>& media_log);
   ~WebMediaPlayerImpl() override;
 
@@ -175,7 +175,7 @@
   unsigned GetAudioDecodedByteCount() const override;
   unsigned GetVideoDecodedByteCount() const override;
 
-  scoped_refptr<ShellVideoFrameProvider> GetVideoFrameProvider() override;
+  scoped_refptr<VideoFrameProvider> GetVideoFrameProvider() override;
 
   SetBoundsCB GetSetBoundsCB() override;
 
@@ -296,7 +296,7 @@
   WebMediaPlayerClient* client_;
   WebMediaPlayerDelegate* delegate_;
   DecoderBuffer::Allocator* buffer_allocator_;
-  scoped_refptr<ShellVideoFrameProvider> video_frame_provider_;
+  scoped_refptr<VideoFrameProvider> video_frame_provider_;
 
   scoped_refptr<WebMediaPlayerProxy> proxy_;
 
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc
index cc31daa..d017bb4 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.cc
+++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -99,9 +99,11 @@
   delete client_;
 }
 
+#if !defined(COBALT_MEDIA_SOURCE_2016)
 scoped_refptr<VideoFrame> WebMediaPlayerHelper::GetCurrentFrame() const {
   return player_->GetVideoFrameProvider()->GetCurrentFrame();
 }
+#endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 
 SbDecodeTarget WebMediaPlayerHelper::GetCurrentDecodeTarget() const {
   return player_->GetVideoFrameProvider()->GetCurrentSbDecodeTarget();
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.h b/src/cobalt/media/sandbox/web_media_player_helper.h
index 297ac94..ab5e3f1 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.h
+++ b/src/cobalt/media/sandbox/web_media_player_helper.h
@@ -23,7 +23,7 @@
 #include "cobalt/media/media_module.h"
 #include "googleurl/src/gurl.h"
 #if defined(COBALT_MEDIA_SOURCE_2016)
-#include "cobalt/media/base/shell_video_frame_provider.h"
+#include "cobalt/media/base/video_frame_provider.h"
 #include "cobalt/media/player/web_media_player.h"
 #else  // defined(COBALT_MEDIA_SOURCE_2016)
 #include "media/base/video_frame.h"
@@ -57,7 +57,9 @@
                        const GURL& video_url);
   ~WebMediaPlayerHelper();
 
+#if !defined(COBALT_MEDIA_SOURCE_2016)
   scoped_refptr<VideoFrame> GetCurrentFrame() const;
+#endif  // !defined(COBALT_MEDIA_SOURCE_2016)
   SbDecodeTarget GetCurrentDecodeTarget() const;
   bool IsPlaybackFinished() const;
 
diff --git a/src/cobalt/media/sandbox/web_media_player_sandbox.cc b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
index 6c4be8a..b6e63b2 100644
--- a/src/cobalt/media/sandbox/web_media_player_sandbox.cc
+++ b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
@@ -28,11 +28,9 @@
 #include "cobalt/media/sandbox/media_sandbox.h"
 #include "cobalt/media/sandbox/web_media_player_helper.h"
 #include "cobalt/render_tree/image.h"
-#if defined(COBALT_MEDIA_SOURCE_2016)
-#include "cobalt/media/base/shell_video_frame_provider.h"
-#else  // defined(COBALT_MEDIA_SOURCE_2016)
+#if !defined(COBALT_MEDIA_SOURCE_2016)
 #include "media/base/video_frame.h"
-#endif  // defined(COBALT_MEDIA_SOURCE_2016)
+#endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 #include "net/base/net_util.h"
 #include "starboard/event.h"
 #include "starboard/file.h"
@@ -339,9 +337,12 @@
       return media_sandbox_.resource_provider()->CreateImageFromSbDecodeTarget(
           decode_target);
     }
-
+#if !defined(COBALT_MEDIA_SOURCE_2016)
     scoped_refptr<VideoFrame> frame = player_helper_->GetCurrentFrame();
     return frame ? reinterpret_cast<Image*>(frame->texture_id()) : NULL;
+#else   // !defined(COBALT_MEDIA_SOURCE_2016)
+    return NULL;
+#endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 #else   // SB_HAS(GRAPHICS)
     return NULL;
 #endif  // SB_HAS(GRAPHICS)
diff --git a/src/cobalt/media/shell_media_platform_starboard.h b/src/cobalt/media/shell_media_platform_starboard.h
index 6c9a807..9229fee 100644
--- a/src/cobalt/media/shell_media_platform_starboard.h
+++ b/src/cobalt/media/shell_media_platform_starboard.h
@@ -30,8 +30,7 @@
  public:
   explicit ShellMediaPlatformStarboard(
       cobalt::render_tree::ResourceProvider* resource_provider)
-      : resource_provider_(resource_provider),
-        video_frame_provider_(new ShellVideoFrameProvider) {
+      : resource_provider_(resource_provider) {
     SetInstance(this);
   }
 
@@ -40,10 +39,6 @@
     SetInstance(NULL);
   }
 
-  scoped_refptr<ShellVideoFrameProvider> GetVideoFrameProvider() override {
-    return video_frame_provider_;
-  }
-
   SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() override {
 #if SB_HAS(GRAPHICS)
@@ -78,7 +73,6 @@
   }
 
   cobalt::render_tree::ResourceProvider* resource_provider_;
-  scoped_refptr<ShellVideoFrameProvider> video_frame_provider_;
 };
 
 }  // namespace media
@@ -91,7 +85,6 @@
 #include "base/memory/aligned_memory.h"
 #include "base/memory/scoped_ptr.h"
 #include "cobalt/media/shell_video_data_allocator_common.h"
-#include "media/base/shell_video_frame_provider.h"
 #include "nb/memory_pool.h"
 #include "starboard/common/locked_ptr.h"
 
@@ -111,9 +104,6 @@
   size_t GetSourceBufferStreamVideoMemoryLimit() const override {
     return SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT;
   }
-  scoped_refptr<ShellVideoFrameProvider> GetVideoFrameProvider() override {
-    return video_frame_provider_;
-  }
   int GetMaxVideoPrerollFrames() const override {
     return SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES;
   }
@@ -152,7 +142,6 @@
   starboard::LockedPtr<nb::MemoryPool> main_memory_pool_;
 
   ShellVideoDataAllocatorCommon video_data_allocator_;
-  scoped_refptr<ShellVideoFrameProvider> video_frame_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellMediaPlatformStarboard);
 };
diff --git a/src/cobalt/network/lib/exported/user_agent.h b/src/cobalt/network/lib/exported/user_agent.h
index 06af638..0b72f83 100644
--- a/src/cobalt/network/lib/exported/user_agent.h
+++ b/src/cobalt/network/lib/exported/user_agent.h
@@ -19,6 +19,7 @@
 #define COBALT_NETWORK_LIB_EXPORTED_USER_AGENT_H_
 
 #include "starboard/export.h"
+#include "starboard/system.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -31,6 +32,19 @@
 // UA string.
 SB_EXPORT_PLATFORM bool CbLibUserAgentSetPlatformNameSuffix(const char* suffix);
 
+// Sets the device type reported in the UA string. If |type| is not a
+// valid device type, the default value for this platform (as per starboard) is
+// kept and the function returns false.
+SB_EXPORT_PLATFORM bool CbLibUserAgentSetDeviceTypeOverride(
+    SbSystemDeviceType type);
+
+// Set the reported device brand and model names. The maximum length of
+// |value| is 512 bytes including null terminator (which is required). If
+// |value| is null, the property is reset to its default value for this
+// platform. Returns true if the value was set or reset successfully.
+SB_EXPORT_PLATFORM bool CbLibUserAgentSetBrandNameOverride(const char* value);
+SB_EXPORT_PLATFORM bool CbLibUserAgentSetModelNameOverride(const char* value);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
index 010ec6b..3c65fc7 100644
--- a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
+++ b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
@@ -43,9 +43,9 @@
     case kSbSystemDeviceTypeSetTopBox:
     case kSbSystemDeviceTypeTV:
     case kSbSystemDeviceTypeAndroidTV:
+    case kSbSystemDeviceTypeUnknown:
       return true;
     case kSbSystemDeviceTypeDesktopPC:
-    case kSbSystemDeviceTypeUnknown:
     default:
       return false;
   }
@@ -90,29 +90,8 @@
 #endif  // SB_API_VERSION >= 5
 
     // Device Type
-    switch (device_type) {
-      case kSbSystemDeviceTypeBlueRayDiskPlayer:
-        platform_info_->device_type = PlatformInfo::kBlueRayDiskPlayer;
-        break;
-      case kSbSystemDeviceTypeGameConsole:
-        platform_info_->device_type = PlatformInfo::kGameConsole;
-        break;
-      case kSbSystemDeviceTypeOverTheTopBox:
-        platform_info_->device_type = PlatformInfo::kOverTheTopBox;
-        break;
-      case kSbSystemDeviceTypeSetTopBox:
-        platform_info_->device_type = PlatformInfo::kSetTopBox;
-        break;
-      case kSbSystemDeviceTypeTV:
-        platform_info_->device_type = PlatformInfo::kTV;
-        break;
-      case kSbSystemDeviceTypeAndroidTV:
-        platform_info_->device_type = PlatformInfo::kAndroidTV;
-        break;
-      case kSbSystemDeviceTypeDesktopPC:
-      default:
-        platform_info_->device_type = PlatformInfo::kInvalidDeviceType;
-    }
+    platform_info_->device_type =
+        StarboardToPlatformInfoDeviceType(device_type);
 
     // Chipset model number
     result = SbSystemGetProperty(kSbSystemPropertyChipsetModelNumber, value,
diff --git a/src/cobalt/network/user_agent_string_factory.cc b/src/cobalt/network/user_agent_string_factory.cc
index e1c03fa..576b6e2 100644
--- a/src/cobalt/network/user_agent_string_factory.cc
+++ b/src/cobalt/network/user_agent_string_factory.cc
@@ -25,13 +25,39 @@
 
 #include "cobalt_build_id.h"  // NOLINT(build/include)
 
-#if defined(COBALT_ENABLE_LIB)
 namespace {
+
 // Max length including null terminator.
-const size_t kUserAgentPlatformMaxSuffixLength = 128;
-char g_user_agent_platform_suffix[kUserAgentPlatformMaxSuffixLength] = {0};
+const size_t kUserAgentPlatformSuffixMaxLength = 128;
+char g_user_agent_platform_suffix[kUserAgentPlatformSuffixMaxLength] = {0};
+
+bool g_device_type_override_set = false;
+SbSystemDeviceType g_device_type_override = kSbSystemDeviceTypeUnknown;
+
+// Max length including null terminator.
+const size_t kPropertyMaxLength = 512;
+bool g_brand_name_override_set = false;
+char g_brand_name_override[kPropertyMaxLength] = {0};
+bool g_model_name_override_set = false;
+char g_model_name_override[kPropertyMaxLength] = {0};
+
+bool ShouldOverrideDevice() {
+  return g_device_type_override_set || g_brand_name_override_set ||
+         g_model_name_override_set;
+}
+
+#if defined(COBALT_ENABLE_LIB)
+bool CopyStringAndTestIfSuccess(char* out_value,
+                                size_t value_length,
+                                const char* from_value) {
+  if (strlen(from_value) + 1 > value_length)
+    return false;
+  base::strlcpy(out_value, from_value, value_length);
+  return true;
+}
+#endif
+
 }  // namespace
-#endif  // defined(COBALT_ENABLE_LIB)
 
 namespace cobalt {
 namespace network {
@@ -103,9 +129,20 @@
         CreateDeviceTypeString().c_str(),
         Sanitize(platform_info_->chipset_model_number.value_or("")).c_str(),
         Sanitize(platform_info_->firmware_version.value_or("")).c_str(),
-        Sanitize(platform_info_->brand).c_str(),
-        Sanitize(platform_info_->model).c_str(),
+        Sanitize(g_brand_name_override_set ?
+            std::string(g_brand_name_override) : platform_info_->brand).c_str(),
+        Sanitize(g_model_name_override_set ?
+            std::string(g_model_name_override) : platform_info_->model).c_str(),
         CreateConnectionTypeString().c_str());
+  } else if (ShouldOverrideDevice()) {
+    // When Starboard does not report platform info but we are overriding the
+    // device.
+    base::StringAppendF(&user_agent, ", _%s_ (%s, %s)",
+        CreateDeviceTypeString().c_str(),
+        Sanitize(g_brand_name_override_set ?
+            g_brand_name_override : "").c_str(),
+        Sanitize(g_model_name_override_set ?
+            g_model_name_override : "").c_str());
   }
 
   if (!aux_field_.empty()) {
@@ -130,19 +167,22 @@
     platform += "; ";
     platform += architecture_tokens_;
   }
-
-#if defined(COBALT_ENABLE_LIB)
   if (g_user_agent_platform_suffix[0] != '\0') {
     platform += "; ";
     platform += g_user_agent_platform_suffix;
   }
-#endif  // defined(COBALT_ENABLE_LIB)
 
   return platform;
 }
 
 std::string UserAgentStringFactory::CreateDeviceTypeString() {
-  switch (platform_info_->device_type) {
+  auto reported_device_type =
+      g_device_type_override_set ?
+          StarboardToPlatformInfoDeviceType(g_device_type_override) :
+          platform_info_ ? platform_info_->device_type :
+                           PlatformInfo::kInvalidDeviceType;
+
+  switch (reported_device_type) {
     case PlatformInfo::kBlueRayDiskPlayer:
       return "BDP";
     case PlatformInfo::kGameConsole:
@@ -155,6 +195,8 @@
       return "TV";
     case PlatformInfo::kAndroidTV:
       return "ATV";
+    case PlatformInfo::kUnknown:
+      return "UNKNOWN";
     case PlatformInfo::kInvalidDeviceType:
     default:
       NOTREACHED();
@@ -177,15 +219,45 @@
   return "";
 }
 
+UserAgentStringFactory::PlatformInfo::DeviceType
+UserAgentStringFactory::StarboardToPlatformInfoDeviceType(
+    SbSystemDeviceType device_type) {
+  switch (device_type) {
+    case kSbSystemDeviceTypeBlueRayDiskPlayer:
+      return PlatformInfo::kBlueRayDiskPlayer;
+      break;
+    case kSbSystemDeviceTypeGameConsole:
+      return PlatformInfo::kGameConsole;
+      break;
+    case kSbSystemDeviceTypeOverTheTopBox:
+      return PlatformInfo::kOverTheTopBox;
+      break;
+    case kSbSystemDeviceTypeSetTopBox:
+      return PlatformInfo::kSetTopBox;
+      break;
+    case kSbSystemDeviceTypeTV:
+      return PlatformInfo::kTV;
+      break;
+    case kSbSystemDeviceTypeAndroidTV:
+      return PlatformInfo::kAndroidTV;
+      break;
+    case kSbSystemDeviceTypeUnknown:
+      return PlatformInfo::kUnknown;
+      break;
+    case kSbSystemDeviceTypeDesktopPC:
+    default:
+      return PlatformInfo::kInvalidDeviceType;
+  }
+}
+
 }  // namespace network
 }  // namespace cobalt
 
 #if defined(COBALT_ENABLE_LIB)
 // Allow host app to append a suffix to the reported platform name.
 bool CbLibUserAgentSetPlatformNameSuffix(const char* suffix) {
-  size_t suffix_length = base::strlcpy(g_user_agent_platform_suffix, suffix,
-                                       kUserAgentPlatformMaxSuffixLength);
-  if (suffix_length >= kUserAgentPlatformMaxSuffixLength) {
+  if (!CopyStringAndTestIfSuccess(g_user_agent_platform_suffix,
+                                 kPropertyMaxLength, suffix)) {
     // If the suffix is too large then nothing is appended to the platform.
     g_user_agent_platform_suffix[0] = '\0';
     return false;
@@ -193,4 +265,55 @@
 
   return true;
 }
+
+bool CbLibUserAgentSetBrandNameOverride(const char* value) {
+  if (!value) {
+    g_brand_name_override_set = false;
+    return true;
+  }
+
+  if (CopyStringAndTestIfSuccess(g_brand_name_override,
+                                 kPropertyMaxLength, value)) {
+    g_brand_name_override_set = true;
+    return true;
+  }
+
+  // Failed to reset/set value.
+  return false;
+}
+
+bool CbLibUserAgentSetModelNameOverride(const char* value) {
+  if (!value) {
+    g_model_name_override_set = false;
+    return true;
+  }
+
+  if (CopyStringAndTestIfSuccess(g_model_name_override,
+                                 kPropertyMaxLength, value)) {
+    g_model_name_override_set = true;
+    return true;
+  }
+
+  // Failed to reset/set value.
+  return false;
+}
+
+bool CbLibUserAgentSetDeviceTypeOverride(SbSystemDeviceType device_type) {
+  switch (device_type) {
+    case kSbSystemDeviceTypeBlueRayDiskPlayer:
+    case kSbSystemDeviceTypeGameConsole:
+    case kSbSystemDeviceTypeOverTheTopBox:
+    case kSbSystemDeviceTypeSetTopBox:
+    case kSbSystemDeviceTypeTV:
+    case kSbSystemDeviceTypeDesktopPC:
+    case kSbSystemDeviceTypeAndroidTV:
+    case kSbSystemDeviceTypeUnknown:
+      g_device_type_override_set = true;
+      g_device_type_override = device_type;
+      return true;
+    default:
+      g_device_type_override_set = false;
+      return false;
+  }
+}
 #endif  // defined(COBALT_ENABLE_LIB)
diff --git a/src/cobalt/network/user_agent_string_factory.h b/src/cobalt/network/user_agent_string_factory.h
index 45fbc75..f51ea1b 100644
--- a/src/cobalt/network/user_agent_string_factory.h
+++ b/src/cobalt/network/user_agent_string_factory.h
@@ -19,6 +19,7 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/optional.h"
+#include "starboard/system.h"
 
 namespace cobalt {
 namespace network {
@@ -53,6 +54,7 @@
       kOverTheTopBox,
       kSetTopBox,
       kTV,
+      kUnknown
     };
 
     enum ConnectionType {
@@ -70,6 +72,10 @@
     std::string model;
     base::optional<ConnectionType> connection_type;
   };
+
+  static PlatformInfo::DeviceType StarboardToPlatformInfoDeviceType(
+      SbSystemDeviceType device_type);
+
   base::optional<PlatformInfo> platform_info_;
 
  private:
diff --git a/src/cobalt/render_tree/map_to_mesh_filter.h b/src/cobalt/render_tree/map_to_mesh_filter.h
index a5c248f..d633a48 100644
--- a/src/cobalt/render_tree/map_to_mesh_filter.h
+++ b/src/cobalt/render_tree/map_to_mesh_filter.h
@@ -29,18 +29,31 @@
 
 enum StereoMode {
   kMono = 0,
-  // Stereoscopic modes where each half of the video represents the view of
+  // Stereoscopic modes where each half of the content represents the view of
   // one eye, and where the texture coordinates of the meshes need to be
   // scaled and offset to the appropriate half. The same mesh may be used for
   // both eyes, or there may be one for each eye.
   kLeftRight = 1,
   kTopBottom = 2,
   // Like kLeftRight, but where the texture coordinates already refer to the
-  // corresponding half of the video and need no adjustment. This can only
+  // corresponding half of the content and need no adjustment. This can only
   // happen when there are distinct meshes for each eye.
   kLeftRightUnadjustedTextureCoords = 3
 };
 
+// The shape of the mesh that this filter applies.
+enum MeshType {
+  // Rectangular content: some rasterizers render rectangular video separately
+  // from the rest of the document (for example, for stereo content). Does not
+  // specify a custom mesh, since a rectangle with the content's aspect ratio
+  // can be assumed (accounting for whether it needs to be halved in one
+  // dimension for stereo). Not enabled on all rasterizers/platforms.
+  kRectangular = 0,
+  // Mesh that is centered around the camera; examples include 360 and 180
+  // content. Needs to be specified, since the shape is arbitrary.
+  kCustomMesh = 1
+};
+
 // A MapToMeshFilter can be used to map source content onto a 3D mesh, within a
 // specified well-defined viewport.
 class MapToMeshFilter {
@@ -104,7 +117,7 @@
   };
 
   MapToMeshFilter(StereoMode stereo_mode, const Builder& builder)
-      : stereo_mode_(stereo_mode), data_(builder) {
+      : stereo_mode_(stereo_mode), mesh_type_(kCustomMesh), data_(builder) {
     DCHECK(left_eye_mesh());
     if (stereo_mode == kLeftRightUnadjustedTextureCoords) {
       // This stereo mode implies there are two meshes.
@@ -114,8 +127,9 @@
 
   // A filter without a an explicit mesh, to represent mesh-mapped content whose
   // mesh will be supplied externally.
-  explicit MapToMeshFilter(StereoMode stereo_mode)
-      : stereo_mode_(stereo_mode), data_() {
+  MapToMeshFilter(StereoMode stereo_mode, MeshType mesh_type)
+      : stereo_mode_(stereo_mode), mesh_type_(mesh_type), data_() {
+    DCHECK(mesh_type != kCustomMesh);
     DCHECK(stereo_mode != kLeftRightUnadjustedTextureCoords);
   }
 
@@ -125,6 +139,8 @@
 
   StereoMode stereo_mode() const { return stereo_mode_; }
 
+  MeshType mesh_type() const { return mesh_type_; }
+
   // The omission of the |resolution| parameter will yield the default
   // meshes in each of the following functions (by failing to match the
   // invalid resolution).
@@ -146,6 +162,7 @@
  private:
   static math::Size InvalidSize() { return math::Size(-1, -1); }
   StereoMode stereo_mode_;
+  MeshType mesh_type_;
   Builder data_;
 };
 
diff --git a/src/cobalt/renderer/copy_font_data.gypi b/src/cobalt/renderer/copy_font_data.gypi
index ae1cc92..4617aea 100644
--- a/src/cobalt/renderer/copy_font_data.gypi
+++ b/src/cobalt/renderer/copy_font_data.gypi
@@ -32,15 +32,14 @@
         'package_fallback_lang_non_cjk': 2,
         'package_fallback_lang_cjk': 1,
         'package_fallback_lang_cjk_low_quality': 0,
-        'package_fallback_lang_jp': 0,
         'package_fallback_historic': 1,
         'package_fallback_color_emoji': 1,
         'package_fallback_emoji': 0,
         'package_fallback_symbols': 1,
       }],
 
-      # '10megabytes' is deprecated but is mapped to 'limited_with_jp'
-      [ 'cobalt_font_package == "limited_with_jp" or cobalt_font_package == "10megabytes"', {
+      # '10megabytes' and 'limited_with_jp' are deprecated but map to 'limited'
+      [ 'cobalt_font_package == "limited" or cobalt_font_package == "limited_with_jp" or cobalt_font_package == "10megabytes"', {
         'source_font_config_dir': '<(static_contents_source_dir)/fonts/config/common',
 
         'package_named_sans_serif': 2,
@@ -49,23 +48,6 @@
         'package_fallback_lang_non_cjk': 1,
         'package_fallback_lang_cjk': 0,
         'package_fallback_lang_cjk_low_quality': 1,
-        'package_fallback_lang_jp': 1,
-        'package_fallback_historic': 0,
-        'package_fallback_color_emoji': 0,
-        'package_fallback_emoji': 1,
-        'package_fallback_symbols': 1,
-      }],
-
-      [ 'cobalt_font_package == "limited"', {
-        'source_font_config_dir': '<(static_contents_source_dir)/fonts/config/common',
-
-        'package_named_sans_serif': 2,
-        'package_named_serif': 0,
-        'package_named_fcc_fonts': 0,
-        'package_fallback_lang_non_cjk': 1,
-        'package_fallback_lang_cjk': 0,
-        'package_fallback_lang_cjk_low_quality': 1,
-        'package_fallback_lang_jp': 0,
         'package_fallback_historic': 0,
         'package_fallback_color_emoji': 0,
         'package_fallback_emoji': 1,
@@ -81,7 +63,6 @@
         'package_fallback_lang_non_cjk': 0,
         'package_fallback_lang_cjk': 0,
         'package_fallback_lang_cjk_low_quality': 0,
-        'package_fallback_lang_jp': 0,
         'package_fallback_historic': 0,
         'package_fallback_color_emoji': 0,
         'package_fallback_emoji': 0,
@@ -100,7 +81,6 @@
         'package_fallback_lang_non_cjk': 0,
         'package_fallback_lang_cjk': 0,
         'package_fallback_lang_cjk_low_quality': 0,
-        'package_fallback_lang_jp': 0,
         'package_fallback_historic': 0,
         'package_fallback_color_emoji': 0,
         'package_fallback_emoji': 0,
@@ -125,9 +105,6 @@
       [ 'cobalt_font_package_override_fallback_lang_cjk_low_quality >= 0', {
         'package_fallback_lang_cjk_low_quality': '<(cobalt_font_package_override_fallback_lang_cjk_low_quality)',
       }],
-      [ 'cobalt_font_package_override_fallback_lang_jp >= 0', {
-        'package_fallback_lang_jp': '<(cobalt_font_package_override_fallback_lang_jp)',
-      }],
       [ 'cobalt_font_package_override_fallback_historic >= 0', {
         'package_fallback_historic': '<(cobalt_font_package_override_fallback_historic)',
       }],
@@ -318,11 +295,6 @@
             '<(source_font_files_dir)/DroidSansFallback.ttf',
           ],
         }],
-        [ 'package_fallback_lang_jp >= 1', {
-          'files+': [
-            '<(source_font_files_dir)/NotoSansJP-Regular.otf',
-          ],
-        }],
         [ 'package_fallback_historic >= 1', {
           'files+': [
             '<(source_font_files_dir)/NotoSansAvestan-Regular.ttf',
diff --git a/src/cobalt/renderer/fps_overlay.cc b/src/cobalt/renderer/fps_overlay.cc
index 50f29b2..3bd4510 100644
--- a/src/cobalt/renderer/fps_overlay.cc
+++ b/src/cobalt/renderer/fps_overlay.cc
@@ -49,7 +49,7 @@
   // together.
   float y_offset = kTextMarginInPixels;
   render_tree::CompositionNode::Builder text_builder;
-  for (auto line : lines) {
+  for (const auto& line : lines) {
     scoped_refptr<render_tree::GlyphBuffer> glyph_buffer =
         resource_provider->CreateGlyphBuffer(line, font);
     math::RectF bounds(glyph_buffer->GetBounds());
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.cc b/src/cobalt/renderer/rasterizer/blitter/image.cc
index c33b7b8..e57d83d 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/image.cc
@@ -123,7 +123,12 @@
         size_.width(), size_.height(), kN32_SkColorType, kPremul_SkAlphaType);
 
     SkBitmap bitmap;
-    bitmap.allocPixels(image_info);
+    bool allocation_successful = bitmap.tryAllocPixels(image_info);
+    if (!allocation_successful) {
+      LOG(WARNING) << "Unable to allocate pixels of size " << size_.width()
+                   << "x" << size_.height();
+      return image_;
+    }
     bool result = SbBlitterDownloadSurfacePixels(
         surface_, SkiaToBlitterPixelFormat(image_info.colorType()),
         bitmap.rowBytes(), bitmap.getPixels());
diff --git a/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc b/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
index 90d8d44..bf4a197 100644
--- a/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
+++ b/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
@@ -50,6 +50,10 @@
 }
 
 math::Rect RectAllocator::Allocate(const math::Size& alloc_size) {
+  if ((alloc_size.width() < 0) || (alloc_size.height() < 0)) {
+    // Invalid allocation was requested.
+    return math::Rect(0, 0, 0, 0);
+  }
   math::Rect allocation(0, 0, alloc_size.width(), alloc_size.height());
 
   // Find the first block that is too small for the requested allocation.
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
index 3042396..b8df300 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -321,11 +321,14 @@
         map_to_mesh_search.found_node->data().map_to_mesh_filter;
 
     CbLibVideoProjectionType new_projection_type;
-    if (!filter->left_eye_mesh()) {
-      // Video is rectangular. Mesh is provided externally (by host).
-      new_projection_type = kCbLibVideoProjectionTypeRectangular;
-    } else {
-      new_projection_type = kCbLibVideoProjectionTypeMesh;
+    switch (filter->mesh_type()) {
+      case render_tree::kRectangular:
+        // Video is rectangular. Mesh is provided externally (by host).
+        new_projection_type = kCbLibVideoProjectionTypeRectangular;
+        break;
+      case render_tree::kCustomMesh:
+        new_projection_type = kCbLibVideoProjectionTypeMesh;
+        break;
     }
 
     if (video_projection_type_ != new_projection_type ||
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index b725a77..a8cf645 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -473,6 +473,11 @@
     const render_tree::ImageNode* image_node,
     const render_tree::MapToMeshFilter& mesh_filter,
     RenderTreeNodeVisitorDrawState* draw_state) {
+  if (mesh_filter.mesh_type() == render_tree::kRectangular) {
+    NOTREACHED() << "This rasterizer does not support rectangular meshes on "
+                    "the map-to-mesh filter.";
+    return;
+  }
   Image* image =
       base::polymorphic_downcast<Image*>(image_node->data().source.get());
   if (!image) {
@@ -552,14 +557,15 @@
       GrContext::Create(kOpenGL_GrBackend, NULL, context_options));
 
   DCHECK(gr_context_);
-  // The GrContext manages a resource cache internally using GrResourceCache
-  // which by default caches 96MB of resources.  This is used for helping with
-  // rendering shadow effects, gradient effects, and software rendered paths.
-  // As we have our own cache for most resources, set it to a much smaller value
-  // so Skia doesn't use too much GPU memory.
+  // The GrContext manages a budget for GPU resources.  Setting the budget equal
+  // to |skia_cache_size_in_bytes| + glyph cache's size will let Skia use
+  // additional |skia_cache_size_in_bytes| for GPU resources like textures,
+  // vertex buffers, etc.
   const int kSkiaCacheMaxResources = 128;
-  gr_context_->setResourceCacheLimits(kSkiaCacheMaxResources,
-                                      skia_cache_size_in_bytes);
+  gr_context_->setResourceCacheLimits(
+      kSkiaCacheMaxResources,
+      skia_cache_size_in_bytes +
+          context_options.fGlyphCacheTextureMaximumBytes);
 
   base::Callback<sk_sp<SkSurface>(const math::Size&)>
       create_sk_surface_function = base::Bind(
@@ -705,7 +711,9 @@
                "width", size.width(), "height", size.height());
   SkImageInfo image_info =
       SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType);
-  return SkSurface::MakeRenderTarget(gr_context_.get(), SkBudgeted::kYes,
+  // Do not count the resources for this surface towards the budget since
+  // the budget is currently only meant for Skia managed resources.
+  return SkSurface::MakeRenderTarget(gr_context_.get(), SkBudgeted::kNo,
                                      image_info);
 }
 
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
index 4c843ac..d19e7b8 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -388,6 +388,7 @@
     if (!source) {
       return;
     }
+
     // WIP TODO: pass in the mesh in the filter here.
     if (TryRenderMapToRect(source, *filter_node->data().map_to_mesh_filter,
                            render_image_with_mesh_function_, &draw_state_)) {
@@ -1311,7 +1312,13 @@
   SkImageInfo image_info =
       SkImageInfo::MakeN32(rect.width(), rect.height(), kPremul_SkAlphaType);
   SkBitmap bitmap;
-  bitmap.allocPixels(image_info);
+
+  bool allocation_successful = bitmap.tryAllocPixels(image_info);
+  if (!allocation_successful) {
+    LOG(WARNING) << "Unable to allocate pixels of size " << rect.width() << "x"
+                 << rect.height();
+    return;
+  }
 
   SkCanvas canvas(bitmap);
   canvas.clear(SkColorSetARGB(0, 0, 0, 0));
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.cc b/src/cobalt/renderer/render_tree_pixel_tester.cc
index ff33071..cfe8807 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.cc
+++ b/src/cobalt/renderer/render_tree_pixel_tester.cc
@@ -108,15 +108,19 @@
     // We need to convert our image to premultiplied alpha and N32 color
     // before proceeding to blur them, as Skia is designed to primarily deal
     // only with images in this format.
-    SkImageInfo premul_alpha_image_info = SkImageInfo::Make(
-        bitmap.width(), bitmap.height(), kN32_SkColorType, kPremul_SkAlphaType);
-    premul_alpha_bitmap.allocPixels(premul_alpha_image_info);
+    SkImageInfo premul_alpha_image_info =
+        SkImageInfo::MakeN32Premul(bitmap.width(), bitmap.height());
+    bool allocation_successful =
+        premul_alpha_bitmap.tryAllocPixels(premul_alpha_image_info);
+    // Since this is a test, just crash.
+    DCHECK(allocation_successful);
     bitmap.readPixels(premul_alpha_image_info, premul_alpha_bitmap.getPixels(),
                       premul_alpha_bitmap.rowBytes(), 0, 0);
   }
   SkBitmap blurred_bitmap;
-  blurred_bitmap.allocPixels(SkImageInfo::Make(
-      bitmap.width(), bitmap.height(), kN32_SkColorType, kPremul_SkAlphaType));
+  bool blurred_bitmap_allocated =
+      blurred_bitmap.tryAllocN32Pixels(bitmap.width(), bitmap.height());
+  DCHECK(blurred_bitmap_allocated);
 
   SkPaint paint;
   sk_sp<SkImageFilter> blur_filter(
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp
index 0b9e06b..8eddeb6 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45.gyp
+++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -81,7 +81,6 @@
           # SpiderMonkey bindings implements indexed deleters.
           'ENGINE_SUPPORTS_INDEXED_DELETERS',
           'ENGINE_SUPPORTS_INT64',
-          'ENGINE_SUPPORTS_STACK_TRACE_COLUMNS',
           # TODO: Remove this when exact rooting and generational GC is enabled.
           'ENGINE_USES_CONSERVATIVE_ROOTING',
         ],
diff --git a/src/cobalt/script/mozjs-45/mozjs_callback_function.h b/src/cobalt/script/mozjs-45/mozjs_callback_function.h
index 6b05688..1a60269 100644
--- a/src/cobalt/script/mozjs-45/mozjs_callback_function.h
+++ b/src/cobalt/script/mozjs-45/mozjs_callback_function.h
@@ -95,6 +95,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
@@ -155,6 +156,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
@@ -217,6 +219,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
@@ -281,6 +284,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
@@ -347,6 +351,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
@@ -416,6 +421,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
@@ -487,6 +493,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
@@ -560,6 +567,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump b/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump
index c4d7009..f0d1b24 100644
--- a/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump
+++ b/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump
@@ -123,6 +123,7 @@
   JSObject* handle() const { return weak_function_.GetObject(); }
   const JS::Value& value() const { return weak_function_.GetValue(); }
   bool WasCollected() const { return weak_function_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { weak_function_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index 924c5e3..d4c64c3 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -91,9 +91,6 @@
 
   ScriptValueFactory* script_value_factory() override;
 
-  // Evaluates any automatically included Javascript for the environment.
-  void EvaluateAutomatics();
-
   JSContext* context() const { return context_; }
 
   JSObject* global_object_proxy() const { return global_object_proxy_; }
@@ -159,6 +156,9 @@
     int count;
   };
 
+  // Evaluates any automatically included Javascript for the environment.
+  void EvaluateAutomatics();
+
   bool EvaluateScriptInternal(const scoped_refptr<SourceCode>& source_code,
                               bool mute_errors,
                               JS::MutableHandleValue out_result);
diff --git a/src/cobalt/script/mozjs-45/mozjs_tracer.cc b/src/cobalt/script/mozjs-45/mozjs_tracer.cc
index 0a07f3a..99f4f5f 100644
--- a/src/cobalt/script/mozjs-45/mozjs_tracer.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_tracer.cc
@@ -32,7 +32,7 @@
   // rather than a |JSContext|. Fortunately, Cobalt will only create one
   // global environment per runtime, so we can still safely get back to our
   // context, and thus our global environment.
-  JSContext* context = NULL;
+  JSContext* context = nullptr;
   JS_ContextIterator(js_tracer_->runtime(), &context);
   DCHECK(context);
   MozjsGlobalEnvironment* global_environment =
@@ -74,10 +74,10 @@
     DCHECK(wrapper_private->context_ == context);
     DCHECK(wrapper_private->wrapper_proxy_);
     JS_CallObjectTracer(js_tracer_, &wrapper_private->wrapper_proxy_,
-                        "WrapperPrivate::TraceWrappable");
+                        "MozjsTracer::Trace");
   }
 
-  DCHECK(JS_ContextIterator(js_tracer_->runtime(), &context) == NULL);
+  DCHECK(JS_ContextIterator(js_tracer_->runtime(), &context) == nullptr);
 }
 
 void MozjsTracer::TraceFrom(Wrappable* wrappable) {
diff --git a/src/cobalt/script/mozjs-45/mozjs_tracer.h b/src/cobalt/script/mozjs-45/mozjs_tracer.h
index a426ae7..22180b1 100644
--- a/src/cobalt/script/mozjs-45/mozjs_tracer.h
+++ b/src/cobalt/script/mozjs-45/mozjs_tracer.h
@@ -36,12 +36,21 @@
 class MozjsTracer : public ::cobalt::script::Tracer {
  public:
   explicit MozjsTracer(JSTracer* js_tracer) : js_tracer_(js_tracer) {}
+
   void Trace(Traceable* traceable) override;
 
+  // Traverse the embedder heap and JavaScript heap, starting from
+  // |wrappable|.  For any reachable |Traceable|s that have corresponding
+  // wrappers, feed those wrappers to the SpiderMonkey-side tracer.  In the
+  // case that they don't, add them to |frontier_|, and trace them ourselves.
   void TraceFrom(Wrappable* wrappable);
 
+  // This is primarly exposed for |MozjsUserObjectHolder|'s implementation of
+  // |TraceMembers|.
+  JSTracer* js_tracer() const { return js_tracer_; }
+
  private:
-  JSTracer* js_tracer_;
+  JSTracer* const js_tracer_;
   // Pending |Traceable|s that we must traverse ourselves, since they did not
   // have a |WrapperPrivate|, or were not a wrapper.
   std::vector<Traceable*> frontier_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
index 8db589d..a33ff7a 100644
--- a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
+++ b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
@@ -57,6 +57,31 @@
     DCHECK(!persistent_root_);
   }
 
+  bool EqualTo(const BaseClass& other) const override {
+    const MozjsUserObjectHolder* mozjs_other =
+        base::polymorphic_downcast<const MozjsUserObjectHolder*>(&other);
+    if (!handle_) {
+      return !mozjs_other->handle_;
+    } else if (!mozjs_other->handle_) {
+      return false;
+    }
+
+    DCHECK(handle_);
+    DCHECK(mozjs_other->handle_);
+
+    JS::RootedValue value1(context_, js_value());
+    JS::RootedValue value2(context_, mozjs_other->js_value());
+    return util::IsSameGcThing(context_, value1, value2);
+  }
+
+  void TraceMembers(Tracer* tracer) override {
+    if (handle_) {
+      MozjsTracer* mozjs_tracer =
+          base::polymorphic_downcast<MozjsTracer*>(tracer);
+      handle_->Trace(mozjs_tracer->js_tracer());
+    }
+  }
+
   void RegisterOwner(Wrappable* owner) override {
     JSAutoRequest auto_request(context_);
     JS::RootedValue owned_value(context_, js_value());
@@ -66,9 +91,8 @@
     if (!owned_value.isNullOrUndefined() && owned_value.isGCThing()) {
       MozjsGlobalEnvironment* global_environment =
           MozjsGlobalEnvironment::GetFromContext(context_);
-      intptr_t key = ReferencedObjectMap::GetKeyForWrappable(owner);
       global_environment->referenced_objects()->AddReferencedObject(
-          key, owned_value);
+          owner, owned_value);
     }
   }
 
@@ -80,9 +104,8 @@
     if (!owned_value.isNullOrUndefined() && owned_value.isGCThing()) {
       MozjsGlobalEnvironment* global_environment =
           MozjsGlobalEnvironment::GetFromContext(context_);
-      intptr_t key = ReferencedObjectMap::GetKeyForWrappable(owner);
       global_environment->referenced_objects()->RemoveReferencedObject(
-          key, owned_value);
+          owner, owned_value);
     }
   }
 
@@ -114,23 +137,6 @@
         new MozjsUserObjectHolder(context_, rooted_value));
   }
 
-  bool EqualTo(const BaseClass& other) const override {
-    const MozjsUserObjectHolder* mozjs_other =
-        base::polymorphic_downcast<const MozjsUserObjectHolder*>(&other);
-    if (!handle_) {
-      return !mozjs_other->handle_;
-    } else if (!mozjs_other->handle_) {
-      return false;
-    }
-
-    DCHECK(handle_);
-    DCHECK(mozjs_other->handle_);
-
-    JS::RootedValue value1(context_, js_value());
-    JS::RootedValue value2(context_, mozjs_other->js_value());
-    return util::IsSameGcThing(context_, value1, value2);
-  }
-
   const JS::Value& js_value() const {
     DCHECK(handle_);
     return handle_->value();
@@ -142,9 +148,6 @@
   }
 
  private:
-  typedef base::hash_map<const Wrappable*, base::WeakPtr<WrapperPrivate> >
-      WrappableAndPrivateHashMap;
-
   JSContext* context_;
   base::optional<MozjsUserObjectType> handle_;
   int prevent_garbage_collection_count_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_value_handle.h b/src/cobalt/script/mozjs-45/mozjs_value_handle.h
index 0a30572..3b35952 100644
--- a/src/cobalt/script/mozjs-45/mozjs_value_handle.h
+++ b/src/cobalt/script/mozjs-45/mozjs_value_handle.h
@@ -34,9 +34,11 @@
 class MozjsValueHandle : public ValueHandle {
  public:
   typedef ValueHandle BaseType;
+
   JSObject* handle() const { return handle_.GetObject(); }
   const JS::Value& value() const { return handle_.GetValue(); }
   bool WasCollected() const { return handle_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { handle_.Trace(js_tracer); }
 
  private:
   MozjsValueHandle(JSContext* context, JS::HandleValue value)
diff --git a/src/cobalt/script/mozjs-45/native_promise.h b/src/cobalt/script/mozjs-45/native_promise.h
index 803e412..f5f09d6 100644
--- a/src/cobalt/script/mozjs-45/native_promise.h
+++ b/src/cobalt/script/mozjs-45/native_promise.h
@@ -69,6 +69,7 @@
   JSObject* handle() const { return promise_resolver_->get().GetObject(); }
   const JS::Value& value() const { return promise_resolver_->get().GetValue(); }
   bool WasCollected() const { return promise_resolver_->get().WasCollected(); }
+  void Trace(JSTracer* js_tracer) { promise_resolver_->get().Trace(js_tracer); }
 
   // The Promise JS object (not the resolver).
   JSObject* promise() const { return promise_resolver_->GetPromise(); }
diff --git a/src/cobalt/script/mozjs-45/promise_wrapper.h b/src/cobalt/script/mozjs-45/promise_wrapper.h
index 1f5420d..cd34140 100644
--- a/src/cobalt/script/mozjs-45/promise_wrapper.h
+++ b/src/cobalt/script/mozjs-45/promise_wrapper.h
@@ -34,6 +34,7 @@
 
   PromiseWrapper(JSContext* context, JS::HandleValue promise_wrapper);
 
+  WeakHeapObject& get() { return weak_promise_wrapper_; }
   const WeakHeapObject& get() const { return weak_promise_wrapper_; }
   JSObject* GetPromise() const;
   void Resolve(JS::HandleValue value) const;
diff --git a/src/cobalt/script/mozjs-45/referenced_object_map.cc b/src/cobalt/script/mozjs-45/referenced_object_map.cc
index 4bac38f..38312fe 100644
--- a/src/cobalt/script/mozjs-45/referenced_object_map.cc
+++ b/src/cobalt/script/mozjs-45/referenced_object_map.cc
@@ -28,24 +28,21 @@
     : context_(context) {}
 
 // Add/Remove a reference from a WrapperPrivate to a JSValue.
-void ReferencedObjectMap::AddReferencedObject(intptr_t key,
+void ReferencedObjectMap::AddReferencedObject(const Wrappable* wrappable,
                                               JS::HandleValue referee) {
   TRACK_MEMORY_SCOPE("Javascript");
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!referee.isNullOrUndefined());
   DCHECK(referee.isGCThing());
   referenced_objects_.insert(
-      std::make_pair(key, WeakHeapObject(context_, referee)));
+      std::make_pair(wrappable, WeakHeapObject(context_, referee)));
 }
 
-void ReferencedObjectMap::RemoveReferencedObject(intptr_t key,
+void ReferencedObjectMap::RemoveReferencedObject(const Wrappable* wrappable,
                                                  JS::HandleValue referee) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  std::pair<ReferencedObjectMultiMap::iterator,
-            ReferencedObjectMultiMap::iterator> pair_range =
-      referenced_objects_.equal_range(key);
-  for (ReferencedObjectMultiMap::iterator it = pair_range.first;
-       it != pair_range.second; ++it) {
+  auto pair_range = referenced_objects_.equal_range(wrappable);
+  for (auto it = pair_range.first; it != pair_range.second; ++it) {
     JS::RootedValue element(context_, it->second.GetValue());
     if (util::IsSameGcThing(context_, referee, element)) {
       // There may be multiple mappings between a specific owner and a JS
@@ -58,24 +55,20 @@
 }
 
 void ReferencedObjectMap::TraceReferencedObjects(JSTracer* trace,
-                                                 intptr_t key) {
+                                                 const Wrappable* wrappable) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  std::pair<ReferencedObjectMultiMap::iterator,
-            ReferencedObjectMultiMap::iterator> pair_range =
-      referenced_objects_.equal_range(key);
-  for (ReferencedObjectMultiMap::iterator it = pair_range.first;
-       it != pair_range.second; ++it) {
+  auto pair_range = referenced_objects_.equal_range(wrappable);
+  for (auto it = pair_range.first; it != pair_range.second; ++it) {
     it->second.Trace(trace);
   }
 }
 
 void ReferencedObjectMap::RemoveNullReferences() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  for (ReferencedObjectMultiMap::iterator it = referenced_objects_.begin();
-       it != referenced_objects_.end();
+  for (auto it = referenced_objects_.begin(); it != referenced_objects_.end();
        /*Incremented in the loop */) {
     if (it->second.WasCollected()) {
-      ReferencedObjectMultiMap::iterator erase_iterator = it++;
+      auto erase_iterator = it++;
       referenced_objects_.erase(erase_iterator);
     } else {
       DCHECK(it->second.IsGcThing());
diff --git a/src/cobalt/script/mozjs-45/referenced_object_map.h b/src/cobalt/script/mozjs-45/referenced_object_map.h
index 134a221..7ae3281 100644
--- a/src/cobalt/script/mozjs-45/referenced_object_map.h
+++ b/src/cobalt/script/mozjs-45/referenced_object_map.h
@@ -25,28 +25,20 @@
 namespace script {
 namespace mozjs {
 
-// Maintains a set of JSObjects that are reachable from a given object. This
-// relationship is registered by mapping a key to a JSObject. The calling code
-// should ensure that a key uniquely identifies the entity that holds a
-// reference to the JSObjects.
-// During garbage collection, supply a key to TraceReferencedObjects to trace
-// all objects that are registered as being reachable from the object
-// represented by that key.
+// Maintains a set of |JSObject|s that are reachable from a given object. This
+// relationship is registered by mapping a |Wrappable| to a |JSObject|. During
+// garbage collection, supply a |Wrappable| to |TraceReferencedObjects| to
+// trace all objects that are registered as being reachable from the object.
 class ReferencedObjectMap {
  public:
   explicit ReferencedObjectMap(JSContext* context);
 
-  // Reinterpret the pointer as an integer to be used as a key for tracking
-  // referenced objects.
-  static intptr_t GetKeyForWrappable(const Wrappable* wrappable) {
-    return reinterpret_cast<intptr_t>(wrappable);
-  }
+  void AddReferencedObject(const Wrappable* wrappable, JS::HandleValue referee);
+  void RemoveReferencedObject(const Wrappable* wrappable,
+                              JS::HandleValue referee);
 
-  void AddReferencedObject(intptr_t key, JS::HandleValue referee);
-  void RemoveReferencedObject(intptr_t key, JS::HandleValue referee);
-
-  // Trace all objects referenced from this WrapperPrivate*.
-  void TraceReferencedObjects(JSTracer* trace, intptr_t key);
+  // Trace all objects referenced from this |wrappable|.
+  void TraceReferencedObjects(JSTracer* trace, const Wrappable* wrappable);
 
   // Remove any referenced objects that are NULL. It may be the case that a
   // weak reference to an object was garbage collected, so remove it from the
@@ -54,11 +46,11 @@
   void RemoveNullReferences();
 
  private:
-  typedef base::hash_multimap<intptr_t, WeakHeapObject>
+  typedef base::hash_multimap<const Wrappable*, WeakHeapObject>
       ReferencedObjectMultiMap;
 
   base::ThreadChecker thread_checker_;
-  JSContext* context_;
+  JSContext* const context_;
   ReferencedObjectMultiMap referenced_objects_;
 };
 
diff --git a/src/cobalt/script/mozjs-45/union_type_conversion_impl.h.pump b/src/cobalt/script/mozjs-45/union_type_conversion_impl.h.pump
index 05f2238..6b5dab9 100644
--- a/src/cobalt/script/mozjs-45/union_type_conversion_impl.h.pump
+++ b/src/cobalt/script/mozjs-45/union_type_conversion_impl.h.pump
@@ -27,8 +27,8 @@
 #include "cobalt/base/type_id.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_value_handle.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/type_traits.h"
 #include "cobalt/script/union_type.h"
 
diff --git a/src/cobalt/script/mozjs-45/util/exception_helpers.cc b/src/cobalt/script/mozjs-45/util/exception_helpers.cc
index a9523f5..d650dd6 100644
--- a/src/cobalt/script/mozjs-45/util/exception_helpers.cc
+++ b/src/cobalt/script/mozjs-45/util/exception_helpers.cc
@@ -79,7 +79,7 @@
 
     sf.line_number = static_cast<int>(line);
     sf.column_number = static_cast<int>(column);
-    sf.function_name = "global code";
+    sf.function_name = "";
     if (name) {
       JS::RootedValue rooted_value(context);
       rooted_value.setString(name);
@@ -125,10 +125,10 @@
         auto* chars = atom->latin1Chars(nogc);
         sf.function_name = std::string(chars, chars + atom->length());
       } else {
-        sf.function_name = "(anonymous function)";
+        sf.function_name = "<anonymous>";
       }
     } else {
-      sf.function_name = "global code";
+      sf.function_name = "";
     }
 
     output->push_back(sf);
diff --git a/src/cobalt/script/mozjs-45/weak_heap_object.cc b/src/cobalt/script/mozjs-45/weak_heap_object.cc
index d6d8ddb..3d26ad6 100644
--- a/src/cobalt/script/mozjs-45/weak_heap_object.cc
+++ b/src/cobalt/script/mozjs-45/weak_heap_object.cc
@@ -40,9 +40,16 @@
   return *this;
 }
 
-void WeakHeapObject::Trace(JSTracer* trace) {
+WeakHeapObject::~WeakHeapObject() {
+  // It's safe to call StopTracking even if StartTracking wasn't called. the
+  // WeakObjectManager handles the case where it's not currently tracking the
+  // WeakHeapObject.
+  weak_object_manager_->StopTracking(this);
+}
+
+void WeakHeapObject::Trace(JSTracer* js_tracer) {
   if (!value_.isNullOrUndefined()) {
-    JS_CallValueTracer(trace, &value_, "WeakHeapObject::Trace");
+    JS_CallValueTracer(js_tracer, &value_, "WeakHeapObject::Trace");
   }
 }
 
@@ -56,13 +63,6 @@
   return (was_collected_ && value_.isNullOrUndefined());
 }
 
-WeakHeapObject::~WeakHeapObject() {
-  // It's safe to call StopTracking even if StartTracking wasn't called. the
-  // WeakObjectManager handles the case where it's not currently tracking the
-  // WeakHeapObject.
-  weak_object_manager_->StopTracking(this);
-}
-
 void WeakHeapObject::Initialize(WeakHeapObjectManager* weak_heap_object_manager,
                                 const JS::Value& value) {
   weak_object_manager_ = weak_heap_object_manager;
diff --git a/src/cobalt/script/mozjs-45/weak_heap_object.h b/src/cobalt/script/mozjs-45/weak_heap_object.h
index 1240e98..ea252c1 100644
--- a/src/cobalt/script/mozjs-45/weak_heap_object.h
+++ b/src/cobalt/script/mozjs-45/weak_heap_object.h
@@ -29,22 +29,22 @@
  public:
   WeakHeapObject(JSContext* context, JS::HandleValue value);
   WeakHeapObject(const WeakHeapObject& other);
-
   WeakHeapObject& operator=(const WeakHeapObject& rhs);
+
+  ~WeakHeapObject();
+
   const JS::Value& GetValue() const { return value_.get(); }
   JSObject* GetObject() const {
     return value_.toObjectOrNull();
   }
 
-  void Trace(JSTracer* trace);
+  void Trace(JSTracer* js_tracer);
   bool IsObject() const;
   bool IsGcThing() const;
 
   // Whether the value was a GC Thing and has been actually GC'd.
   bool WasCollected() const;
 
-  ~WeakHeapObject();
-
  private:
   void Initialize(WeakHeapObjectManager* weak_heap_object_manager,
                   const JS::Value& value);
diff --git a/src/cobalt/script/mozjs-45/wrapper_private.cc b/src/cobalt/script/mozjs-45/wrapper_private.cc
index 24b2e2a..e7aa1a6 100644
--- a/src/cobalt/script/mozjs-45/wrapper_private.cc
+++ b/src/cobalt/script/mozjs-45/wrapper_private.cc
@@ -125,10 +125,8 @@
 
     MozjsGlobalEnvironment* global_environment =
         MozjsGlobalEnvironment::GetFromContext(wrapper_private->context_);
-    intptr_t key = ReferencedObjectMap::GetKeyForWrappable(
-        wrapper_private->wrappable_.get());
-    global_environment->referenced_objects()->TraceReferencedObjects(trace,
-                                                                     key);
+    global_environment->referenced_objects()->TraceReferencedObjects(
+        trace, wrapper_private->wrappable_);
     MozjsTracer mozjs_tracer(trace);
     mozjs_tracer.TraceFrom(wrapper_private->wrappable_);
   }
diff --git a/src/cobalt/script/script_value.h b/src/cobalt/script/script_value.h
index fcca2c4..09bd016 100644
--- a/src/cobalt/script/script_value.h
+++ b/src/cobalt/script/script_value.h
@@ -18,6 +18,7 @@
 #include "base/basictypes.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "cobalt/script/tracer.h"
 
 namespace cobalt {
 namespace script {
@@ -41,8 +42,44 @@
 // detached from the rest of the graph of JavaScript GC things, and can safely
 // be garbage collected.
 template <class T>
-class ScriptValue {
+class ScriptValue : public Traceable {
  public:
+  // A reference type for when a |Traceable| owns a |ScriptValue|, and only
+  // wants to keep it alive if the owning object continues to get traced. This
+  // is the preferred reference type in that if you can use it, you should (as
+  // opposed to legacy types |Reference| or |StrongReference|, which predate
+  // the TraceMembers system).
+  class TracedReference : public Traceable {
+   public:
+    explicit TracedReference(scoped_ptr<ScriptValue> script_value)
+        : referenced_value_(script_value.Pass()) {
+      DCHECK(referenced_value_);
+    }
+
+    explicit TracedReference(const ScriptValue& script_value)
+        : referenced_value_(script_value.MakeCopy()) {
+      DCHECK(referenced_value_);
+    }
+
+    const T& value() const { return *(referenced_value_->GetScriptValue()); }
+
+    // Return the referenced ScriptValue. This ScriptValue can
+    // be passed back into the JavaScript bindings layer where the referenced
+    // JavaScript object can be extracted from the ScriptValue.
+    const ScriptValue<T>& referenced_value() const {
+      return *(referenced_value_.get());
+    }
+
+    void TraceMembers(Tracer* tracer) override {
+      tracer->Trace(referenced_value_.get());
+    }
+
+   private:
+    scoped_ptr<ScriptValue> referenced_value_;
+
+    DISALLOW_COPY_AND_ASSIGN(TracedReference);
+  };
+
   // The Reference class maintains the ownership relationship between a
   // Wrappable and the JavaScript value wrapped by a ScriptValue. This is an
   // RAII object in that creation of a Reference instance will mark the
diff --git a/src/cobalt/script/testing/fake_script_value.h b/src/cobalt/script/testing/fake_script_value.h
index 1982740..9ecf42e 100644
--- a/src/cobalt/script/testing/fake_script_value.h
+++ b/src/cobalt/script/testing/fake_script_value.h
@@ -26,24 +26,27 @@
 class FakeScriptValue : public cobalt::script::ScriptValue<T> {
  public:
   typedef cobalt::script::ScriptValue<T> BaseClass;
+
   explicit FakeScriptValue(const T* listener)
       : value_(listener) {}
 
-  void RegisterOwner(script::Wrappable*) override {}
-  void DeregisterOwner(script::Wrappable*) override {}
-  void PreventGarbageCollection() override {}
-  void AllowGarbageCollection() override {}
-  const T* GetScriptValue(void) const override { return value_; }
-  scoped_ptr<BaseClass> MakeCopy() const override {
-    return make_scoped_ptr<BaseClass>(new FakeScriptValue(value_));
-  }
-
   bool EqualTo(const BaseClass& other) const override {
     const FakeScriptValue* other_script_object =
         base::polymorphic_downcast<const FakeScriptValue*>(&other);
     return value_ == other_script_object->value_;
   }
 
+  void TraceMembers(Tracer*) override {};
+
+  void RegisterOwner(script::Wrappable*) override {}
+  void DeregisterOwner(script::Wrappable*) override {}
+  void PreventGarbageCollection() override {}
+  void AllowGarbageCollection() override {}
+  const T* GetScriptValue() const override { return value_; }
+  scoped_ptr<BaseClass> MakeCopy() const override {
+    return make_scoped_ptr<BaseClass>(new FakeScriptValue(value_));
+  }
+
  private:
   const T* value_;
 };
diff --git a/src/cobalt/script/tracer.h b/src/cobalt/script/tracer.h
index c7ffdbe..3e7f771 100644
--- a/src/cobalt/script/tracer.h
+++ b/src/cobalt/script/tracer.h
@@ -15,7 +15,10 @@
 #ifndef COBALT_SCRIPT_TRACER_H_
 #define COBALT_SCRIPT_TRACER_H_
 
+#include <memory>
+
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "cobalt/script/sequence.h"
 
@@ -23,6 +26,7 @@
 namespace script {
 
 class Tracer;
+
 class Traceable {
  public:
   // Trace all native |Traceable|s accessible by the |Traceable|.  Any class
@@ -56,6 +60,16 @@
     Trace(const_cast<Traceable*>(&traceable));
   }
 
+  template <typename T>
+  void Trace(const scoped_ptr<T>& ptr) {
+    Trace(ptr.get());
+  }
+
+  template <typename T>
+  void Trace(const std::unique_ptr<T>& ptr) {
+    Trace(ptr.get());
+  }
+
   // Trace the items of a container of |Traceable|s, such as |std::vector|.
   template <typename T>
   void TraceItems(const T& items) {
diff --git a/src/cobalt/script/v8c/conversion_helpers.h b/src/cobalt/script/v8c/conversion_helpers.h
index 00d8d2f..0dea922 100644
--- a/src/cobalt/script/v8c/conversion_helpers.h
+++ b/src/cobalt/script/v8c/conversion_helpers.h
@@ -496,7 +496,7 @@
   V8cGlobalEnvironment* global_environment =
       V8cGlobalEnvironment::GetFromIsolate(isolate);
   WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
-  v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
+  v8::Local<v8::Object> object = value.As<v8::Object>();
 
   // Note that |DoesObjectImplementInterface| handles the case in which
   // |object| has no |WrapperPrivate|.
diff --git a/src/cobalt/script/v8c/interface_data.h b/src/cobalt/script/v8c/interface_data.h
deleted file mode 100644
index afd5a4d..0000000
--- a/src/cobalt/script/v8c/interface_data.h
+++ /dev/null
@@ -1,34 +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.
-
-#ifndef COBALT_SCRIPT_V8C_INTERFACE_DATA_H_
-#define COBALT_SCRIPT_V8C_INTERFACE_DATA_H_
-
-#include "v8/include/v8.h"
-
-namespace cobalt {
-namespace script {
-namespace v8c {
-
-// Data that is cached on a per-interface basis.
-struct InterfaceData {
-  v8::Eternal<v8::FunctionTemplate> function_template;
-  v8::Eternal<v8::Object> interface_object;
-};
-
-}  // namespace v8c
-}  // namespace script
-}  // namespace cobalt
-
-#endif  // COBALT_SCRIPT_V8C_INTERFACE_DATA_H_
diff --git a/src/cobalt/script/v8c/union_type_conversion_impl.h b/src/cobalt/script/v8c/union_type_conversion_impl.h
index af42037..8446e0a 100644
--- a/src/cobalt/script/v8c/union_type_conversion_impl.h
+++ b/src/cobalt/script/v8c/union_type_conversion_impl.h
@@ -92,8 +92,7 @@
     // case. Just choose the first interface in the flattened members that
     // matches.
 
-    v8::Local<v8::Context> context = isolate->GetCurrentContext();
-    v8::Local<v8::Object> object = value->ToObject(context).ToLocalChecked();
+    v8::Local<v8::Object> object = value.As<v8::Object>();
 
     V8cGlobalEnvironment* global_environment =
         V8cGlobalEnvironment::GetFromIsolate(isolate);
@@ -285,8 +284,7 @@
     // case. Just choose the first interface in the flattened members that
     // matches.
 
-    v8::Local<v8::Context> context = isolate->GetCurrentContext();
-    v8::Local<v8::Object> object = value->ToObject(context).ToLocalChecked();
+    v8::Local<v8::Object> object = value.As<v8::Object>();
 
     V8cGlobalEnvironment* global_environment =
         V8cGlobalEnvironment::GetFromIsolate(isolate);
@@ -528,8 +526,7 @@
     // case. Just choose the first interface in the flattened members that
     // matches.
 
-    v8::Local<v8::Context> context = isolate->GetCurrentContext();
-    v8::Local<v8::Object> object = value->ToObject(context).ToLocalChecked();
+    v8::Local<v8::Object> object = value.As<v8::Object>();
 
     V8cGlobalEnvironment* global_environment =
         V8cGlobalEnvironment::GetFromIsolate(isolate);
diff --git a/src/cobalt/script/v8c/union_type_conversion_impl.h.pump b/src/cobalt/script/v8c/union_type_conversion_impl.h.pump
index 00bd006..71b0ffd 100644
--- a/src/cobalt/script/v8c/union_type_conversion_impl.h.pump
+++ b/src/cobalt/script/v8c/union_type_conversion_impl.h.pump
@@ -110,8 +110,7 @@
     // case. Just choose the first interface in the flattened members that
     // matches.
 
-    v8::Local<v8::Context> context = isolate->GetCurrentContext();
-    v8::Local<v8::Object> object = value->ToObject(context).ToLocalChecked();
+    v8::Local<v8::Object> object = value.As<v8::Object>();
 
     V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
     const WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
diff --git a/src/cobalt/script/v8c/v8c.gyp b/src/cobalt/script/v8c/v8c.gyp
index 075815a..ab51b262 100644
--- a/src/cobalt/script/v8c/v8c.gyp
+++ b/src/cobalt/script/v8c/v8c.gyp
@@ -69,7 +69,6 @@
         'defines': [
           'ENGINE_SUPPORTS_INDEXED_DELETERS',
           'ENGINE_SUPPORTS_INT64',
-          'ENGINE_SUPPORTS_STACK_TRACE_COLUMNS',
           # TODO: Remove this when exact rooting and generational GC is enabled.
           'ENGINE_USES_CONSERVATIVE_ROOTING',
         ],
diff --git a/src/cobalt/script/v8c/v8c_callback_interface.cc b/src/cobalt/script/v8c/v8c_callback_interface.cc
index 9369a2a..ee89329 100644
--- a/src/cobalt/script/v8c/v8c_callback_interface.cc
+++ b/src/cobalt/script/v8c/v8c_callback_interface.cc
@@ -23,36 +23,27 @@
 // Helper class to get the actual callable object from a v8::Object
 // implementing a callback interface.
 // Returns true if a callable was found, and false if not.
-v8::MaybeLocal<v8::Object> GetCallableForCallbackInterface(
+v8::MaybeLocal<v8::Function> GetCallableForCallbackInterface(
     v8::Isolate* isolate, v8::Local<v8::Object> implementing_object,
-    const char* property_name) {
+    v8::Local<v8::String> key) {
   DCHECK(!implementing_object.IsEmpty());
-  DCHECK(property_name);
 
-  if (implementing_object->IsCallable()) {
-    return implementing_object;
+  if (implementing_object->IsFunction()) {
+    return implementing_object.As<v8::Function>();
   }
 
-  v8::MaybeLocal<v8::Value> maybe_property_value = implementing_object->Get(
-      isolate->GetCurrentContext(),
-      v8::String::NewFromUtf8(isolate, property_name,
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked());
+  v8::MaybeLocal<v8::Value> maybe_property_value =
+      implementing_object->Get(isolate->GetCurrentContext(), key);
   v8::Local<v8::Value> property_value;
   if (!maybe_property_value.ToLocal(&property_value)) {
     return {};
   }
 
-  if (!property_value->IsObject()) {
-    return {};
-  }
-  v8::Local<v8::Object> property_object =
-      v8::Local<v8::Object>::Cast(property_value);
-  if (!property_object->IsCallable()) {
+  if (!property_value->IsFunction()) {
     return {};
   }
 
-  return property_object;
+  return property_value.As<v8::Function>();
 }
 
 }  // namespace v8c
diff --git a/src/cobalt/script/v8c/v8c_callback_interface.h b/src/cobalt/script/v8c/v8c_callback_interface.h
index f446d2c..f95ce67 100644
--- a/src/cobalt/script/v8c/v8c_callback_interface.h
+++ b/src/cobalt/script/v8c/v8c_callback_interface.h
@@ -26,9 +26,9 @@
 // Helper class to get the actual callable object from a v8::Object
 // implementing a callback interface.
 // Returns true if a callable was found, and false if not.
-v8::MaybeLocal<v8::Object> GetCallableForCallbackInterface(
+v8::MaybeLocal<v8::Function> GetCallableForCallbackInterface(
     v8::Isolate* isolate, v8::Local<v8::Object> implementing_object,
-    const char* property_name);
+    v8::Local<v8::String> key);
 
 }  // namespace v8c
 }  // namespace script
diff --git a/src/cobalt/script/v8c/v8c_engine.h b/src/cobalt/script/v8c/v8c_engine.h
index 9192415..8b36a35 100644
--- a/src/cobalt/script/v8c/v8c_engine.h
+++ b/src/cobalt/script/v8c/v8c_engine.h
@@ -32,6 +32,12 @@
 
 class V8cEngine : public JavaScriptEngine {
  public:
+  // Helper function to allow others to retrieve us (a |V8cEngine|) from our
+  // |v8::Isolate|.
+  static V8cEngine* GetFromIsolate(v8::Isolate* isolate) {
+    return static_cast<V8cEngine*>(isolate->GetData(kIsolateDataIndex));
+  }
+
   explicit V8cEngine(const Options& options);
   ~V8cEngine() override;
 
@@ -42,8 +48,11 @@
   void SetGcThreshold(int64_t bytes) override;
 
   v8::Isolate* isolate() const { return isolate_; }
+  V8cHeapTracer* heap_tracer() const { return v8c_heap_tracer_.get(); }
 
  private:
+  // Where we store ourselves as embedder private data in our corresponding
+  // |v8::Isolate|.
   static const int kIsolateDataIndex = 0;
 
   base::ThreadChecker thread_checker_;
diff --git a/src/cobalt/script/v8c/v8c_global_environment.cc b/src/cobalt/script/v8c/v8c_global_environment.cc
index 0eed92b..a5eaa7d 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.cc
+++ b/src/cobalt/script/v8c/v8c_global_environment.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/debug/trace_event.h"
 #include "base/lazy_instance.h"
 #include "base/stringprintf.h"
 #include "cobalt/base/polymorphic_downcast.h"
@@ -61,6 +62,8 @@
       eval_enabled_(false),
       isolate_(isolate) {
   TRACK_MEMORY_SCOPE("Javascript");
+  TRACE_EVENT0("cobalt::script",
+               "V8cGlobalEnvironment::V8cGlobalEnvironment()");
   wrapper_factory_.reset(new WrapperFactory(isolate));
   isolate_->SetData(kIsolateDataIndex, this);
   DCHECK(isolate_->GetData(kIsolateDataIndex) == this);
@@ -70,6 +73,8 @@
 }
 
 V8cGlobalEnvironment::~V8cGlobalEnvironment() {
+  TRACE_EVENT0("cobalt::script",
+               "V8cGlobalEnvironment::~V8cGlobalEnvironment()");
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // TODO: Change type of this member to scoped_ptr
@@ -83,6 +88,7 @@
 }
 
 void V8cGlobalEnvironment::CreateGlobalObject() {
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::CreateGlobalObject()");
   TRACK_MEMORY_SCOPE("Javascript");
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -100,41 +106,19 @@
     const scoped_refptr<SourceCode>& source_code, bool mute_errors,
     std::string* out_result_utf8) {
   TRACK_MEMORY_SCOPE("Javascript");
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateScript()");
   DCHECK(thread_checker_.CalledOnValidThread());
 
   EntryScope entry_scope(isolate_);
   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
-
-  V8cSourceCode* v8c_source_code =
-      base::polymorphic_downcast<V8cSourceCode*>(source_code.get());
-  const base::SourceLocation& source_location = v8c_source_code->location();
-
   v8::TryCatch try_catch(isolate_);
-  v8::ScriptOrigin script_origin(
-      v8::String::NewFromUtf8(isolate_, source_location.file_path.c_str(),
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked(),
-      v8::Integer::New(isolate_, source_location.line_number),
-      v8::Integer::New(isolate_, source_location.column_number),
-      v8::Boolean::New(isolate_, !mute_errors));
-  v8::Local<v8::String> source =
-      v8::String::NewFromUtf8(isolate_, v8c_source_code->source_utf8().c_str(),
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
 
-  v8::MaybeLocal<v8::Script> maybe_script =
-      v8::Script::Compile(context, source, &script_origin);
-  v8::Local<v8::Script> script;
-  if (!maybe_script.ToLocal(&script)) {
-    if (out_result_utf8) {
-      *out_result_utf8 = ExceptionToString(try_catch);
-    }
-    return false;
-  }
-
-  v8::MaybeLocal<v8::Value> maybe_result = script->Run(context);
   v8::Local<v8::Value> result;
-  if (!maybe_result.ToLocal(&result)) {
+  if (!EvaluateScriptInternal(source_code, mute_errors).ToLocal(&result)) {
+    if (!try_catch.HasCaught()) {
+      LOG(WARNING) << "Script evaluation failed with no JavaScript exception.";
+      return false;
+    }
     if (out_result_utf8) {
       *out_result_utf8 = ExceptionToString(try_catch);
     }
@@ -153,38 +137,18 @@
     const scoped_refptr<Wrappable>& owning_object, bool mute_errors,
     base::optional<ValueHandleHolder::Reference>* out_value_handle) {
   TRACK_MEMORY_SCOPE("Javascript");
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateScript()");
   DCHECK(thread_checker_.CalledOnValidThread());
 
   EntryScope entry_scope(isolate_);
   v8::Local<v8::Context> context = isolate_->GetCurrentContext();
-
-  V8cSourceCode* v8c_source_code =
-      base::polymorphic_downcast<V8cSourceCode*>(source_code.get());
-  const base::SourceLocation& source_location = v8c_source_code->location();
-
   v8::TryCatch try_catch(isolate_);
-  v8::ScriptOrigin script_origin(
-      v8::String::NewFromUtf8(isolate_, source_location.file_path.c_str(),
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked(),
-      v8::Integer::New(isolate_, source_location.line_number),
-      v8::Integer::New(isolate_, source_location.column_number),
-      v8::Boolean::New(isolate_, !mute_errors));
-  v8::Local<v8::String> source =
-      v8::String::NewFromUtf8(isolate_, v8c_source_code->source_utf8().c_str(),
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
 
-  v8::MaybeLocal<v8::Script> maybe_script =
-      v8::Script::Compile(context, source, &script_origin);
-  v8::Local<v8::Script> script;
-  if (!maybe_script.ToLocal(&script)) {
-    return false;
-  }
-
-  v8::MaybeLocal<v8::Value> maybe_result = script->Run(context);
   v8::Local<v8::Value> result;
-  if (!maybe_result.ToLocal(&result)) {
+  if (!EvaluateScriptInternal(source_code, mute_errors).ToLocal(&result)) {
+    if (!try_catch.HasCaught()) {
+      LOG(WARNING) << "Script evaluation failed with no JavaScript exception.";
+    }
     return false;
   }
 
@@ -197,11 +161,20 @@
 }
 
 std::vector<StackFrame> V8cGlobalEnvironment::GetStackTrace(int max_frames) {
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::GetStackTrace()");
   DCHECK(thread_checker_.CalledOnValidThread());
+
+  // cobalt::script treats |max_frames| being set to 0 as "the entire stack",
+  // while V8 interprets the frame count being set to 0 as "give me 0 frames",
+  // so we have to translate between the two.
+  const int kV8CallMaxFrameAmount = 4096;
+  int v8_max_frames = (max_frames == 0) ? kV8CallMaxFrameAmount : max_frames;
+
   v8::HandleScope handle_scope(isolate_);
-  std::vector<StackFrame> result;
   v8::Local<v8::StackTrace> stack_trace =
-      v8::StackTrace::CurrentStackTrace(isolate_, max_frames);
+      v8::StackTrace::CurrentStackTrace(isolate_, v8_max_frames);
+
+  std::vector<StackFrame> result;
   for (int i = 0; i < stack_trace->GetFrameCount(); i++) {
     v8::Local<v8::StackFrame> stack_frame = stack_trace->GetFrame(i);
     result.emplace_back(
@@ -209,6 +182,7 @@
         *v8::String::Utf8Value(isolate_, stack_frame->GetFunctionName()),
         *v8::String::Utf8Value(isolate_, stack_frame->GetScriptName()));
   }
+
   return result;
 }
 
@@ -277,6 +251,7 @@
 
 void V8cGlobalEnvironment::Bind(const std::string& identifier,
                                 const scoped_refptr<Wrappable>& impl) {
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::Bind()");
   TRACK_MEMORY_SCOPE("Javascript");
   DCHECK(impl);
 
@@ -300,12 +275,94 @@
   return script_value_factory_;
 }
 
+v8::MaybeLocal<v8::Value> V8cGlobalEnvironment::EvaluateScriptInternal(
+    const scoped_refptr<SourceCode>& source_code, bool mute_errors) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Note that we expect an |EntryScope| and |v8::TryCatch| to have been set
+  // up by our caller.
+  V8cSourceCode* v8c_source_code =
+      base::polymorphic_downcast<V8cSourceCode*>(source_code.get());
+  const base::SourceLocation& source_location = v8c_source_code->location();
+
+  v8::Local<v8::String> resource_name;
+  if (!v8::String::NewFromUtf8(isolate_, source_location.file_path.c_str(),
+                               v8::NewStringType::kNormal)
+           .ToLocal(&resource_name)) {
+    // Technically possible, but whoa man should this never happen.
+    LOG(WARNING) << "Failed to convert source location file path \""
+                 << source_location.file_path << "\" to a V8 UTF-8 string.";
+    return {};
+  }
+
+  // Note that |v8::ScriptOrigin| offsets are 0-based, whereas
+  // |SourceLocation| line/column numbers are 1-based, so subtract 1 to
+  // translate between the two.
+  v8::ScriptOrigin script_origin(
+      /*resource_name=*/resource_name,
+      /*resource_line_offset=*/
+      v8::Integer::New(isolate_, source_location.line_number - 1),
+      /*resource_column_offset=*/
+      v8::Integer::New(isolate_, source_location.column_number - 1),
+      /*resource_is_shared_cross_origin=*/
+      v8::Boolean::New(isolate_, !mute_errors));
+
+  v8::Local<v8::String> source;
+  if (!v8::String::NewFromUtf8(isolate_, v8c_source_code->source_utf8().c_str(),
+                               v8::NewStringType::kNormal)
+           .ToLocal(&source)) {
+    LOG(WARNING) << "Failed to convert source code to V8 UTF-8 string.";
+    return {};
+  }
+
+  v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+  v8::Local<v8::Script> script;
+  if (!v8::Script::Compile(context, source, &script_origin).ToLocal(&script)) {
+    LOG(WARNING) << "Failed to compile script.";
+    return {};
+  }
+
+  v8::Local<v8::Value> result;
+  if (!script->Run(context).ToLocal(&result)) {
+    LOG(WARNING) << "Failed to run script.";
+    return {};
+  }
+
+  return result;
+}
+
 void V8cGlobalEnvironment::EvaluateAutomatics() {
+  TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateAutomatics()");
   // TODO: Maybe add fetch and stream polyfills.  Investigate what V8 has to
   // natively offer first.
   NOTIMPLEMENTED();
 }
 
+bool V8cGlobalEnvironment::HasInterfaceData(int key) const {
+  DCHECK_GE(key, 0);
+  if (key >= cached_interface_data_.size()) {
+    return false;
+  }
+  return !cached_interface_data_[key].IsEmpty();
+}
+
+v8::Local<v8::FunctionTemplate> V8cGlobalEnvironment::GetInterfaceData(
+    int key) const {
+  DCHECK(HasInterfaceData(key));
+  return cached_interface_data_[key].Get(isolate_);
+}
+
+void V8cGlobalEnvironment::AddInterfaceData(
+    int key, v8::Local<v8::FunctionTemplate> function_template) {
+  DCHECK(!HasInterfaceData(key));
+  if (key >= cached_interface_data_.size()) {
+    cached_interface_data_.resize(key + 1);
+  }
+  DCHECK(!HasInterfaceData(key));
+  cached_interface_data_[key].Set(isolate_, function_template);
+}
+
 }  // namespace v8c
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c_global_environment.h b/src/cobalt/script/v8c/v8c_global_environment.h
index 0f12324..6428f79 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.h
+++ b/src/cobalt/script/v8c/v8c_global_environment.h
@@ -27,7 +27,6 @@
 #include "base/threading/thread_checker.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
-#include "cobalt/script/v8c/interface_data.h"
 #include "cobalt/script/v8c/v8c_heap_tracer.h"
 #include "cobalt/script/v8c/wrapper_factory.h"
 #include "v8/include/libplatform/libplatform.h"
@@ -94,21 +93,23 @@
 
   ScriptValueFactory* script_value_factory() override;
 
-  // Evaluates any automatically included Javascript for the environment.
-  void EvaluateAutomatics();
-
   v8::Isolate* isolate() const { return isolate_; }
   v8::Local<v8::Context> context() const {
     return v8::Local<v8::Context>::New(isolate_, context_);
   }
 
-  InterfaceData* GetInterfaceData(int key) {
-    DCHECK_GE(key, 0);
-    if (key >= cached_interface_data_.size()) {
-      cached_interface_data_.resize(key + 1);
-    }
-    return &cached_interface_data_[key];
-  }
+  // Check whether we have interface data loaded for interface key |key|.
+  bool HasInterfaceData(int key) const;
+
+  // Get interface data for |key|.  Attempting to get interface data for a key
+  // that does not have any is a usage error.  Check |HasInterfaceData| first.
+  v8::Local<v8::FunctionTemplate> GetInterfaceData(int key) const;
+
+  // Register interface data (which is just |function_template|) for key
+  // |key|.  Attempting to add interface data for a key that already has
+  // interface data is a usage error.
+  void AddInterfaceData(int key,
+                        v8::Local<v8::FunctionTemplate> function_template);
 
   WrapperFactory* wrapper_factory() { return wrapper_factory_.get(); }
 
@@ -143,6 +144,12 @@
     int count;
   };
 
+  v8::MaybeLocal<v8::Value> EvaluateScriptInternal(
+      const scoped_refptr<SourceCode>& source_code, bool mute_errors);
+
+  // Evaluates any automatically included Javascript for the environment.
+  void EvaluateAutomatics();
+
   // Where we store ourselves as embedder private data in our corresponding
   // |v8::Isolate|.
   static const int kIsolateDataIndex = 1;
@@ -155,7 +162,10 @@
   v8::Global<v8::Object> global_object_;
   scoped_ptr<WrapperFactory> wrapper_factory_;
 
-  std::vector<InterfaceData> cached_interface_data_;
+  // Data that is cached on a per-interface basis. Note that we can get to
+  // everything (the function instance, the prototype template, and the
+  // instance template) from just the function template.
+  std::vector<v8::Eternal<v8::FunctionTemplate>> cached_interface_data_;
 
   // TODO: Should be scoped_ptr, fix headers/sources template mess.
   V8cScriptValueFactory* script_value_factory_;
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.cc b/src/cobalt/script/v8c/v8c_heap_tracer.cc
index 8bf7f91..0c19377 100644
--- a/src/cobalt/script/v8c/v8c_heap_tracer.cc
+++ b/src/cobalt/script/v8c/v8c_heap_tracer.cc
@@ -28,9 +28,11 @@
     WrapperPrivate* wrapper_private =
         static_cast<WrapperPrivate*>(embedder_field.first);
     wrapper_private->Mark();
+
     Wrappable* wrappable = wrapper_private->raw_wrappable();
-    frontier_.push_back(wrappable);
-    visited_.insert(wrappable);
+    if (visited_.insert(wrappable).second) {
+      frontier_.push_back(wrappable);
+    }
 
     // We expect this field to always be null, since we only have it as a
     // workaround for V8.  See "wrapper_private.h" for details.
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.h b/src/cobalt/script/v8c/v8c_heap_tracer.h
index 0fc1a41..e081277 100644
--- a/src/cobalt/script/v8c/v8c_heap_tracer.h
+++ b/src/cobalt/script/v8c/v8c_heap_tracer.h
@@ -57,7 +57,7 @@
 
  private:
   v8::Isolate* const isolate_;
-  v8::Platform* platform_ = GetPlatform();
+  v8::Platform* const platform_ = GetPlatform();
   std::vector<Traceable*> frontier_;
   std::unordered_set<Traceable*> visited_;
 };
diff --git a/src/cobalt/script/v8c/v8c_user_object_holder.h b/src/cobalt/script/v8c/v8c_user_object_holder.h
index 8e54923..29d6e33 100644
--- a/src/cobalt/script/v8c/v8c_user_object_holder.h
+++ b/src/cobalt/script/v8c/v8c_user_object_holder.h
@@ -38,22 +38,53 @@
  public:
   typedef ScriptValue<typename V8cUserObjectType::BaseType> BaseClass;
 
-  V8cUserObjectHolder()
-      : isolate_(nullptr), prevent_garbage_collection_count_(0) {}
-
+  V8cUserObjectHolder() = default;
   V8cUserObjectHolder(v8::Isolate* isolate, v8::Local<v8::Value> value)
       : isolate_(isolate),
         handle_(isolate, value),
         prevent_garbage_collection_count_(0) {
     handle_.SetWeak();
   }
-
-  V8cUserObjectHolder& operator=(const V8cUserObjectHolder& other) {
+  V8cUserObjectHolder(const V8cUserObjectHolder&) = delete;
+  V8cUserObjectHolder& operator=(const V8cUserObjectHolder&) = delete;
+  V8cUserObjectHolder(V8cUserObjectHolder&& other)
+      : isolate_(other.isolate_),
+        handle_(other.isolate_, other.v8_value()),
+        prevent_garbage_collection_count_(
+            other.prevent_garbage_collection_count_) {
+    if (prevent_garbage_collection_count_ == 0) {
+      handle_.SetWeak();
+    }
+    other.isolate_ = nullptr;
+    other.handle_.Clear();
+    other.prevent_garbage_collection_count_ = 0;
+  }
+  V8cUserObjectHolder& operator=(V8cUserObjectHolder&& other) {
     isolate_ = other.isolate_;
     handle_.Set(isolate_, other.v8_value());
+    prevent_garbage_collection_count_ = other.prevent_garbage_collection_count_;
+    if (prevent_garbage_collection_count_ == 0) {
+      handle_.SetWeak();
+    }
+    other.isolate_ = nullptr;
+    other.handle_.Clear();
+    other.prevent_garbage_collection_count_ = 0;
     return *this;
   }
 
+  bool EqualTo(const BaseClass& other) const override {
+    v8::HandleScope handle_scope(isolate_);
+    const V8cUserObjectHolder* v8c_other =
+        base::polymorphic_downcast<const V8cUserObjectHolder*>(&other);
+    return v8_value() == v8c_other->v8_value();
+  }
+
+  void TraceMembers(Tracer* tracer) override {
+    if (!handle_.IsEmpty()) {
+      handle_.Get().RegisterExternalReference(isolate_);
+    }
+  }
+
   void RegisterOwner(Wrappable* owner) override {
     V8cGlobalEnvironment* global_environment =
         V8cGlobalEnvironment::GetFromIsolate(isolate_);
@@ -90,29 +121,22 @@
     }
   }
 
+  const typename V8cUserObjectType::BaseType* GetScriptValue() const override {
+    return handle_.IsEmpty() ? nullptr : &handle_;
+  }
+
   scoped_ptr<BaseClass> MakeCopy() const override {
     v8::HandleScope handle_scope(isolate_);
     return make_scoped_ptr<BaseClass>(
         new V8cUserObjectHolder(isolate_, v8_value()));
   }
 
-  bool EqualTo(const BaseClass& other) const override {
-    v8::HandleScope handle_scope(isolate_);
-    const V8cUserObjectHolder* v8c_other =
-        base::polymorphic_downcast<const V8cUserObjectHolder*>(&other);
-    return v8_value() == v8c_other->v8_value();
-  }
-
-  const typename V8cUserObjectType::BaseType* GetScriptValue() const override {
-    return handle_.IsEmpty() ? nullptr : &handle_;
-  }
-
   v8::Local<v8::Value> v8_value() const { return handle_.NewLocal(isolate_); }
 
  private:
-  v8::Isolate* isolate_;
-  V8cUserObjectType handle_;
-  int prevent_garbage_collection_count_;
+  v8::Isolate* isolate_ = nullptr;
+  V8cUserObjectType handle_{};
+  int prevent_garbage_collection_count_ = 0;
 };
 
 }  // namespace v8c
diff --git a/src/cobalt/script/v8c/wrapper_factory.cc b/src/cobalt/script/v8c/wrapper_factory.cc
index 3af19cf..45e7de6 100644
--- a/src/cobalt/script/v8c/wrapper_factory.cc
+++ b/src/cobalt/script/v8c/wrapper_factory.cc
@@ -29,12 +29,11 @@
 
 void WrapperFactory::RegisterWrappableType(
     base::TypeId wrappable_type, const CreateWrapperFunction& create_function,
-    const PrototypeClassFunction& class_function) {
-  std::pair<WrappableTypeFunctionsHashMap::iterator, bool> pib =
-      wrappable_type_functions_.insert(std::make_pair(
-          wrappable_type,
-          WrappableTypeFunctions(create_function, class_function)));
-  DCHECK(pib.second)
+    const GetFunctionTemplate& get_function_template) {
+  auto insert_pair = wrappable_type_functions_.insert(std::make_pair(
+      wrappable_type,
+      WrappableTypeFunctions(create_function, get_function_template)));
+  DCHECK(insert_pair.second)
       << "RegisterWrappableType registered for type more than once.";
 }
 
@@ -61,8 +60,7 @@
 
 scoped_ptr<Wrappable::WeakWrapperHandle> WrapperFactory::CreateWrapper(
     const scoped_refptr<Wrappable>& wrappable) const {
-  WrappableTypeFunctionsHashMap::const_iterator it =
-      wrappable_type_functions_.find(wrappable->GetWrappableType());
+  auto it = wrappable_type_functions_.find(wrappable->GetWrappableType());
   if (it == wrappable_type_functions_.end()) {
     NOTREACHED();
     return scoped_ptr<Wrappable::WeakWrapperHandle>();
@@ -78,7 +76,7 @@
 
 bool WrapperFactory::DoesObjectImplementInterface(v8::Local<v8::Object> object,
                                                   base::TypeId type_id) const {
-  // If the object doesn't have a wrapper private which means it is not a
+  // If the object doesn't have a wrapper private, then that means it is not a
   // platform object, so the object doesn't implement the interface.
   if (!WrapperPrivate::HasWrapperPrivate(object)) {
     return false;
@@ -88,7 +86,7 @@
   DCHECK(it != wrappable_type_functions_.end());
 
   v8::Local<v8::FunctionTemplate> function_template =
-      it->second.prototype_class.Run(isolate_);
+      it->second.get_function_template.Run(isolate_);
   return function_template->HasInstance(object);
 }
 
diff --git a/src/cobalt/script/v8c/wrapper_factory.h b/src/cobalt/script/v8c/wrapper_factory.h
index 7991b05..4910bbc 100644
--- a/src/cobalt/script/v8c/wrapper_factory.h
+++ b/src/cobalt/script/v8c/wrapper_factory.h
@@ -35,15 +35,15 @@
                                                const scoped_refptr<Wrappable>&)>
       CreateWrapperFunction;
 
-  // Callback to get v8::FunctionTemplate of prototype.
+  // Callback to get v8::FunctionTemplate of an interface.
   typedef base::Callback<v8::Local<v8::FunctionTemplate>(v8::Isolate*)>
-      PrototypeClassFunction;
+      GetFunctionTemplate;
 
   explicit WrapperFactory(v8::Isolate* isolate) : isolate_(isolate) {}
 
   void RegisterWrappableType(base::TypeId wrappable_type,
                              const CreateWrapperFunction& create_function,
-                             const PrototypeClassFunction& class_function);
+                             const GetFunctionTemplate& class_function);
 
   v8::Local<v8::Object> GetWrapper(const scoped_refptr<Wrappable>& wrappable);
 
@@ -57,10 +57,11 @@
  private:
   struct WrappableTypeFunctions {
     CreateWrapperFunction create_wrapper;
-    PrototypeClassFunction prototype_class;
+    GetFunctionTemplate get_function_template;
     WrappableTypeFunctions(const CreateWrapperFunction& create_wrapper,
-                           const PrototypeClassFunction& prototype_class)
-        : create_wrapper(create_wrapper), prototype_class(prototype_class) {}
+                           const GetFunctionTemplate& get_function_template)
+        : create_wrapper(create_wrapper),
+          get_function_template(get_function_template) {}
   };
 
   scoped_ptr<Wrappable::WeakWrapperHandle> CreateWrapper(
diff --git a/src/cobalt/script/v8c/wrapper_private.h b/src/cobalt/script/v8c/wrapper_private.h
index f7203b0..646213c 100644
--- a/src/cobalt/script/v8c/wrapper_private.h
+++ b/src/cobalt/script/v8c/wrapper_private.h
@@ -33,7 +33,6 @@
   // The callback that V8 will run when the |v8::Object| that we live inside
   // of dies.
   static void Callback(const v8::WeakCallbackInfo<WrapperPrivate>& data) {
-    data.GetParameter()->wrapper_.Reset();
     delete data.GetParameter();
   }
 
@@ -50,6 +49,9 @@
     return object->InternalFieldCount() == kInternalFieldCount;
   }
 
+  // The total amount of internal fields in |wrapper_| we use.  See
+  // |kInternalFieldDataIndex| and |kInternalFieldDummyIndex| below for
+  // further information.
   static const int kInternalFieldCount = 2;
 
   WrapperPrivate() = delete;
@@ -63,6 +65,11 @@
     wrapper_.SetWeak(this, &WrapperPrivate::Callback,
                      v8::WeakCallbackType::kParameter);
   }
+  ~WrapperPrivate() {
+    DCHECK(wrapper_.IsNearDeath());
+    wrapper_.ClearWeak();
+    wrapper_.Reset();
+  }
 
   // Mark |wrapper_| as reachable from other |Traceable|s.  This will be
   // called by |V8cHeapTracer| during tracing.
diff --git a/src/cobalt/tools/automated_testing/c_val_names.py b/src/cobalt/tools/automated_testing/c_val_names.py
index 2400cfd..226ee9a 100644
--- a/src/cobalt/tools/automated_testing/c_val_names.py
+++ b/src/cobalt/tools/automated_testing/c_val_names.py
@@ -49,6 +49,10 @@
   return 'Count.Renderer.Rasterize.NewRenderTree'
 
 
+def count_version_compatibility_violation():
+  return 'Count.VersionCompatibilityViolation'
+
+
 def event_duration_dom_video_start_delay():
   return 'Event.Duration.MainWebModule.DOM.VideoStartDelay'
 
@@ -67,10 +71,14 @@
   return 'Event.MainWebModule.{}.ValueDictionary'.format(event_type)
 
 
-def layout_is_render_tree_pending():
+def is_render_tree_generation_pending():
   return 'MainWebModule.Layout.IsRenderTreePending'
 
 
+def is_render_tree_rasterization_pending():
+  return 'MainWebModule.IsRenderTreeRasterizationPending'
+
+
 def memory_font_cache_resource_loaded():
   return 'Memory.MainWebModule.RemoteTypefaceCache.Resource.Loaded'
 
diff --git a/src/cobalt/tools/automated_testing/cobalt_test.py b/src/cobalt/tools/automated_testing/cobalt_test.py
index e573d54..f135f30 100644
--- a/src/cobalt/tools/automated_testing/cobalt_test.py
+++ b/src/cobalt/tools/automated_testing/cobalt_test.py
@@ -227,7 +227,8 @@
   def is_processing(self, check_animations):
     """Checks to see if Cobalt is currently processing."""
     return (self.get_cval(c_val_names.count_dom_active_java_script_events()) or
-            self.get_cval(c_val_names.layout_is_render_tree_pending()) or
+            self.get_cval(c_val_names.is_render_tree_generation_pending()) or
+            self.get_cval(c_val_names.is_render_tree_rasterization_pending()) or
             (check_animations and
              self.get_cval(c_val_names.renderer_has_active_animations())) or
             self.get_cval(c_val_names.count_image_cache_resource_loading()))
diff --git a/src/cobalt/trace_event/scoped_trace_to_file.h b/src/cobalt/trace_event/scoped_trace_to_file.h
index 8c2b2f5..32ba2ce 100644
--- a/src/cobalt/trace_event/scoped_trace_to_file.h
+++ b/src/cobalt/trace_event/scoped_trace_to_file.h
@@ -33,6 +33,8 @@
   explicit ScopedTraceToFile(const FilePath& output_path_relative_to_logs);
   ~ScopedTraceToFile();
 
+  const FilePath& absolute_output_path() const { return absolute_output_path_; }
+
  private:
   FilePath absolute_output_path_;
 };
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index 6baf5a2..d30a2c0 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -8,6 +8,13 @@
 
 **NOTE: Starboard versions 3 and below are no longer supported.**
 
+## Version 10
+
+### Moved `tizen` to `contrib/tizen`.
+
+Please see [contrib/README.md](contrib/README.md) for description of
+expectations for contents in this directory.
+
 ## Version 9
 
 ### Add string label to `SbMicrophoneInfo`.
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 9e5f2d6..3b3da18 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -71,6 +71,9 @@
 // Minimum API version for supporting system-level closed caption settings.
 #define SB_ACCESSIBILITY_CAPTIONS_API_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// Minimum API version for supporting audioless video playback.
+#define SB_AUDIOLESS_VIDEO_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // --- Release Candidate Feature Defines -------------------------------------
 
 // --- Common Detected Features ----------------------------------------------
diff --git a/src/starboard/contrib/README.md b/src/starboard/contrib/README.md
new file mode 100644
index 0000000..fe405cc
--- /dev/null
+++ b/src/starboard/contrib/README.md
@@ -0,0 +1,10 @@
+# The starboard/contrib directroy
+
+Contents of this directory have been contributed by the community, but
+have not been fully integrated into the Google-managed internal continuous
+integration system.
+
+Therefore, while ports in this directory have compiled and worked correctly
+in the past, they may require some maintenence to work with the current
+release of Cobalt.
+
diff --git a/src/starboard/contrib/__init__.py b/src/starboard/contrib/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/starboard/contrib/__init__.py
@@ -0,0 +1 @@
+
diff --git a/src/starboard/contrib/_env.py b/src/starboard/contrib/_env.py
new file mode 100644
index 0000000..f697920
--- /dev/null
+++ b/src/starboard/contrib/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/creator/README.md b/src/starboard/contrib/creator/README.md
similarity index 100%
rename from src/starboard/creator/README.md
rename to src/starboard/contrib/creator/README.md
diff --git a/src/starboard/creator/__init__.py b/src/starboard/contrib/creator/__init__.py
similarity index 100%
rename from src/starboard/creator/__init__.py
rename to src/starboard/contrib/creator/__init__.py
diff --git a/src/starboard/contrib/creator/_env.py b/src/starboard/contrib/creator/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/starboard/contrib/creator/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/creator/__init__.py b/src/starboard/contrib/creator/ci20x11/__init__.py
similarity index 100%
copy from src/starboard/creator/__init__.py
copy to src/starboard/contrib/creator/ci20x11/__init__.py
diff --git a/src/starboard/contrib/creator/ci20x11/_env.py b/src/starboard/contrib/creator/ci20x11/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/starboard/contrib/creator/ci20x11/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/atomic_public.h b/src/starboard/contrib/creator/ci20x11/atomic_public.h
similarity index 78%
rename from src/starboard/creator/ci20x11/gcc/4.9/atomic_public.h
rename to src/starboard/contrib/creator/ci20x11/atomic_public.h
index 547cf9a..db75dc1 100644
--- a/src/starboard/creator/ci20x11/gcc/4.9/atomic_public.h
+++ b/src/starboard/contrib/creator/ci20x11/atomic_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
+#define STARBOARD_CONTRIB_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
 
 #include "starboard/linux/shared/atomic_public.h"
 
-#endif  // STARBOARD_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/configuration_public.h b/src/starboard/contrib/creator/ci20x11/configuration_public.h
similarity index 70%
rename from src/starboard/creator/ci20x11/gcc/4.9/configuration_public.h
rename to src/starboard/contrib/creator/ci20x11/configuration_public.h
index e47392a..d5b6080 100644
--- a/src/starboard/creator/ci20x11/gcc/4.9/configuration_public.h
+++ b/src/starboard/contrib/creator/ci20x11/configuration_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_CREATOR_CI20X11_GCC_4_9_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_GCC_4_9_CONFIGURATION_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_CONTRIB_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
 
-#include "starboard/creator/shared/configuration_public.h"
+#include "starboard/contrib/creator/shared/configuration_public.h"
 
-#endif  // STARBOARD_CREATOR_CI20X11_GCC_4_9_CONFIGURATION_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/atomic_public.h b/src/starboard/contrib/creator/ci20x11/gcc/4.9/atomic_public.h
similarity index 76%
copy from src/starboard/creator/ci20x11/gcc/4.9/atomic_public.h
copy to src/starboard/contrib/creator/ci20x11/gcc/4.9/atomic_public.h
index 547cf9a..05a42e8 100644
--- a/src/starboard/creator/ci20x11/gcc/4.9/atomic_public.h
+++ b/src/starboard/contrib/creator/ci20x11/gcc/4.9/atomic_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
+#define STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
 
 #include "starboard/linux/shared/atomic_public.h"
 
-#endif  // STARBOARD_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/compiler_flags.gypi b/src/starboard/contrib/creator/ci20x11/gcc/4.9/compiler_flags.gypi
similarity index 100%
rename from src/starboard/creator/ci20x11/gcc/4.9/compiler_flags.gypi
rename to src/starboard/contrib/creator/ci20x11/gcc/4.9/compiler_flags.gypi
diff --git a/src/starboard/creator/ci20x11/thread_types_public.h b/src/starboard/contrib/creator/ci20x11/gcc/4.9/configuration_public.h
similarity index 67%
copy from src/starboard/creator/ci20x11/thread_types_public.h
copy to src/starboard/contrib/creator/ci20x11/gcc/4.9/configuration_public.h
index 7437a70..e9d4416 100644
--- a/src/starboard/creator/ci20x11/thread_types_public.h
+++ b/src/starboard/contrib/creator/ci20x11/gcc/4.9/configuration_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_CONFIGURATION_PUBLIC_H_
 
-#include "starboard/linux/shared/thread_types_public.h"
+#include "starboard/contrib/creator/shared/configuration_public.h"
 
-#endif  // STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/gyp_configuration.gypi b/src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.gypi
similarity index 100%
rename from src/starboard/creator/ci20x11/gcc/4.9/gyp_configuration.gypi
rename to src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.gypi
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/gyp_configuration.py b/src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.py
similarity index 98%
rename from src/starboard/creator/ci20x11/gcc/4.9/gyp_configuration.py
rename to src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.py
index f180c32..e20b9f7 100644
--- a/src/starboard/creator/ci20x11/gcc/4.9/gyp_configuration.py
+++ b/src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.py
@@ -20,7 +20,7 @@
 # pylint: disable=import-self,g-import-not-at-top
 import gyp_utils
 # Import the shared Linux platform configuration.
-from starboard.creator.shared import gyp_configuration
+from starboard.contrib.creator.shared import gyp_configuration
 from starboard.tools.testing import test_filter
 
 class PlatformConfig(gyp_configuration.PlatformConfig):
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/starboard_platform.gyp b/src/starboard/contrib/creator/ci20x11/gcc/4.9/starboard_platform.gyp
similarity index 100%
rename from src/starboard/creator/ci20x11/gcc/4.9/starboard_platform.gyp
rename to src/starboard/contrib/creator/ci20x11/gcc/4.9/starboard_platform.gyp
diff --git a/src/starboard/creator/ci20x11/gcc/4.9/thread_types_public.h b/src/starboard/contrib/creator/ci20x11/gcc/4.9/thread_types_public.h
similarity index 75%
rename from src/starboard/creator/ci20x11/gcc/4.9/thread_types_public.h
rename to src/starboard/contrib/creator/ci20x11/gcc/4.9/thread_types_public.h
index 34bd624..b442fe0 100644
--- a/src/starboard/creator/ci20x11/gcc/4.9/thread_types_public.h
+++ b/src/starboard/contrib/creator/ci20x11/gcc/4.9/thread_types_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_CREATOR_CI20X11_GCC_4_9_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_GCC_4_9_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_THREAD_TYPES_PUBLIC_H_
 
 #include "starboard/linux/shared/thread_types_public.h"
 
-#endif  // STARBOARD_CREATOR_CI20X11_GCC_4_9_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_CREATOR_CI20X11_GCC_4_9_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/creator/ci20x11/gcc/gyp_configuration.gypi b/src/starboard/contrib/creator/ci20x11/gcc/gyp_configuration.gypi
similarity index 100%
rename from src/starboard/creator/ci20x11/gcc/gyp_configuration.gypi
rename to src/starboard/contrib/creator/ci20x11/gcc/gyp_configuration.gypi
diff --git a/src/starboard/creator/ci20x11/gcc/starboard_platform.gyp b/src/starboard/contrib/creator/ci20x11/gcc/starboard_platform.gyp
similarity index 100%
rename from src/starboard/creator/ci20x11/gcc/starboard_platform.gyp
rename to src/starboard/contrib/creator/ci20x11/gcc/starboard_platform.gyp
diff --git a/src/starboard/creator/ci20x11/gyp_configuration.gypi b/src/starboard/contrib/creator/ci20x11/gyp_configuration.gypi
similarity index 100%
rename from src/starboard/creator/ci20x11/gyp_configuration.gypi
rename to src/starboard/contrib/creator/ci20x11/gyp_configuration.gypi
diff --git a/src/starboard/creator/ci20x11/gyp_configuration.py b/src/starboard/contrib/creator/ci20x11/gyp_configuration.py
similarity index 96%
rename from src/starboard/creator/ci20x11/gyp_configuration.py
rename to src/starboard/contrib/creator/ci20x11/gyp_configuration.py
index 3d62ed8..09351b4 100644
--- a/src/starboard/creator/ci20x11/gyp_configuration.py
+++ b/src/starboard/contrib/creator/ci20x11/gyp_configuration.py
@@ -17,8 +17,9 @@
 import os
 import sys
 
+import _env  # pylint: disable=unused-import
 # Import the shared Creator platform configuration.
-from starboard.creator.shared import gyp_configuration
+from starboard.contrib.creator.shared import gyp_configuration
 from starboard.tools.testing import test_filter
 
 class PlatformConfig(gyp_configuration.PlatformConfig):
diff --git a/src/starboard/creator/ci20x11/libraries.gypi b/src/starboard/contrib/creator/ci20x11/libraries.gypi
similarity index 100%
rename from src/starboard/creator/ci20x11/libraries.gypi
rename to src/starboard/contrib/creator/ci20x11/libraries.gypi
diff --git a/src/starboard/creator/ci20x11/main.cc b/src/starboard/contrib/creator/ci20x11/main.cc
similarity index 100%
rename from src/starboard/creator/ci20x11/main.cc
rename to src/starboard/contrib/creator/ci20x11/main.cc
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/contrib/creator/ci20x11/starboard_platform.gyp
similarity index 98%
rename from src/starboard/creator/ci20x11/starboard_platform.gyp
rename to src/starboard/contrib/creator/ci20x11/starboard_platform.gyp
index c1e7eb0..3ae1a09 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/contrib/creator/ci20x11/starboard_platform.gyp
@@ -25,11 +25,11 @@
       'target_name': 'starboard_platform',
       'type': 'static_library',
       'sources': [
-        '<(DEPTH)/starboard/creator/ci20x11/atomic_public.h',
-        '<(DEPTH)/starboard/creator/ci20x11/configuration_public.h',
-        '<(DEPTH)/starboard/creator/ci20x11/main.cc',
-        '<(DEPTH)/starboard/creator/ci20x11/system_get_property.cc',
-        '<(DEPTH)/starboard/creator/shared/player_components_impl.cc',
+        '<(DEPTH)/starboard/contrib/creator/ci20x11/atomic_public.h',
+        '<(DEPTH)/starboard/contrib/creator/ci20x11/configuration_public.h',
+        '<(DEPTH)/starboard/contrib/creator/ci20x11/main.cc',
+        '<(DEPTH)/starboard/contrib/creator/ci20x11/system_get_property.cc',
+        '<(DEPTH)/starboard/contrib/creator/shared/player_components_impl.cc',
         '<(DEPTH)/starboard/linux/shared/atomic_public.h',
         '<(DEPTH)/starboard/linux/shared/decode_target_internal.h',
         '<(DEPTH)/starboard/linux/shared/decode_target_internal.cc',
diff --git a/src/starboard/creator/ci20x11/system_get_property.cc b/src/starboard/contrib/creator/ci20x11/system_get_property.cc
similarity index 100%
rename from src/starboard/creator/ci20x11/system_get_property.cc
rename to src/starboard/contrib/creator/ci20x11/system_get_property.cc
diff --git a/src/starboard/creator/ci20x11/thread_types_public.h b/src/starboard/contrib/creator/ci20x11/thread_types_public.h
similarity index 77%
rename from src/starboard/creator/ci20x11/thread_types_public.h
rename to src/starboard/contrib/creator/ci20x11/thread_types_public.h
index 7437a70..4a0e470 100644
--- a/src/starboard/creator/ci20x11/thread_types_public.h
+++ b/src/starboard/contrib/creator/ci20x11/thread_types_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_CONTRIB_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
 
 #include "starboard/linux/shared/thread_types_public.h"
 
-#endif  // STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/creator/shared/__init__.py b/src/starboard/contrib/creator/shared/__init__.py
similarity index 100%
rename from src/starboard/creator/shared/__init__.py
rename to src/starboard/contrib/creator/shared/__init__.py
diff --git a/src/starboard/contrib/creator/shared/_env.py b/src/starboard/contrib/creator/shared/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/starboard/contrib/creator/shared/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/contrib/creator/shared/configuration_public.h
similarity index 98%
rename from src/starboard/creator/shared/configuration_public.h
rename to src/starboard/contrib/creator/shared/configuration_public.h
index 9172529..1ea3bed 100644
--- a/src/starboard/creator/shared/configuration_public.h
+++ b/src/starboard/contrib/creator/shared/configuration_public.h
@@ -14,8 +14,8 @@
 
 // The shared Starboard configuration for Creator devices.
 
-#ifndef STARBOARD_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_CONTRIB_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
 
 // --- Architecture Configuration --------------------------------------------
 
@@ -444,4 +444,4 @@
 #error "CREATOR_SHARED builds need a GCC-like compiler (for the moment)."
 #endif
 
-#endif  // STARBOARD_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/creator/shared/gyp_configuration.gypi b/src/starboard/contrib/creator/shared/gyp_configuration.gypi
similarity index 100%
rename from src/starboard/creator/shared/gyp_configuration.gypi
rename to src/starboard/contrib/creator/shared/gyp_configuration.gypi
diff --git a/src/starboard/creator/shared/gyp_configuration.py b/src/starboard/contrib/creator/shared/gyp_configuration.py
similarity index 98%
rename from src/starboard/creator/shared/gyp_configuration.py
rename to src/starboard/contrib/creator/shared/gyp_configuration.py
index b5f75cb..cbee9c8 100644
--- a/src/starboard/creator/shared/gyp_configuration.py
+++ b/src/starboard/contrib/creator/shared/gyp_configuration.py
@@ -20,6 +20,8 @@
 
 import config.base
 import gyp_utils
+
+import _env  # pylint: disable=unused-import
 from starboard.tools.testing import test_filter
 
 
diff --git a/src/starboard/creator/shared/player_components_impl.cc b/src/starboard/contrib/creator/shared/player_components_impl.cc
similarity index 100%
rename from src/starboard/creator/shared/player_components_impl.cc
rename to src/starboard/contrib/creator/shared/player_components_impl.cc
diff --git a/src/starboard/tizen/armv7l/atomic_public.h b/src/starboard/contrib/tizen/armv7l/atomic_public.h
similarity index 73%
rename from src/starboard/tizen/armv7l/atomic_public.h
rename to src/starboard/contrib/tizen/armv7l/atomic_public.h
index d42b571..b1adbbe 100644
--- a/src/starboard/tizen/armv7l/atomic_public.h
+++ b/src/starboard/contrib/tizen/armv7l/atomic_public.h
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef STARBOARD_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
-#define STARBOARD_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
+#define STARBOARD_CONTRIB_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
 
-#include "starboard/tizen/shared/atomic_public.h"
+#include "starboard/contrib/tizen/shared/atomic_public.h"
 
-#endif  // STARBOARD_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_TIZEN_ARMV7L_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/tizen/armv7l/configuration_public.h b/src/starboard/contrib/tizen/armv7l/configuration_public.h
similarity index 95%
rename from src/starboard/tizen/armv7l/configuration_public.h
rename to src/starboard/contrib/tizen/armv7l/configuration_public.h
index b5b8830..3b9e99b 100644
--- a/src/starboard/tizen/armv7l/configuration_public.h
+++ b/src/starboard/contrib/tizen/armv7l/configuration_public.h
@@ -19,8 +19,8 @@
 // Other source files should never include this header directly, but should
 // include the generic "starboard/configuration.h" instead.
 
-#ifndef STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_CONTRIB_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
 
 // The API version implemented by this platform.
 #define SB_API_VERSION 4
@@ -152,7 +152,7 @@
 // --- Common Configuration ---------------------------------------------------
 
 // Include the Tizen configuration that's common between all Tizen.
-#include "starboard/tizen/shared/configuration_public.h"
+#include "starboard/contrib/tizen/shared/configuration_public.h"
 
 // --- User Configuration ----------------------------------------------------
 
@@ -165,4 +165,4 @@
 // has spent in the executing state.
 #define SB_HAS_TIME_THREAD_NOW 1
 
-#endif  // STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
+#endif  // STARBOARD_CONTRIB_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/tizen/armv7l/gyp_configuration.gypi b/src/starboard/contrib/tizen/armv7l/gyp_configuration.gypi
similarity index 100%
rename from src/starboard/tizen/armv7l/gyp_configuration.gypi
rename to src/starboard/contrib/tizen/armv7l/gyp_configuration.gypi
diff --git a/src/starboard/tizen/armv7l/gyp_configuration.py b/src/starboard/contrib/tizen/armv7l/gyp_configuration.py
similarity index 100%
rename from src/starboard/tizen/armv7l/gyp_configuration.py
rename to src/starboard/contrib/tizen/armv7l/gyp_configuration.py
diff --git a/src/starboard/tizen/armv7l/starboard_common.gyp b/src/starboard/contrib/tizen/armv7l/starboard_common.gyp
similarity index 92%
rename from src/starboard/tizen/armv7l/starboard_common.gyp
rename to src/starboard/contrib/tizen/armv7l/starboard_common.gyp
index 86c0acf..d627fbd 100644
--- a/src/starboard/tizen/armv7l/starboard_common.gyp
+++ b/src/starboard/contrib/tizen/armv7l/starboard_common.gyp
@@ -219,13 +219,13 @@
         '<(DEPTH)/starboard/shared/wayland/window_get_platform_handle.cc',
         '<(DEPTH)/starboard/shared/wayland/window_get_size.cc',
         '<(DEPTH)/starboard/shared/wayland/window_internal.cc',
-        '<(DEPTH)/starboard/tizen/shared/system_get_device_type.cc',
-        '<(DEPTH)/starboard/tizen/shared/thread_create.cc',
-        '<(DEPTH)/starboard/tizen/shared/audio/audio_sink_adaptor.cc',
-        '<(DEPTH)/starboard/tizen/shared/audio/audio_sink_private.cc',
-        '<(DEPTH)/starboard/tizen/shared/log/log.cc',
-        '<(DEPTH)/starboard/tizen/shared/get_home_directory.cc',
-        '<(DEPTH)/starboard/tizen/shared/memory_flush.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system_get_device_type.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/thread_create.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/audio/audio_sink_adaptor.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/audio/audio_sink_private.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/log/log.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/get_home_directory.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/memory_flush.cc',
       ],
       'defines': [
         # This must be defined when building Starboard, and must not when
@@ -234,14 +234,14 @@
       ],
       'dependencies': [
         '<(DEPTH)/starboard/common/common.gyp:common',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:capi-appfw-application',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:capi-media-audio-io',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:edbus',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:elementary',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:evas',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:gles20',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:tizen-extension-client',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:wayland-egl',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:capi-appfw-application',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:capi-media-audio-io',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:edbus',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:elementary',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:evas',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:gles20',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:tizen-extension-client',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:wayland-egl',
         '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
         'starboard_base_symbolize',
       ],
diff --git a/src/starboard/tizen/armv7l/starboard_platform.gyp b/src/starboard/contrib/tizen/armv7l/starboard_platform.gyp
similarity index 90%
rename from src/starboard/tizen/armv7l/starboard_platform.gyp
rename to src/starboard/contrib/tizen/armv7l/starboard_platform.gyp
index 37371df..ac9a851 100644
--- a/src/starboard/tizen/armv7l/starboard_platform.gyp
+++ b/src/starboard/contrib/tizen/armv7l/starboard_platform.gyp
@@ -105,17 +105,17 @@
         '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
         '<(DEPTH)/starboard/shared/stub/media_is_transfer_characteristics_supported.cc',
         '<(DEPTH)/starboard/shared/wayland/application_wayland.cc',
-        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc',
-        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h',
+        '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h',
-        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc',
-        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_common.h',
-        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc',
-        '<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h',
-        '<(DEPTH)/starboard/tizen/shared/main.cc',
-        '<(DEPTH)/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc',
-        '<(DEPTH)/starboard/tizen/shared/system_get_path.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.h',
+        '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.h',
+        '<(DEPTH)/starboard/contrib/tizen/shared/main.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/player/filter/ffmpeg_player_components_impl.cc',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system_get_path.cc',
         'atomic_public.h',
         'configuration_public.h',
         'system_get_property.cc',
@@ -127,8 +127,8 @@
       ],
       'dependencies': [
         '<(DEPTH)/starboard/common/common.gyp:common',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:aul',
-        '<(DEPTH)/starboard/tizen/shared/system.gyp:elementary',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:aul',
+        '<(DEPTH)/starboard/contrib/tizen/shared/system.gyp:elementary',
         '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
         'starboard_common.gyp:starboard_common',
       ],
diff --git a/src/starboard/tizen/armv7l/system_get_property.cc b/src/starboard/contrib/tizen/armv7l/system_get_property.cc
similarity index 100%
rename from src/starboard/tizen/armv7l/system_get_property.cc
rename to src/starboard/contrib/tizen/armv7l/system_get_property.cc
diff --git a/src/starboard/tizen/armv7l/thread_types_public.h b/src/starboard/contrib/tizen/armv7l/thread_types_public.h
similarity index 100%
rename from src/starboard/tizen/armv7l/thread_types_public.h
rename to src/starboard/contrib/tizen/armv7l/thread_types_public.h
diff --git a/src/starboard/tizen/packaging/README.md b/src/starboard/contrib/tizen/packaging/README.md
similarity index 70%
rename from src/starboard/tizen/packaging/README.md
rename to src/starboard/contrib/tizen/packaging/README.md
index dc3c3a9..0049ea3 100644
--- a/src/starboard/tizen/packaging/README.md
+++ b/src/starboard/contrib/tizen/packaging/README.md
@@ -1,7 +1,7 @@
 ## Introduction
 
 cobalt tizen is a Cobalt engine port to tizen platform. The port
-implements starboard/tizen platform APIs.
+implements starboard/contrib/tizen platform APIs.
 
 ## Precondition
 
@@ -17,10 +17,10 @@
 
 1. Build commands:
   - Need to add packaging-dir option
-    --packaging-dir src/starboard/tizen/packaging/
+    --packaging-dir src/starboard/contrib/tizen/packaging/
   - armv7l
     $ gbs -c packaging/gbs.conf -v build -A armv7l -P profile.cobalt [options]
     Detail option see "gbs --help" and "gbs -v build --help"
-    ex) gbs -c src/starboard/tizen/packaging/gbs.conf -v build -A armv7l -P profile.cobalt --packaging-dir src/starboard/tizen/packaging/
+    ex) gbs -c src/starboard/contrib/tizen/packaging/gbs.conf -v build -A armv7l -P profile.cobalt --packaging-dir src/starboard/contrib/tizen/packaging/
 
 cobalt tizen code uses the BSD license, see our `LICENSE` file.
diff --git a/src/starboard/tizen/packaging/com.youtube.cobalt.manifest b/src/starboard/contrib/tizen/packaging/com.youtube.cobalt.manifest
similarity index 100%
rename from src/starboard/tizen/packaging/com.youtube.cobalt.manifest
rename to src/starboard/contrib/tizen/packaging/com.youtube.cobalt.manifest
diff --git a/src/starboard/tizen/packaging/com.youtube.cobalt.spec b/src/starboard/contrib/tizen/packaging/com.youtube.cobalt.spec
similarity index 96%
rename from src/starboard/tizen/packaging/com.youtube.cobalt.spec
rename to src/starboard/contrib/tizen/packaging/com.youtube.cobalt.spec
index c42faff..1b4a636 100644
--- a/src/starboard/tizen/packaging/com.youtube.cobalt.spec
+++ b/src/starboard/contrib/tizen/packaging/com.youtube.cobalt.spec
@@ -150,7 +150,7 @@
 cp -rd %{_outdir}/content/dir_source_root %{buildroot}%{_contentdir}/
 %endif
 
-cp src/starboard/tizen/packaging/%{_pkgname}.xml %{buildroot}%{_manifestdir}
+cp src/starboard/contrib/tizen/packaging/%{_pkgname}.xml %{buildroot}%{_manifestdir}
 
 %post
 
@@ -160,7 +160,7 @@
 # rpm files
 #####################
 %files
-%manifest src/starboard/tizen/packaging/%{_pkgname}.manifest
+%manifest src/starboard/contrib/tizen/packaging/%{_pkgname}.manifest
 %defattr(-,root,root,-)
 %if "%{_name}" == "all"
 %{_bindir}/*
diff --git a/src/starboard/tizen/packaging/com.youtube.cobalt.xml b/src/starboard/contrib/tizen/packaging/com.youtube.cobalt.xml
similarity index 100%
rename from src/starboard/tizen/packaging/com.youtube.cobalt.xml
rename to src/starboard/contrib/tizen/packaging/com.youtube.cobalt.xml
diff --git a/src/starboard/tizen/packaging/gbs.conf b/src/starboard/contrib/tizen/packaging/gbs.conf
similarity index 100%
rename from src/starboard/tizen/packaging/gbs.conf
rename to src/starboard/contrib/tizen/packaging/gbs.conf
diff --git a/src/starboard/tizen/shared/atomic_public.h b/src/starboard/contrib/tizen/shared/atomic_public.h
similarity index 100%
rename from src/starboard/tizen/shared/atomic_public.h
rename to src/starboard/contrib/tizen/shared/atomic_public.h
diff --git a/src/starboard/tizen/shared/audio/audio_sink_adaptor.cc b/src/starboard/contrib/tizen/shared/audio/audio_sink_adaptor.cc
similarity index 97%
rename from src/starboard/tizen/shared/audio/audio_sink_adaptor.cc
rename to src/starboard/contrib/tizen/shared/audio/audio_sink_adaptor.cc
index 5e06b2f..6e26abe 100644
--- a/src/starboard/tizen/shared/audio/audio_sink_adaptor.cc
+++ b/src/starboard/contrib/tizen/shared/audio/audio_sink_adaptor.cc
@@ -14,7 +14,7 @@
 
 #include "starboard/audio_sink.h"
 #include "starboard/log.h"
-#include "starboard/tizen/shared/audio/audio_sink_private.h"
+#include "starboard/contrib/tizen/shared/audio/audio_sink_private.h"
 
 bool SbAudioSinkIsValid(SbAudioSink audio_sink) {
   if (audio_sink == kSbAudioSinkInvalid) {
diff --git a/src/starboard/tizen/shared/audio/audio_sink_private.cc b/src/starboard/contrib/tizen/shared/audio/audio_sink_private.cc
similarity index 98%
rename from src/starboard/tizen/shared/audio/audio_sink_private.cc
rename to src/starboard/contrib/tizen/shared/audio/audio_sink_private.cc
index 52c3b95..aac2939 100644
--- a/src/starboard/tizen/shared/audio/audio_sink_private.cc
+++ b/src/starboard/contrib/tizen/shared/audio/audio_sink_private.cc
@@ -15,7 +15,7 @@
 #include "starboard/audio_sink.h"
 #include "starboard/log.h"
 #include "starboard/mutex.h"
-#include "starboard/tizen/shared/audio/audio_sink_private.h"
+#include "starboard/contrib/tizen/shared/audio/audio_sink_private.h"
 
 #define CHECK_CAPI_AUDIO_ERROR(func)                            \
   if (capi_ret != AUDIO_IO_ERROR_NONE) {                        \
diff --git a/src/starboard/tizen/shared/audio/audio_sink_private.h b/src/starboard/contrib/tizen/shared/audio/audio_sink_private.h
similarity index 100%
rename from src/starboard/tizen/shared/audio/audio_sink_private.h
rename to src/starboard/contrib/tizen/shared/audio/audio_sink_private.h
diff --git a/src/starboard/tizen/shared/configuration_public.h b/src/starboard/contrib/tizen/shared/configuration_public.h
similarity index 100%
rename from src/starboard/tizen/shared/configuration_public.h
rename to src/starboard/contrib/tizen/shared/configuration_public.h
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
similarity index 98%
rename from src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
rename to src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
index ebe970b..7092f7b 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h"
+#include "starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h"
 
 #include "starboard/audio_sink.h"
 #include "starboard/log.h"
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
similarity index 88%
rename from src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
rename to src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
index 3271d88..42e20a7 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
-#define STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
+#ifndef STARBOARD_CONTRIB_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
+#define STARBOARD_CONTRIB_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
 
 #include <queue>
 
@@ -21,7 +21,7 @@
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/tizen/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.h"
 
 namespace starboard {
 namespace shared {
@@ -68,4 +68,4 @@
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
+#endif  // STARBOARD_CONTRIB_TIZEN_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.cc
similarity index 95%
rename from src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc
rename to src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.cc
index 7dc091c..cc7e83a 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc
+++ b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/tizen/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.h"
 
 #include "starboard/log.h"
 #include "starboard/mutex.h"
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.h
similarity index 100%
rename from src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h
rename to src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.h
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
similarity index 98%
rename from src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
rename to src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
index c898592..f0fe7be 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h"
+#include "starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.h"
 
 #include "starboard/memory.h"
 
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
similarity index 90%
rename from src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
rename to src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
index ea4e33b..5d257ac 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
+++ b/src/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
-#define STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
+#ifndef STARBOARD_CONTRIB_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
+#define STARBOARD_CONTRIB_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
 
 #include "starboard/log.h"
 #include "starboard/media.h"
@@ -23,7 +23,7 @@
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
 #include "starboard/shared/starboard/player/video_frame_internal.h"
 #include "starboard/thread.h"
-#include "starboard/tizen/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.h"
 
 namespace starboard {
 namespace shared {
@@ -95,4 +95,4 @@
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // STARBOARD_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
+#endif  // STARBOARD_CONTRIB_TIZEN_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_
diff --git a/src/starboard/tizen/shared/get_home_directory.cc b/src/starboard/contrib/tizen/shared/get_home_directory.cc
similarity index 100%
rename from src/starboard/tizen/shared/get_home_directory.cc
rename to src/starboard/contrib/tizen/shared/get_home_directory.cc
diff --git a/src/starboard/tizen/shared/gyp_configuration.gypi b/src/starboard/contrib/tizen/shared/gyp_configuration.gypi
similarity index 100%
rename from src/starboard/tizen/shared/gyp_configuration.gypi
rename to src/starboard/contrib/tizen/shared/gyp_configuration.gypi
diff --git a/src/starboard/tizen/shared/log/log.cc b/src/starboard/contrib/tizen/shared/log/log.cc
similarity index 100%
rename from src/starboard/tizen/shared/log/log.cc
rename to src/starboard/contrib/tizen/shared/log/log.cc
diff --git a/src/starboard/tizen/shared/main.cc b/src/starboard/contrib/tizen/shared/main.cc
similarity index 100%
rename from src/starboard/tizen/shared/main.cc
rename to src/starboard/contrib/tizen/shared/main.cc
diff --git a/src/starboard/tizen/shared/memory_flush.cc b/src/starboard/contrib/tizen/shared/memory_flush.cc
similarity index 100%
rename from src/starboard/tizen/shared/memory_flush.cc
rename to src/starboard/contrib/tizen/shared/memory_flush.cc
diff --git a/src/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc b/src/starboard/contrib/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
similarity index 93%
rename from src/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
rename to src/starboard/contrib/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
index 1081d37..84ddd6a 100644
--- a/src/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
+++ b/src/starboard/contrib/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
@@ -16,8 +16,8 @@
 
 #include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h"
 #include "starboard/shared/starboard/player/filter/video_renderer_impl_internal.h"
-#include "starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h"
-#include "starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h"
+#include "starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h"
+#include "starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.h"
 
 namespace starboard {
 namespace shared {
diff --git a/src/starboard/tizen/shared/system.gyp b/src/starboard/contrib/tizen/shared/system.gyp
similarity index 100%
rename from src/starboard/tizen/shared/system.gyp
rename to src/starboard/contrib/tizen/shared/system.gyp
diff --git a/src/starboard/tizen/shared/system_get_device_type.cc b/src/starboard/contrib/tizen/shared/system_get_device_type.cc
similarity index 100%
rename from src/starboard/tizen/shared/system_get_device_type.cc
rename to src/starboard/contrib/tizen/shared/system_get_device_type.cc
diff --git a/src/starboard/tizen/shared/system_get_path.cc b/src/starboard/contrib/tizen/shared/system_get_path.cc
similarity index 100%
rename from src/starboard/tizen/shared/system_get_path.cc
rename to src/starboard/contrib/tizen/shared/system_get_path.cc
diff --git a/src/starboard/tizen/shared/thread_create.cc b/src/starboard/contrib/tizen/shared/thread_create.cc
similarity index 100%
rename from src/starboard/tizen/shared/thread_create.cc
rename to src/starboard/contrib/tizen/shared/thread_create.cc
diff --git a/src/starboard/creator/ci20x11/atomic_public.h b/src/starboard/creator/ci20x11/atomic_public.h
deleted file mode 100644
index 932fe9d..0000000
--- a/src/starboard/creator/ci20x11/atomic_public.h
+++ /dev/null
@@ -1,20 +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 STARBOARD_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
-
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif  // STARBOARD_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/creator/ci20x11/configuration_public.h b/src/starboard/creator/ci20x11/configuration_public.h
deleted file mode 100644
index 4c00bad..0000000
--- a/src/starboard/creator/ci20x11/configuration_public.h
+++ /dev/null
@@ -1,20 +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 STARBOARD_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
-
-#include "starboard/creator/shared/configuration_public.h"
-
-#endif  // STARBOARD_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/event.h b/src/starboard/event.h
index cda0d09..a693ae9 100644
--- a/src/starboard/event.h
+++ b/src/starboard/event.h
@@ -203,7 +203,8 @@
 
   // The platform's accessibility settings have changed. The application should
   // query the accessibility settings using the appropriate APIs to get the
-  // new settings.
+  // new settings. Note this excludes captions settings changes, which
+  // causes kSbEventTypeAccessibilityCaptionSettingsChanged to fire.
   kSbEventTypeAccessiblitySettingsChanged,
 
 #if SB_API_VERSION >= 6
@@ -258,6 +259,11 @@
   kSbEventTypeOnScreenKeyboardBlurred,
 
 #endif  // SB_HAS(ON_SCREEN_KEYBOARD)
+#if SB_HAS(CAPTIONS)
+  // One or more of the fields returned by SbAccessibilityGetCaptionSettings
+  // has changed.
+  kSbEventTypeAccessibilityCaptionSettingsChanged,
+#endif  // SB_HAS(CAPTIONS)
 } SbEventType;
 
 // Structure representing a Starboard event and its data.
diff --git a/src/starboard/linux/x64x11/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
index d714153..1a4a493 100644
--- a/src/starboard/linux/x64x11/starboard_platform_tests.gyp
+++ b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
@@ -13,7 +13,7 @@
 # limitations under the License.
 {
   'includes': [
-    '<(DEPTH)/starboard/shared/starboard/player/filter/filter_tests.gypi',
+    '<(DEPTH)/starboard/shared/starboard/player/filter/testing/filter_tests.gypi',
   ],
   'targets': [
     {
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index e84264e..d48b409 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -23,6 +23,8 @@
       'type': '<(gtest_target_type)',
       'sources': [
         '<(DEPTH)/starboard/common/test_main.cc',
+        '<(DEPTH)/starboard/testing/fake_graphics_context_provider.cc',
+        '<(DEPTH)/starboard/testing/fake_graphics_context_provider.h',
         'accessibility_get_setting_test.cc',
         'align_test.cc',
         'atomic_test.cc',
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index c5a5410..971950d 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -15,6 +15,7 @@
 #include "starboard/blitter.h"
 #include "starboard/decode_target.h"
 #include "starboard/player.h"
+#include "starboard/testing/fake_graphics_context_provider.h"
 #include "starboard/window.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -24,28 +25,26 @@
 
 #if SB_HAS(PLAYER_WITH_URL)
 // This test does not apply. See player_create_with_url_test.cc instead.
-#else
+#else  // SB_HAS(PLAYER_WITH_URL)
 
-#if SB_HAS(GLES2)
-void GlesContextRunner(
-    SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
-    SbDecodeTargetGlesContextRunnerTarget target_function,
-    void* target_function_context) {
-  SB_UNREFERENCED_PARAMETER(graphics_context_provider);
+using ::starboard::testing::FakeGraphicsContextProvider;
 
-  // Just call the function directly in case the player implementation relies
-  // on this function call being made.
-  (*target_function)(target_function_context);
-}
-#endif  // SB_HAS(GLES2)
+class SbPlayerTest : public ::testing::Test {
+ protected:
+  void SetUp() {
+    SbWindowOptions window_options;
+    SbWindowSetDefaultOptions(&window_options);
 
-TEST(SbPlayerTest, SunnyDay) {
-  SbWindowOptions window_options;
-  SbWindowSetDefaultOptions(&window_options);
+    window_ = SbWindowCreate(&window_options);
+    EXPECT_TRUE(SbWindowIsValid(window_));
+  }
+  void TearDown() { SbWindowDestroy(window_); }
 
-  SbWindow window = SbWindowCreate(&window_options);
-  EXPECT_TRUE(SbWindowIsValid(window));
+  SbWindow window_;
+  FakeGraphicsContextProvider fake_graphics_context_provider_;
+};
 
+TEST_F(SbPlayerTest, SunnyDay) {
   SbMediaAudioHeader audio_header;
 
   audio_header.format_tag = 0xff;
@@ -54,9 +53,6 @@
   audio_header.block_alignment = 4;
   audio_header.bits_per_sample = 32;
   audio_header.audio_specific_config_size = 0;
-#if SB_API_VERSION >= 6
-  audio_header.audio_specific_config = NULL;
-#endif  // SB_API_VERSION >= 6
   audio_header.average_bytes_per_second = audio_header.samples_per_second *
                                           audio_header.number_of_channels *
                                           audio_header.bits_per_sample / 8;
@@ -73,22 +69,11 @@
       continue;
     }
 
-    SbDecodeTargetGraphicsContextProvider
-        decode_target_graphics_context_provider;
-#if SB_HAS(BLITTER)
-    decode_target_graphics_context_provider.device = kSbBlitterInvalidDevice;
-#elif SB_HAS(GLES2)
-    decode_target_graphics_context_provider.egl_display = NULL;
-    decode_target_graphics_context_provider.egl_context = NULL;
-    decode_target_graphics_context_provider.gles_context_runner =
-        &GlesContextRunner;
-    decode_target_graphics_context_provider.gles_context_runner_context = NULL;
-#endif  // SB_HAS(BLITTER)
-
     SbPlayer player = SbPlayerCreate(
-        window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
+        window_, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
         SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header, NULL, NULL,
-        NULL, NULL, output_mode, &decode_target_graphics_context_provider);
+        NULL, NULL, output_mode,
+        fake_graphics_context_provider_.decoder_target_provider());
     EXPECT_TRUE(SbPlayerIsValid(player));
 
     if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
@@ -97,10 +82,38 @@
 
     SbPlayerDestroy(player);
   }
-
-  SbWindowDestroy(window);
 }
 
+#if SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION
+TEST_F(SbPlayerTest, Audioless) {
+  SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
+  SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
+
+  SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
+                                       kSbPlayerOutputModePunchOut};
+
+  for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
+    SbPlayerOutputMode output_mode = output_modes[i];
+    if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
+      continue;
+    }
+
+    SbPlayer player = SbPlayerCreate(
+        window_, kSbMediaVideoCodecH264, kSbMediaAudioCodecNone,
+        SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, NULL, NULL, NULL, NULL,
+        NULL, output_mode,
+        fake_graphics_context_provider_.decoder_target_provider());
+    EXPECT_TRUE(SbPlayerIsValid(player));
+
+    if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
+      SbDecodeTarget current_frame = SbPlayerGetCurrentFrame(player);
+    }
+
+    SbPlayerDestroy(player);
+  }
+}
+#endif  // SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION
+
 #endif  // SB_HAS(PLAYER_WITH_URL)
 
 }  // namespace
diff --git a/src/starboard/nplb/socket_resolve_test.cc b/src/starboard/nplb/socket_resolve_test.cc
index ebca117..3c2b9bd 100644
--- a/src/starboard/nplb/socket_resolve_test.cc
+++ b/src/starboard/nplb/socket_resolve_test.cc
@@ -37,7 +37,8 @@
 const void* kNull = NULL;
 
 // A random host name to use to test DNS resolution.
-const char* kTestHostName = "www.yahoo.com";
+const char kTestHostName[] = "www.yahoo.com";
+const char kLocalhost[] = "localhost";
 
 #define LOG_ADDRESS(i) "In returned address #" << (i)
 
@@ -57,6 +58,24 @@
   SbSocketFreeResolution(resolution);
 }
 
+TEST_P(SbSocketResolveTest, Localhost) {
+  SbSocketResolution* resolution =
+      SbSocketResolve(kLocalhost, GetResolveFilter());
+  ASSERT_NE(kNull, resolution);
+  EXPECT_LT(0, resolution->address_count);
+  EXPECT_NE(kNull, resolution->addresses);
+  for (int i = 0; i < resolution->address_count; ++i) {
+    const SbSocketAddress& address = resolution->addresses[i];
+    EXPECT_TRUE(address.type == kSbSocketAddressTypeIpv4 ||
+                address.type == kSbSocketAddressTypeIpv6)
+        << LOG_ADDRESS(i);
+    EXPECT_TRUE(IsLocalhost(&address)) << LOG_ADDRESS(i);
+    EXPECT_FALSE(IsUnspecified(&address)) << LOG_ADDRESS(i);
+  }
+
+  SbSocketFreeResolution(resolution);
+}
+
 TEST_P(SbSocketResolveTest, SunnyDayFiltered) {
   SbSocketResolution* resolution =
       SbSocketResolve(kTestHostName, GetResolveFilter());
diff --git a/src/starboard/player.h b/src/starboard/player.h
index 95aa665..6de176c 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -301,6 +301,10 @@
 // |audio_codec|: The audio codec used for the player. The value should never
 //   be |kSbMediaAudioCodecNone|. In addition, the caller must provide a
 //   populated |audio_header| if the audio codec is |kSbMediaAudioCodecAac|.
+#if SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION
+//   This can be set to |kSbMediaAudioCodecNone| to play a video without any
+//   audio track.  In such case |audio_header| must be NULL.
+#endif  // SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION
 //
 // |duration_pts|: The expected media duration in 90KHz ticks (PTS). It may be
 //   set to |SB_PLAYER_NO_DURATION| for live streams.
@@ -318,6 +322,9 @@
 //   is no longer valid after this function returns.  The implementation has to
 //   make a copy of the content if it is needed after the function returns.
 #endif  // SB_API_VERSION >= 6
+#if SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION
+//   When |audio_codec| is |kSbMediaAudioCodecNone|, this must be set to NULL.
+#endif  // SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION
 //
 // |sample_deallocator_func|: If not |NULL|, the player calls this function
 //   on an internal thread to free the sample buffers passed into
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index aba2888..f6c72be 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -59,9 +59,10 @@
         clang.GetClangSpecification())
     raspi_home = self._GetRasPiHome()
 
-    toolchain = os.path.realpath(os.path.join(
-        raspi_home,
-        'tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64'))
+    toolchain = os.path.realpath(
+        os.path.join(
+            raspi_home,
+            'tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64'))
     toolchain_bin_dir = os.path.join(toolchain, 'bin')
     env_variables.update({
         'CC': os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-gcc'),
@@ -69,6 +70,10 @@
     })
     return env_variables
 
+  def GetLauncherPath(self):
+    """Gets the path to the launcher module for this platform."""
+    return os.path.dirname(__file__)
+
   def GetTestFilters(self):
     filters = super(RaspiPlatformConfig, self).GetTestFilters()
     filters.extend([
@@ -76,14 +81,13 @@
         # disable the related tests.
         test_filter.TestFilter(
             'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.'
-                    'SunnyDayDestination/1'),
+            'SunnyDayDestination/1'),
         test_filter.TestFilter(
             'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.'
-                    'SunnyDaySourceForDestination/1'),
+            'SunnyDaySourceForDestination/1'),
         test_filter.TestFilter(
             'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.'
-                    'SunnyDaySourceNotLoopback/1'),
-
+            'SunnyDaySourceNotLoopback/1'),
         test_filter.TestFilter('starboard_platform_tests',
                                test_filter.FILTER_ALL),
         test_filter.TestFilter('nplb_blitter_pixel_tests',
diff --git a/src/starboard/raspi/shared/launcher.py b/src/starboard/raspi/shared/launcher.py
new file mode 100644
index 0000000..ebf20ed
--- /dev/null
+++ b/src/starboard/raspi/shared/launcher.py
@@ -0,0 +1,248 @@
+#
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Raspi implementation of Starboard launcher abstraction."""
+
+import functools
+import logging
+import os
+import re
+import signal
+import sys
+import threading
+import time
+
+import _env  # pylint: disable=unused-import
+import pexpect
+from starboard.tools import abstract_launcher
+
+
+# pylint: disable=unused-argument
+def _SigIntOrSigTermHandler(signum, frame):
+  """Clean up and exit with status |signum|.
+
+  Args:
+    signum: Signal number that triggered this callback.  Passed in when the
+      signal handler is called by python runtime.
+    frame: Current stack frame.  Passed in when the signal handler is called
+      by python runtime.
+  """
+  sys.exit(signum)
+
+
+class Launcher(abstract_launcher.AbstractLauncher):
+  """Class for launching Cobalt/tools on Raspi."""
+
+  _STARTUP_TIMEOUT_SECONDS = 900
+
+  _RASPI_USERNAME = 'pi'
+  _RASPI_PASSWORD = 'raspberry'
+
+  # pexpect times out each second to allow Kill to quickly stop a test run
+  _PEXPECT_TIMEOUT = 1
+
+  # Wait up to 30 seconds for the password prompt from the raspi
+  _PEXPECT_PASSWORD_TIMEOUT_MAX_RETRIES = 30
+  # Wait up to 900 seconds for new output from the raspi
+  _PEXPECT_READLINE_TIMEOUT_MAX_RETRIES = 900
+
+  # This is used to strip ansi color codes from pexpect output.
+  _PEXPECT_SANITIZE_LINE_RE = re.compile(r'\x1b[^m]*m')
+
+  def __init__(self, platform, target_name, config, device_id, **kwargs):
+    super(Launcher, self).__init__(platform, target_name, config, device_id,
+                                   **kwargs)
+    env = os.environ.copy()
+    env.update(self.env_variables)
+    self.full_env = env
+
+    if not self.device_id:
+      self.device_id = self.full_env.get('RASPI_ADDR')
+      if not self.device_id:
+        raise ValueError(
+            'Unable to determine target, please pass it in, or set RASPI_ADDR '
+            'environment variable.')
+
+    self.startup_timeout_seconds = Launcher._STARTUP_TIMEOUT_SECONDS
+
+    self.pexpect_process = None
+    self._InitPexpectCommands()
+
+    self.run_inactive = threading.Event()
+    self.run_inactive.set()
+
+    self.shutdown_initiated = threading.Event()
+
+    signal.signal(signal.SIGINT, functools.partial(_SigIntOrSigTermHandler))
+    signal.signal(signal.SIGTERM, functools.partial(_SigIntOrSigTermHandler))
+
+  def _InitPexpectCommands(self):
+    """Initializes all of the pexpect commands needed for running the test."""
+
+    test_path = self.GetTargetPath()
+    if not os.path.isfile(test_path):
+      raise ValueError('TargetPath ({}) must be a file.'.format(test_path))
+
+    test_dir_path, test_file = os.path.split(test_path)
+    test_base_dir = os.path.basename(os.path.normpath(test_dir_path))
+
+    raspi_user_hostname = Launcher._RASPI_USERNAME + '@' + self.device_id
+    raspi_test_path = os.path.join(test_base_dir, test_file)
+
+    # rsync command setup
+    options = '-avzh --exclude obj*'
+    source = test_dir_path
+    destination = raspi_user_hostname + ':~/'
+    self.rsync_command = 'rsync ' + options + ' ' + source + ' ' + destination
+
+    # ssh command setup
+    self.ssh_command = 'ssh ' + raspi_user_hostname
+
+    # escape command line metacharacters in the flags
+    flags = ' '.join(self.target_command_line_params)
+    meta_chars = '()[]{}%!^"<>&|'
+    meta_re = re.compile('(' + '|'.join(
+        re.escape(char) for char in list(meta_chars)) + ')')
+    escaped_flags = re.subn(meta_re, r'\\\1', flags)[0]
+
+    # test output tags
+    self.test_complete_tag = 'TEST-{time}'.format(time=time.time())
+    self.test_success_tag = 'succeeded'
+    self.test_failure_tag = 'failed'
+
+    # test command setup
+    test_base_command = raspi_test_path + ' ' + escaped_flags
+    test_success_output = ' && echo {} {}'.format(self.test_complete_tag,
+                                                  self.test_success_tag)
+    test_failure_output = ' || echo {} {}'.format(self.test_complete_tag,
+                                                  self.test_failure_tag)
+    self.test_command = '{} {} {}'.format(
+        test_base_command, test_success_output, test_failure_output)
+
+  def _PexpectSpawnAndConnect(self, command):
+    """Spawns a process with pexpect and connect to the raspi.
+
+    Args:
+       command: The command to use when spawning the pexpect process.
+    """
+
+    self.pexpect_process = pexpect.spawn(
+        command, timeout=Launcher._PEXPECT_TIMEOUT)
+    retry_count = 0
+    while True:
+      try:
+        self.pexpect_process.expect(r'\S+ password:')
+        break
+      except pexpect.TIMEOUT:
+        if self.shutdown_initiated.is_set():
+          return
+        retry_count += 1
+        # Check if the max retry count has been exceeded. If it has, then
+        # re-raise the timeout exception.
+        if retry_count > Launcher._PEXPECT_PASSWORD_TIMEOUT_MAX_RETRIES:
+          exc_info = sys.exc_info()
+          raise exc_info[0], exc_info[1], exc_info[2]
+    self.pexpect_process.sendline(Launcher._RASPI_PASSWORD)
+
+  def _PexpectReadLines(self):
+    """Reads all lines from the pexpect process."""
+
+    retry_count = 0
+    while True:
+      try:
+        # Sanitize the line to remove ansi color codes.
+        line = Launcher._PEXPECT_SANITIZE_LINE_RE.sub(
+            '', self.pexpect_process.readline())
+        if not line:
+          break
+        self.output_file.write(line)
+        self.output_file.flush()
+        # Check for the test complete tag. It will be followed by either a
+        # success or failure tag.
+        if line.startswith(self.test_complete_tag):
+          if line.find(self.test_success_tag) != -1:
+            self.return_value = 0
+          break
+        # A line was successfully read without timing out; reset the retry
+        # count before attempting to read the next line.
+        retry_count = 0
+      except pexpect.TIMEOUT:
+        if self.shutdown_initiated.is_set():
+          return
+        retry_count += 1
+        # Check if the max retry count has been exceeded. If it has, then
+        # re-raise the timeout exception.
+        if retry_count > Launcher._PEXPECT_READLINE_TIMEOUT_MAX_RETRIES:
+          exc_info = sys.exc_info()
+          raise exc_info[0], exc_info[1], exc_info[2]
+
+  def _CleanupPexpectProcess(self):
+    """Closes current pexpect process."""
+
+    if self.pexpect_process is not None and self.pexpect_process.isalive():
+      # Send ctrl-c to the raspi and close the process.
+      self.pexpect_process.sendline(chr(3))
+      self.pexpect_process.close()
+
+  def Run(self):
+    """Runs launcher's executable on the target raspi.
+
+    Returns:
+       Whether or not the run finished successfully.
+    """
+
+    self.return_value = 1
+
+    try:
+      # Notify other threads that the run is now active
+      self.run_inactive.clear()
+
+      # rsync the test files to the raspi
+      if not self.shutdown_initiated.is_set():
+        self._PexpectSpawnAndConnect(self.rsync_command)
+      if not self.shutdown_initiated.is_set():
+        self._PexpectReadLines()
+
+      # ssh into the raspi and run the test
+      if not self.shutdown_initiated.is_set():
+        self._PexpectSpawnAndConnect(self.ssh_command)
+      if not self.shutdown_initiated.is_set():
+        self.pexpect_process.sendline(self.test_command)
+        self._PexpectReadLines()
+
+    except pexpect.EOF:
+      logging.exception('pexpect encountered EOF while reading line.')
+    except pexpect.TIMEOUT:
+      logging.exception('pexpect timed out while reading line.')
+    # pylint: disable=W0703
+    except Exception:
+      logging.exception('Error occured while running test.')
+    finally:
+      self._CleanupPexpectProcess()
+
+      # Notify other threads that the run is no longer active
+      self.run_inactive.set()
+
+    return self.return_value
+
+  def Kill(self):
+    """Stops the run so that the launcher can be killed."""
+
+    sys.stderr.write('\n***Killing Launcher***\n')
+    if self.run_inactive.is_set():
+      return
+    # Initiate the shutdown. This causes the run to abort within one second.
+    self.shutdown_initiated.set()
+    # Wait up to three seconds for the run to be set to inactive.
+    self.run_inactive.wait(3)
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
index e04395e..3def412 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -42,7 +42,6 @@
 #if LIBAVUTIL_VERSION_MAJOR > 52
 
 void ReleaseBuffer(void* opaque, uint8_t* data) {
-  SbMemorySet(data, 0, sizeof(data));
   SbMemoryDeallocate(data);
 }
 
diff --git a/src/starboard/shared/posix/memory_flush.cc b/src/starboard/shared/posix/memory_flush.cc
index 202b081..61dadd9 100644
--- a/src/starboard/shared/posix/memory_flush.cc
+++ b/src/starboard/shared/posix/memory_flush.cc
@@ -18,10 +18,6 @@
 
 #include <iomanip>
 
-#if SB_IS(ARCH_MIPS)
-#include <sys/cachectl.h>
-#endif
-
 #include "starboard/log.h"
 
 #if !SB_CAN(MAP_EXECUTABLE_MEMORY)
@@ -37,11 +33,6 @@
                          << std::dec << result << "d)";
 #endif
 
-#if SB_IS(ARCH_MIPS)
-  _flush_cache(reinterpret_cast<char*>(memory), (size_t)size_bytes, BCACHE);
-  return;
-#endif
-
 #if !defined(__has_builtin)
 #define __has_builtin(a) (0)
 #endif
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 87ee74b..b2a5220 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -140,8 +140,11 @@
 
   ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
 
-  video_renderer_ = player_components->CreateVideoRenderer(
-      video_parameters, GetMediaTimeProvider());
+  auto media_time_provider = GetMediaTimeProvider();
+  SB_DCHECK(media_time_provider);
+
+  video_renderer_ = player_components->CreateVideoRenderer(video_parameters,
+                                                           media_time_provider);
   video_renderer_->Initialize(
       std::bind(&FilterBasedPlayerWorkerHandler::OnError, this));
 
@@ -418,7 +421,6 @@
   if (audio_renderer_) {
     return audio_renderer_.get();
   }
-  SB_DCHECK(media_time_provider_impl_);
   return media_time_provider_impl_.get();
 }
 
diff --git a/src/starboard/shared/starboard/player/filter/audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
similarity index 97%
rename from src/starboard/shared/starboard/player/filter/audio_decoder_test.cc
rename to src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
index 1f854cb..6bf38cf 100644
--- a/src/starboard/shared/starboard/player/filter/audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
@@ -167,7 +167,7 @@
     --number_of_inputs_to_write;
 
     while (number_of_inputs_to_write > 0) {
-      Event event;
+      Event event = kError;
       ASSERT_NO_FATAL_FAILURE(WaitForNextEvent(&event));
       if (event == kConsumed) {
         ASSERT_NO_FATAL_FAILURE(WriteSingleInput(start_index));
@@ -189,7 +189,7 @@
     }
 
     for (;;) {
-      Event event;
+      Event event = kError;
       ASSERT_NO_FATAL_FAILURE(WaitForNextEvent(&event));
       if (event == kError) {
         if (error_occurred) {
@@ -245,12 +245,13 @@
   last_input_buffer_ = dmp_reader_.GetAudioInputBuffer(0);
   std::vector<uint8_t> content(last_input_buffer_->size(), 0xab);
   // Replace the content with invalid data.
-  last_input_buffer_->SetDecryptedContent(content.data(), content.size());
+  last_input_buffer_->SetDecryptedContent(content.data(),
+                                          static_cast<int>(content.size()));
   audio_decoder_->Decode(last_input_buffer_, consumed_cb());
 
   audio_decoder_->WriteEndOfStream();
 
-  bool error_occurred;
+  bool error_occurred = false;
   ASSERT_NO_FATAL_FAILURE(DrainOutputs(&error_occurred));
   // The decoder is allowed to either signal an error or return dummy audio
   // data but not both.
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
similarity index 98%
rename from src/starboard/shared/starboard/player/filter/audio_renderer_internal_test.cc
rename to src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
index 30cdab6..babf1f1 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -233,6 +233,7 @@
   static void DeallocateSampleCB(SbPlayer player,
                                  void* context,
                                  const void* sample_buffer) {
+    SB_UNREFERENCED_PARAMETER(player);
     AudioRendererTest* test = static_cast<AudioRendererTest*>(context);
     EXPECT_TRUE(test != NULL);
     test->OnDeallocateSample(sample_buffer);
@@ -264,7 +265,7 @@
 
   Seek(0);
 
-  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  FillRendererWithDecodedAudioAndWriteEOS();
 
   bool is_playing = true;
   bool is_eos_played = true;
@@ -342,7 +343,7 @@
 
   Seek(0);
 
-  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  FillRendererWithDecodedAudioAndWriteEOS();
   bool is_playing = false;
   bool is_eos_played = true;
 
@@ -406,7 +407,7 @@
 
   audio_renderer_->Play();
 
-  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  FillRendererWithDecodedAudioAndWriteEOS();
 
   SendDecoderOutput(new DecodedAudio);
 
diff --git a/src/starboard/shared/starboard/player/filter/filter_tests.gypi b/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi
similarity index 74%
rename from src/starboard/shared/starboard/player/filter/filter_tests.gypi
rename to src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi
index 5babcbf..c635d56 100644
--- a/src/starboard/shared/starboard/player/filter/filter_tests.gypi
+++ b/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi
@@ -19,16 +19,16 @@
       'type': '<(gtest_target_type)',
       'sources': [
         '<(DEPTH)/starboard/common/test_main.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_test.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal_test.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/fake_graphics_context_provider.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/fake_graphics_context_provider.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl_test.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_test.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc',
         '<(DEPTH)/starboard/shared/starboard/player/video_dmp_common.cc',
         '<(DEPTH)/starboard/shared/starboard/player/video_dmp_common.h',
         '<(DEPTH)/starboard/shared/starboard/player/video_dmp_reader.cc',
         '<(DEPTH)/starboard/shared/starboard/player/video_dmp_reader.h',
+        '<(DEPTH)/starboard/testing/fake_graphics_context_provider.cc',
+        '<(DEPTH)/starboard/testing/fake_graphics_context_provider.h',
       ],
       'defines': [
         # This allows the tests to include internal only header files.
@@ -49,7 +49,7 @@
             ],
             'output_dir': '/starboard/shared/starboard/player',
           },
-          'includes': ['../../../../build/copy_test_data.gypi'],
+          'includes': ['../../../../../build/copy_test_data.gypi'],
         }
       ],
     },
@@ -62,7 +62,7 @@
       'variables': {
         'executable_name': 'filter_tests',
       },
-      'includes': [ '../../../../build/deploy.gypi' ],
+      'includes': [ '../../../../../build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/shared/starboard/player/filter/media_time_provider_impl_test.cc b/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
similarity index 100%
rename from src/starboard/shared/starboard/player/filter/media_time_provider_impl_test.cc
rename to src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
diff --git a/src/starboard/shared/starboard/player/filter/video_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
similarity index 97%
rename from src/starboard/shared/starboard/player/filter/video_decoder_test.cc
rename to src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
index 3e4d819..e7e452f 100644
--- a/src/starboard/shared/starboard/player/filter/video_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
@@ -27,11 +27,11 @@
 #include "starboard/mutex.h"
 #include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/media/media_util.h"
-#include "starboard/shared/starboard/player/filter/fake_graphics_context_provider.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
 #include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/string.h"
+#include "starboard/testing/fake_graphics_context_provider.h"
 #include "starboard/thread.h"
 #include "starboard/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -48,6 +48,7 @@
 namespace testing {
 namespace {
 
+using ::starboard::testing::FakeGraphicsContextProvider;
 using ::std::placeholders::_1;
 using ::std::placeholders::_2;
 using ::testing::ValuesIn;
@@ -374,7 +375,7 @@
   WriteSingleInput(0);
   WriteEndOfStream();
 
-  bool error_occurred;
+  bool error_occurred = false;
   ASSERT_NO_FATAL_FAILURE(DrainOutputs(
       &error_occurred, [=](const Event& event, bool* continue_process) {
         if (event.frame) {
@@ -391,12 +392,13 @@
   outstanding_inputs_.insert(input_buffer->pts());
   std::vector<uint8_t> content(input_buffer->size(), 0xab);
   // Replace the content with invalid data.
-  input_buffer->SetDecryptedContent(content.data(), content.size());
+  input_buffer->SetDecryptedContent(content.data(),
+                                    static_cast<int>(content.size()));
   video_decoder_->WriteInputBuffer(input_buffer);
 
   WriteEndOfStream();
 
-  bool error_occurred;
+  bool error_occurred = true;
   ASSERT_NO_FATAL_FAILURE(DrainOutputs(&error_occurred));
   if (error_occurred) {
     ASSERT_TRUE(decoded_frames_.empty());
@@ -442,6 +444,7 @@
   ASSERT_NO_FATAL_FAILURE(WriteMultipleInputs(
       0, dmp_reader_.number_of_video_buffers(),
       [&](const Event& event, bool* continue_process) {
+        SB_UNREFERENCED_PARAMETER(event);
         frames_decoded += decoded_frames_.size();
         decoded_frames_.clear();
         *continue_process = frames_decoded < number_of_expected_decoded_frames;
@@ -458,6 +461,7 @@
   ASSERT_NO_FATAL_FAILURE(WriteMultipleInputs(
       0, dmp_reader_.number_of_video_buffers(),
       [=](const Event& event, bool* continue_process) {
+        SB_UNREFERENCED_PARAMETER(event);
         if (decoded_frames_.size() >= video_decoder_->GetPrerollFrameCount()) {
           *continue_process = false;
           return;
@@ -477,12 +481,14 @@
   ASSERT_NO_FATAL_FAILURE(WriteMultipleInputs(
       0, dmp_reader_.number_of_video_buffers(),
       [=](const Event& event, bool* continue_process) {
+        SB_UNREFERENCED_PARAMETER(event);
         *continue_process = decoded_frames_.size() < kMaxCachedFrames;
       }));
   WriteEndOfStream();
   bool error_occurred = false;
   ASSERT_NO_FATAL_FAILURE(DrainOutputs(
       &error_occurred, [=](const Event& event, bool* continue_process) {
+        SB_UNREFERENCED_PARAMETER(event);
         *continue_process = decoded_frames_.size() < kMaxCachedFrames;
       }));
   ASSERT_FALSE(error_occurred);
@@ -501,6 +507,7 @@
 
   ASSERT_NO_FATAL_FAILURE(WriteMultipleInputs(
       0, gop_size, [=](const Event& event, bool* continue_process) {
+        SB_UNREFERENCED_PARAMETER(event);
         while (decoded_frames_.size() >
                video_decoder_->GetPrerollFrameCount()) {
           decoded_frames_.pop_front();
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
index 5186d7e..8d4fd20 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
@@ -105,8 +105,8 @@
     return;
   }
   end_of_stream_written_ = true;
-  decoder_->WriteEndOfStream();
   first_input_written_ = true;
+  decoder_->WriteEndOfStream();
 }
 
 void VideoRenderer::Seek(SbMediaTime seek_to_pts) {
diff --git a/src/starboard/shared/starboard/player/video_dmp_common.cc b/src/starboard/shared/starboard/player/video_dmp_common.cc
index 4f7d166..c729bd1 100644
--- a/src/starboard/shared/starboard/player/video_dmp_common.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_common.cc
@@ -28,8 +28,8 @@
     int32_t int32_value;                                                   \
     Read(read_cb, &int32_value, sizeof(int32_value));                      \
     if (reverse_byte_order) {                                              \
-      std::reverse(reinterpret_cast<uint8_t*>(int32_value),                \
-                   reinterpret_cast<uint8_t*>(int32_value + 1));           \
+      std::reverse(reinterpret_cast<uint8_t*>(&int32_value),               \
+                   reinterpret_cast<uint8_t*>(&int32_value + 1));          \
     }                                                                      \
     *value = static_cast<Type>(int32_value);                               \
   }
@@ -40,8 +40,8 @@
     uint32_t uint32_value;                                                 \
     Read(read_cb, &uint32_value, sizeof(uint32_value));                    \
     if (reverse_byte_order) {                                              \
-      std::reverse(reinterpret_cast<uint8_t*>(uint32_value),               \
-                   reinterpret_cast<uint8_t*>(uint32_value + 1));          \
+      std::reverse(reinterpret_cast<uint8_t*>(&uint32_value),              \
+                   reinterpret_cast<uint8_t*>(&uint32_value + 1));         \
     }                                                                      \
     *value = static_cast<Type>(uint32_value);                              \
   }
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.cc b/src/starboard/shared/starboard/player/video_dmp_reader.cc
index 98c8c91..882c6e1 100644
--- a/src/starboard/shared/starboard/player/video_dmp_reader.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_reader.cc
@@ -50,7 +50,11 @@
 
 static void DeallocateSampleFunc(SbPlayer player,
                                  void* context,
-                                 const void* sample_buffer) {}
+                                 const void* sample_buffer) {
+  SB_UNREFERENCED_PARAMETER(player);
+  SB_UNREFERENCED_PARAMETER(context);
+  SB_UNREFERENCED_PARAMETER(sample_buffer);
+}
 
 }  // namespace
 
@@ -76,8 +80,8 @@
   SB_DCHECK(index < audio_access_units_.size());
   const AudioAccessUnit& au = audio_access_units_[index];
   return new InputBuffer(kSbMediaTypeAudio, DeallocateSampleFunc, NULL, NULL,
-                         au.data().data(), au.data().size(), au.timestamp(),
-                         NULL, NULL);
+                         au.data().data(), static_cast<int>(au.data().size()),
+                         au.timestamp(), NULL, NULL);
 }
 
 scoped_refptr<InputBuffer> VideoDmpReader::GetVideoInputBuffer(
@@ -85,8 +89,8 @@
   SB_DCHECK(index < video_access_units_.size());
   const VideoAccessUnit& au = video_access_units_[index];
   return new InputBuffer(kSbMediaTypeVideo, DeallocateSampleFunc, NULL, NULL,
-                         au.data().data(), au.data().size(), au.timestamp(),
-                         &au.video_sample_info(), NULL);
+                         au.data().data(), static_cast<int>(au.data().size()),
+                         au.timestamp(), &au.video_sample_info(), NULL);
 }
 
 void VideoDmpReader::Parse() {
@@ -110,8 +114,8 @@
       break;
     }
     if (reverse_byte_order_) {
-      std::reverse(reinterpret_cast<uint8_t*>(type),
-                   reinterpret_cast<uint8_t*>(type + 1));
+      std::reverse(reinterpret_cast<uint8_t*>(&type),
+                   reinterpret_cast<uint8_t*>(&type + 1));
     }
     switch (type) {
       case kRecordTypeAudioConfig:
diff --git a/src/starboard/shared/uwp/sso.cc b/src/starboard/shared/uwp/sso.cc
index 7c5c67f..1378c78 100644
--- a/src/starboard/shared/uwp/sso.cc
+++ b/src/starboard/shared/uwp/sso.cc
@@ -56,7 +56,20 @@
 namespace uwp {
 
 concurrency::task<WebTokenRequestResult^> TryToFetchSsoToken(
+    const std::string& url, bool prompt);
+
+concurrency::task<WebTokenRequestResult^> TryToFetchSsoToken(
     const std::string& url) {
+  return TryToFetchSsoToken(url, false);
+}
+
+concurrency::task<WebTokenRequestResult^> TryToFetchSsoTokenAndPrompt(
+    const std::string& url) {
+  return TryToFetchSsoToken(url, true);
+}
+
+concurrency::task<WebTokenRequestResult^> TryToFetchSsoToken(
+    const std::string& url, bool prompt) {
   if (SbFileExists(GetSsoRejectionFilePath().c_str())) {
     concurrency::task_completion_event<WebTokenRequestResult^> result;
     result.set(nullptr);
@@ -65,7 +78,7 @@
   return concurrency::create_task(
       WebAuthenticationCoreManager::FindAccountProviderAsync(
           sbwin32::stringToPlatformString(kXboxLiveAccountProviderId)))
-  .then([url](concurrency::task<WebAccountProvider^> previous_task) {
+  .then([url, prompt](concurrency::task<WebAccountProvider^> previous_task) {
     WebAccountProvider^ xbox_provider = nullptr;
     try {
       xbox_provider = previous_task.get();
@@ -83,7 +96,7 @@
 
     bool main_thread = sbuwp::GetDispatcher()->HasThreadAccess;
 
-    if (main_thread) {
+    if (main_thread && prompt) {
       return concurrency::create_task(
           WebAuthenticationCoreManager::RequestTokenAsync(request));
     } else {
@@ -118,10 +131,10 @@
       case WebTokenRequestStatus::UserInteractionRequired: {
         concurrency::task_completion_event<WebTokenRequestResult^> completion;
         RunInMainThreadAsync([url, completion]() {
-          // When we run TryToFetchSsoToken in the main thread,
+          // When we run TryToFetchSsoTokenAndPrompt in the main thread,
           // we'll always ask for user input via RequestTokenAsync, which
           // never returns this case.
-          TryToFetchSsoToken(url)
+          TryToFetchSsoTokenAndPrompt(url)
           .then([completion](concurrency::task<WebTokenRequestResult^> result) {
             try {
               completion.set(result.get());
diff --git a/src/starboard/shared/win32/audio_decoder.cc b/src/starboard/shared/win32/audio_decoder.cc
index b9440e8..d851857 100644
--- a/src/starboard/shared/win32/audio_decoder.cc
+++ b/src/starboard/shared/win32/audio_decoder.cc
@@ -64,11 +64,6 @@
       sample_type_(kSbMediaAudioSampleTypeFloat32),
       stream_ended_(false) {
   SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
-  decoder_impl_ = AbstractWin32AudioDecoder::Create(
-      audio_codec_, GetStorageType(), GetSampleType(), audio_header_,
-      drm_system_);
-  decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
-  callback_scheduler_.reset(new CallbackScheduler());
 }
 
 AudioDecoder::~AudioDecoder() {
@@ -87,6 +82,11 @@
   SB_DCHECK(output_cb);
   SB_DCHECK(!output_cb_);
   output_cb_ = output_cb;
+  decoder_impl_ = AbstractWin32AudioDecoder::Create(
+      audio_codec_, GetStorageType(), GetSampleType(), audio_header_,
+      drm_system_);
+  decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
+  callback_scheduler_.reset(new CallbackScheduler());
 }
 
 void AudioDecoder::Decode(const scoped_refptr<InputBuffer>& input_buffer,
diff --git a/src/starboard/shared/win32/decrypting_decoder.cc b/src/starboard/shared/win32/decrypting_decoder.cc
index b713f25..c02670e 100644
--- a/src/starboard/shared/win32/decrypting_decoder.cc
+++ b/src/starboard/shared/win32/decrypting_decoder.cc
@@ -203,7 +203,11 @@
           drm_system_->GetLicense(key_id, key_id_size);
       if (license && license->usable()) {
         decryptor_.reset(new MediaTransform(license->decryptor()));
-        ActivateDecryptor();
+        bool success = ActivateDecryptor();
+        if (!success) {
+          decryptor_.reset();
+          return false;
+        }
       }
     }
     if (!decryptor_) {
@@ -257,7 +261,7 @@
   }
 }
 
-void DecryptingDecoder::ActivateDecryptor() {
+bool DecryptingDecoder::ActivateDecryptor() {
   SB_DCHECK(decryptor_);
 
   ComPtr<IMFMediaType> decoder_output_type = decoder_->GetCurrentOutputType();
@@ -290,7 +294,7 @@
       std::min(decoder_protection_version, decryption_protection_version);
   if (protection_version < SAMPLE_PROTECTION_VERSION_RC4) {
     SB_NOTREACHED();
-    return;
+    return true;
   }
 
   BYTE* cert_data = NULL;
@@ -305,6 +309,11 @@
   hr = decryption_sample_protection->InitOutputProtection(
       protection_version, 0, cert_data, cert_data_size, &crypt_seed,
       &crypt_seed_size);
+  if (FAILED(hr)) {
+    // This can happen if we call InitOutputProtection while procesing
+    // a UWP resume event or shortly after.
+    return false;
+  }
   CheckResult(hr);
 
   hr = decoder_sample_protection->InitInputProtection(
@@ -331,9 +340,10 @@
     output_type->GetGUID(MF_MT_SUBTYPE, &sub_type);
     if (IsEqualGUID(sub_type, original_sub_type)) {
       decoder_->SetOutputType(output_type);
-      return;
+      return true;
     }
   }
+  return true;
 }
 
 void DecryptingDecoder::Reset() {
diff --git a/src/starboard/shared/win32/decrypting_decoder.h b/src/starboard/shared/win32/decrypting_decoder.h
index c12bda4..225d612 100644
--- a/src/starboard/shared/win32/decrypting_decoder.h
+++ b/src/starboard/shared/win32/decrypting_decoder.h
@@ -63,7 +63,7 @@
   void Reset();
 
  private:
-  void ActivateDecryptor();
+  bool ActivateDecryptor();
 
   // TODO: Clarify the thread pattern of this class.
   const std::string type_;  // For debugging purpose.
diff --git a/src/starboard/shared/win32/video_decoder.cc b/src/starboard/shared/win32/video_decoder.cc
index 5b531ec..f3136b2 100644
--- a/src/starboard/shared/win32/video_decoder.cc
+++ b/src/starboard/shared/win32/video_decoder.cc
@@ -169,8 +169,6 @@
   ComPtr<ID3D11DeviceContext> d3d_context;
   d3d_device_->GetImmediateContext(d3d_context.GetAddressOf());
   CheckResult(d3d_context.As(&video_context_));
-
-  InitializeCodec();
 }
 
 VideoDecoder::~VideoDecoder() {
@@ -199,6 +197,7 @@
   SB_DCHECK(error_cb);
   decoder_status_cb_ = decoder_status_cb;
   error_cb_ = error_cb;
+  InitializeCodec();
 }
 
 void VideoDecoder::WriteInputBuffer(
diff --git a/src/starboard/shared/starboard/player/filter/fake_graphics_context_provider.cc b/src/starboard/testing/fake_graphics_context_provider.cc
similarity index 69%
rename from src/starboard/shared/starboard/player/filter/fake_graphics_context_provider.cc
rename to src/starboard/testing/fake_graphics_context_provider.cc
index be3549a..69f52e8 100644
--- a/src/starboard/shared/starboard/player/filter/fake_graphics_context_provider.cc
+++ b/src/starboard/testing/fake_graphics_context_provider.cc
@@ -12,34 +12,66 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/shared/starboard/player/filter/fake_graphics_context_provider.h"
+#include "starboard/testing/fake_graphics_context_provider.h"
 
-#include <functional>
+#include "starboard/configuration.h"
 
 namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-namespace filter {
 namespace testing {
 
-FakeGraphicsContextProvider::FakeGraphicsContextProvider()
-    : decode_target_context_thread_("dt_context") {
+FakeGraphicsContextProvider::FakeGraphicsContextProvider() {
+#if SB_HAS(BLITTER)
+  decoder_target_provider_.device = kSbBlitterInvalidDevice;
+#elif SB_HAS(GLES2)
+  decoder_target_provider_.egl_display = NULL;
+  decoder_target_provider_.egl_context = NULL;
   decoder_target_provider_.gles_context_runner = DecodeTargetGlesContextRunner;
   decoder_target_provider_.gles_context_runner_context = this;
+
+  decode_target_context_thread_ = SbThreadCreate(
+      0, kSbThreadPriorityNormal, kSbThreadNoAffinity, true, "dt_context",
+      &FakeGraphicsContextProvider::ThreadEntryPoint, this);
+#endif  // SB_HAS(BLITTER)
 }
 
+FakeGraphicsContextProvider::~FakeGraphicsContextProvider() {
+#if SB_HAS(GLES2)
+  functor_queue_.Wake();
+  SbThreadJoin(decode_target_context_thread_, NULL);
+#endif  // SB_HAS(GLES2)
+}
+
+#if SB_HAS(GLES2)
+
 void FakeGraphicsContextProvider::ReleaseDecodeTarget(
     SbDecodeTarget decode_target) {
   Mutex mutex;
   ConditionVariable condition_variable(mutex);
   ScopedLock scoped_lock(mutex);
-  decode_target_context_thread_.job_queue()->Schedule(std::bind(
+
+  functor_queue_.Put(std::bind(
       &FakeGraphicsContextProvider::ReleaseDecodeTargetOnGlesContextThread,
       this, &mutex, &condition_variable, decode_target));
   condition_variable.Wait();
 }
 
+// static
+void* FakeGraphicsContextProvider::ThreadEntryPoint(void* context) {
+  auto provider = static_cast<FakeGraphicsContextProvider*>(context);
+  provider->RunLoop();
+
+  return NULL;
+}
+
+void FakeGraphicsContextProvider::RunLoop() {
+  while (std::function<void()> functor = functor_queue_.Get()) {
+    if (!functor) {
+      break;
+    }
+    functor();
+  }
+}
+
 void FakeGraphicsContextProvider::ReleaseDecodeTargetOnGlesContextThread(
     Mutex* mutex,
     ConditionVariable* condition_variable,
@@ -65,7 +97,8 @@
   Mutex mutex;
   ConditionVariable condition_variable(mutex);
   ScopedLock scoped_lock(mutex);
-  decode_target_context_thread_.job_queue()->Schedule(std::bind(
+
+  functor_queue_.Put(std::bind(
       &FakeGraphicsContextProvider::RunDecodeTargetFunctionOnGlesContextThread,
       this, &mutex, &condition_variable, target_function,
       target_function_context));
@@ -84,9 +117,7 @@
                                             target_function_context);
 }
 
+#endif  // SB_HAS(GLES2)
+
 }  // namespace testing
-}  // namespace filter
-}  // namespace player
-}  // namespace starboard
-}  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/fake_graphics_context_provider.h b/src/starboard/testing/fake_graphics_context_provider.h
similarity index 69%
rename from src/starboard/shared/starboard/player/filter/fake_graphics_context_provider.h
rename to src/starboard/testing/fake_graphics_context_provider.h
index 0c6a3a4..fae6c1d 100644
--- a/src/starboard/shared/starboard/player/filter/fake_graphics_context_provider.h
+++ b/src/starboard/testing/fake_graphics_context_provider.h
@@ -12,36 +12,45 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_FAKE_GRAPHICS_CONTEXT_PROVIDER_H_
-#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_FAKE_GRAPHICS_CONTEXT_PROVIDER_H_
+#ifndef STARBOARD_TESTING_FAKE_GRAPHICS_CONTEXT_PROVIDER_H_
+#define STARBOARD_TESTING_FAKE_GRAPHICS_CONTEXT_PROVIDER_H_
+
+#include <functional>
 
 #include "starboard/condition_variable.h"
 #include "starboard/decode_target.h"
 #include "starboard/mutex.h"
-#include "starboard/shared/starboard/player/job_queue.h"
-#include "starboard/shared/starboard/player/job_thread.h"
+#include "starboard/queue.h"
+#include "starboard/thread.h"
 
 namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-namespace filter {
 namespace testing {
 
 // This class provides a SbDecodeTargetGraphicsContextProvider implementation
-// used by VideoDecoder related tests.  It creates a thread and forwards decode
+// used by SbPlayer related tests.  It creates a thread and forwards decode
 // target creation/destruction to the thread.
 class FakeGraphicsContextProvider {
  public:
   FakeGraphicsContextProvider();
+  ~FakeGraphicsContextProvider();
 
   SbDecodeTargetGraphicsContextProvider* decoder_target_provider() {
+#if SB_HAS(BLITTER) || SB_HAS(GLES2)
     return &decoder_target_provider_;
+#else   // SB_HAS(BLITTER) || SB_HAS(GLES2)
+    return NULL;
+#endif  // SB_HAS(BLITTER) || SB_HAS(GLES2)
   }
 
+#if SB_HAS(BLITTER) || SB_HAS(GLES2)
   void ReleaseDecodeTarget(SbDecodeTarget decode_target);
+#endif  // SB_HAS(BLITTER) || SB_HAS(GLES2)
 
  private:
+  static void* ThreadEntryPoint(void* context);
+  void RunLoop();
+
+#if SB_HAS(GLES2)
   void ReleaseDecodeTargetOnGlesContextThread(
       Mutex* mutex,
       ConditionVariable* condition_variable,
@@ -60,16 +69,16 @@
       SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
       SbDecodeTargetGlesContextRunnerTarget target_function,
       void* target_function_context);
+#endif  // SB_HAS(GLES2)
 
-  JobThread decode_target_context_thread_;
+#if SB_HAS(BLITTER) || SB_HAS(GLES2)
+  Queue<std::function<void()>> functor_queue_;
+  SbThread decode_target_context_thread_;
   SbDecodeTargetGraphicsContextProvider decoder_target_provider_;
+#endif  // SB_HAS(BLITTER) || SB_HAS(GLES2)
 };
 
 }  // namespace testing
-}  // namespace filter
-}  // namespace player
-}  // namespace starboard
-}  // namespace shared
 }  // namespace starboard
 
-#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_FAKE_GRAPHICS_CONTEXT_PROVIDER_H_
+#endif  // STARBOARD_TESTING_FAKE_GRAPHICS_CONTEXT_PROVIDER_H_
diff --git a/src/starboard/tools/raspi/run_test.py b/src/starboard/tools/raspi/run_test.py
deleted file mode 100755
index 326b654..0000000
--- a/src/starboard/tools/raspi/run_test.py
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/python2
-#
-# 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.
-"""Run a test on Raspi device and print results of test to stdout."""
-
-from __future__ import print_function
-
-import argparse
-import functools
-import logging
-import os
-import re
-import signal
-import sys
-import textwrap
-import time
-
-import pexpect
-
-_COBALT_SRC = os.path.abspath(os.path.join(*([__file__] + 4 * [os.pardir])))
-
-_RASPI_USERNAME = 'pi'
-_RASPI_PASSWORD = 'raspberry'
-
-# Timeouts are in seconds
-_PEXPECT_DEFAULT_TIMEOUT = 600
-_PEXPECT_EXPECT_TIMEOUT = 60
-
-
-def _CleanupProcess(process):
-  """Closes current pexpect process.
-
-  Args:
-    process: Current pexpect process.
-  """
-  if process is not None and process.isalive():
-    # Send ctrl-c to the raspi.
-    process.sendline(chr(3))
-    process.close()
-
-
-# pylint: disable=unused-argument
-def _SigIntOrSigTermHandler(process, signum, frame):
-  """Clean up and exit with status |signum|.
-
-  Args:
-    process: Current pexpect process.
-    signum: Signal number that triggered this callback.  Passed in when the
-      signal handler is called by python runtime.
-    frame: Current stack frame.  Passed in when the signal handler is called by
-      python runtime.
-  """
-  _CleanupProcess(process)
-  sys.exit(signum)
-
-
-def _RunTest(test_path, raspi_ip, flags):
-  """Run a test on a Raspi machine and print relevant stdout.
-
-  Args:
-    test_path: path to the binary test file to be run
-    raspi_ip: the IP address of the Raspi device to run the test on
-    flags: list of flags to pass into binary.
-
-  Returns:
-    Exit status of running the test.
-  """
-  return_value = 1
-
-  try:
-    process = None
-
-    if not os.path.isfile(test_path):
-      raise ValueError('test_path ({}) must be a file.'.format(test_path))
-
-    # This is used to strip ansi color codes from output.
-    sanitize_line_re = re.compile(r'\x1b[^m]*m')
-
-    sys.stdout.write('Process launched, ID={}\n'.format(os.getpid()))
-    sys.stdout.flush()
-
-    test_dir_path, test_file = os.path.split(test_path)
-    test_base_dir = os.path.basename(os.path.normpath(test_dir_path))
-
-    raspi_user_hostname = _RASPI_USERNAME + '@' + raspi_ip
-    raspi_test_path = os.path.join(test_base_dir, test_file)
-
-    # rsync the test files to the raspi
-    options = '-avzh --exclude obj*'
-    source = test_dir_path
-    destination = raspi_user_hostname + ':~/'
-    rsync_command = 'rsync ' + options + ' ' + source + ' ' + destination
-    process = pexpect.spawn(rsync_command, timeout=_PEXPECT_DEFAULT_TIMEOUT)
-
-    signal.signal(signal.SIGINT,
-                  functools.partial(_SigIntOrSigTermHandler, process))
-    signal.signal(signal.SIGTERM,
-                  functools.partial(_SigIntOrSigTermHandler, process))
-
-    process.expect(r'\S+ password:', timeout=_PEXPECT_EXPECT_TIMEOUT)
-    process.sendline(_RASPI_PASSWORD)
-
-    while True:
-      line = sanitize_line_re.sub('', process.readline())
-      if line:
-        sys.stdout.write(line)
-        sys.stdout.flush()
-      else:
-        break
-
-    # ssh into the raspi and run the test
-    ssh_command = 'ssh ' + raspi_user_hostname
-    process = pexpect.spawn(ssh_command, timeout=_PEXPECT_DEFAULT_TIMEOUT)
-
-    signal.signal(signal.SIGINT,
-                  functools.partial(_SigIntOrSigTermHandler, process))
-    signal.signal(signal.SIGTERM,
-                  functools.partial(_SigIntOrSigTermHandler, process))
-
-    process.expect(r'\S+ password:', timeout=_PEXPECT_EXPECT_TIMEOUT)
-    process.sendline(_RASPI_PASSWORD)
-
-    # Escape command line metacharacters in the flags
-    meta_chars = '()[]{}%!^"<>&|'
-    meta_re = re.compile('(' + '|'.join(
-        re.escape(char) for char in list(meta_chars)) + ')')
-    escaped_flags = re.subn(meta_re, r'\\\1', flags)[0]
-
-    test_command = raspi_test_path + ' ' + escaped_flags
-    test_time_tag = 'TEST-{time}'.format(time=time.time())
-    test_success_tag = 'succeeded'
-    test_failure_tag = 'failed'
-    test_success_output = ' && echo ' + test_time_tag + ' ' + test_success_tag
-    test_failure_output = ' || echo ' + test_time_tag + ' ' + test_failure_tag
-    process.sendline(test_command + test_success_output + test_failure_output)
-
-    while True:
-      line = sanitize_line_re.sub('', process.readline())
-      if not line:
-        break
-      sys.stdout.write(line)
-      sys.stdout.flush()
-      if line.startswith(test_time_tag):
-        if line.find(test_success_tag) != -1:
-          return_value = 0
-        break
-
-  except ValueError:
-    logging.exception('Test path invalid.')
-  except pexpect.EOF:
-    logging.exception('pexpect encountered EOF while reading line.')
-  except pexpect.TIMEOUT:
-    logging.exception('pexpect timed out while reading line.')
-  # pylint: disable=W0703
-  except Exception:
-    logging.exception('Error occured while running test.')
-  finally:
-    _CleanupProcess(process)
-
-  return return_value
-
-
-def _AddTargetFlag(parser):
-  """Add target to argument parser."""
-  parser.add_argument(
-      '-t',
-      '--target',
-      default=None,
-      help=(
-          'IP address of the Raspi device to send package to.  If no value is '
-          'specified for this argument then it will default to the '
-          'environment variable `RASPI_ADDR\' if that is set, and '
-          'otherwise exit with status code 1.'),
-      nargs='?')
-
-
-def _AddFlagsFlag(parser, default=None):
-  """Add flags to argument parser.
-
-  Args:
-    parser: An ArgumentParser instance.
-    default: A string consisting of default value.
-  """
-  if default is None:
-    default = ''
-  parser.add_argument(
-      '-f',
-      '--flags',
-      default=default,
-      help=('Space separated flags that will be forwarded into Cobalt upon '
-            'launch. Note that there is no way to pass in a flag that has a '
-            'space in it.'),
-      nargs='?')
-
-
-def main():
-  """Run RunTest using path to binary provided by command line arguments.
-
-  Returns:
-    exit status
-  """
-  sys.path.append(os.path.join(_COBALT_SRC, 'cobalt', 'build'))
-
-  logging.basicConfig(
-      level=logging.INFO,
-      format='%(levelname)s|%(filename)s:%(lineno)s|%(message)s')
-
-  parser = argparse.ArgumentParser(
-      formatter_class=argparse.ArgumentDefaultsHelpFormatter,
-      description=textwrap.dedent(__doc__))
-
-  _AddFlagsFlag(parser)
-  _AddTargetFlag(parser)
-  parser.add_argument('test_path', help='Path of test to be run.', type=str)
-
-  args = parser.parse_args()
-
-  return _RunTest(args.test_path, raspi_ip=args.target, flags=args.flags)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/src/starboard_configuration.py b/src/starboard_configuration.py
index 75bc5ce..cff7294 100644
--- a/src/starboard_configuration.py
+++ b/src/starboard_configuration.py
@@ -18,6 +18,7 @@
 # relative to the location of this file.
 PORT_ROOTS = [
     ["starboard"],
+    ["starboard", "contrib"],
     ["starboard", "port"],
     ["third_party", "starboard"]
 ]