Import Cobalt 20.master.0.221987
diff --git a/src/base/base.gyp b/src/base/base.gyp
index adce093..9cbd82f 100644
--- a/src/base/base.gyp
+++ b/src/base/base.gyp
@@ -5,7 +5,6 @@
 {
   'variables': {
     'chromium_code': 1,
-    'optimize_target_for_speed': 1,
   },
   'targets': [
     {
diff --git a/src/base/trace_event/trace_event.h b/src/base/trace_event/trace_event.h
index ca80197..ef7d6a3 100644
--- a/src/base/trace_event/trace_event.h
+++ b/src/base/trace_event/trace_event.h
@@ -259,6 +259,21 @@
 // override base:TimeTicks::Now().
 #define INTERNAL_TRACE_TIME_NOW() base::subtle::TimeNowIgnoringOverride()
 
+#if defined(TRACING_DISABLED)
+
+#define INTERNAL_TRACE_EVENT_ADD(...) (void)0
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(...) (void)0
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(...) (void)0
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(...) (void)0
+#define INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(...) (void)0
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(...) (void)0
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMPS(...) (void)0
+#define INTERNAL_TRACE_EVENT_ADD_LINK_IDS(...) (void)0
+#define INTERNAL_TRACE_EVENT_METADATA_ADD(...) (void)0
+#define INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(...) (void)0
+#define INTERNAL_TRACE_TASK_EXECUTION(...) (void)0
+
+#else
 // Implementation detail: internal macro to create static category and add
 // event if the category is enabled.
 #define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...)  \
@@ -467,6 +482,8 @@
 
 #endif
 
+#endif  // defined(TRACING_DISABLED)
+
 namespace trace_event_internal {
 
 // Specify these values when the corresponding argument of AddTraceEvent is not
diff --git a/src/base/trace_event/trace_log.cc b/src/base/trace_event/trace_log.cc
index c3a3bcb..9c6982d 100644
--- a/src/base/trace_event/trace_log.cc
+++ b/src/base/trace_event/trace_log.cc
@@ -585,6 +585,7 @@
 
 void TraceLog::SetEnabled(const TraceConfig& trace_config,
                           uint8_t modes_to_enable) {
+#if !defined(TRACING_DISABLED)
   DCHECK(trace_config.process_filter_config().IsEnabled(process_id_));
 
   AutoLock lock(lock_);
@@ -671,6 +672,7 @@
     }
   }
   dispatching_to_observers_ = false;
+#endif  // !defined(TRACING_DISABLED)
 }
 
 void TraceLog::SetArgumentFilterPredicate(
diff --git a/src/cobalt/bindings/bindings.gypi b/src/cobalt/bindings/bindings.gypi
index ad63d68..e89948c 100644
--- a/src/cobalt/bindings/bindings.gypi
+++ b/src/cobalt/bindings/bindings.gypi
@@ -224,6 +224,7 @@
         'generated_type_conversion',
         'global_constructors_idls',
         'interfaces_info_overall',
+        '<(DEPTH)/cobalt/script/engine.gyp:engine',
         '<@(bindings_dependencies)',
       ],
       'export_dependent_settings': [
diff --git a/src/cobalt/bindings/v8c/templates/interface.cc.template b/src/cobalt/bindings/v8c/templates/interface.cc.template
index 8d8736c..c2c9da6 100644
--- a/src/cobalt/bindings/v8c/templates/interface.cc.template
+++ b/src/cobalt/bindings/v8c/templates/interface.cc.template
@@ -16,6 +16,7 @@
 
 {% from 'macros.cc.template' import add_extra_arguments %}
 {% from 'macros.cc.template' import call_cobalt_function %}
+{% from 'macros.cc.template' import get_cobalt_value %}
 {% from 'macros.cc.template' import check_if_object_implements_interface with context %}
 {% from 'macros.cc.template' import constructor_implementation with context %}
 {% from 'macros.cc.template' import function_implementation with context %}
@@ -50,6 +51,7 @@
 #include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
+#include "cobalt/script/v8c/common_v8c_bindings_code.h"
 #include "v8/include/v8.h"
 
 {% endblock includes %}
@@ -393,42 +395,32 @@
 {% else %}
 void {{attribute.idl_name}}AttributeGetter(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  v8::Isolate* isolate = info.GetIsolate();
-  v8::Local<v8::Object> object = info.Holder();
 
-{% if attribute.is_static %}
-{{ static_function_prologue() -}}
-{% endif %}
-
-{% if not attribute.is_static %}
-{{ check_if_object_implements_interface() }}
-{{ nonstatic_function_prologue(impl_class) }}
-{% endif %}
-
-{{ call_cobalt_function(impl_class, attribute.type,
+  script::v8c::shared_bindings::AttributeGetterImpl<{{impl_class}}, {{binding_class}}>(info,
+                    {{ "true" if attribute.is_static else "false"}},
+                    {{ "true" if attribute.type == "void" else "false"}},
+                    [](v8::Isolate* isolate, {{impl_class}}* impl,
+                       cobalt::script::ExceptionState& exception_state,
+                       v8::Local<v8::Value>& result_value) {
+    {{ get_cobalt_value(impl_class, attribute.type,
                         attribute.getter_function_name, [],
                         attribute.raises_exception, attribute.call_with,
                         attribute.is_static) }}
-  if (exception_state.is_exception_set()) {
-    return;
-  }
-  info.GetReturnValue().Set(result_value);
+  });
 }
 
 {% if attribute.has_setter %}
 void {{attribute.idl_name}}AttributeSetter(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  v8::Isolate* isolate = info.GetIsolate();
-  v8::Local<v8::Object> object = info.Holder();
-  v8::Local<v8::Value> v8_value = info[0];
-
-{% if attribute.is_static %}
-{{ static_function_prologue() }}
-{% else %}
-{{ check_if_object_implements_interface() }}
-{{ nonstatic_function_prologue(impl_class)}}
-{% endif %} {#- attribute.is_static #}
+  script::v8c::shared_bindings::AttributeSetterImpl<{{impl_class}}, {{binding_class}}>(info,
+                    {{ "true" if attribute.is_static else "false"}},
+                    {{ "true" if attribute.type == "void" else "false"}},
+                    [](v8::Isolate* isolate, {{impl_class}}* impl,
+                       V8cExceptionState& exception_state,
+                       v8::Local<v8::Value>& result_value,
+                       v8::Local<v8::Value> v8_value) {
 {{ set_attribute_implementation(attribute, impl_class) -}}
+  });
 }
 {% endif %} {#- attribute.has_setter #}
 
@@ -466,33 +458,15 @@
 
 {% if stringifier %}
 void Stringifier(const v8::FunctionCallbackInfo<v8::Value>& info) {
-  v8::Isolate* isolate = info.GetIsolate();
-  v8::Local<v8::Object> object = info.Holder();
-  V8cExceptionState exception_state(isolate);
+  auto* impl = script::v8c::shared_bindings::get_impl_class_from_info<{{impl_class}}, {{binding_class}}>(info);
 
-  {{ 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);
+  ToJSValue(info.GetIsolate(), stringified, &v8_stringified);
 
   info.GetReturnValue().Set(v8_stringified);
 }
@@ -570,12 +544,7 @@
     // 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);
+    script::v8c::shared_bindings::set_intrinsic_error_prototype_interface_template(isolate, function_template);
   }
 {% endif %}
 
@@ -631,59 +600,27 @@
 #if defined({{attribute.conditional}})
 {% endif %}
   {
-    // The name of the property is the identifier of the attribute.
-    v8::Local<v8::String> name = NewInternalString(
-        isolate,
-        "{{attribute.idl_name}}");
+{% if not attribute.is_constructor_attribute %}
 
+    script::v8c::shared_bindings::set_property_for_nonconstructor_attribute(
+                  isolate,
     // 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 %}
-    v8::Local<v8::FunctionTemplate> getter =
-        v8::FunctionTemplate::New(isolate, {{attribute.idl_name}}AttributeGetter);
+                  {{ "false" if attribute.is_unforgeable else "true"}},
+                  {{ "true" if attribute.has_setter else "false"}},
+                  {{ "true" if attribute.on_interface else "false"}},
+                  {{ "true" if attribute.on_instance else "false"}},
+                  function_template,
+                  instance_template,
+                  prototype_template,
+                  "{{attribute.idl_name}}"
+                  ,{{attribute.idl_name}}AttributeGetter
 {% if attribute.has_setter %}
-    v8::Local<v8::FunctionTemplate> setter =
-        v8::FunctionTemplate::New(isolate, {{attribute.idl_name}}AttributeSetter);
-{% else %}
-    v8::Local<v8::FunctionTemplate> setter;
+                  ,{{attribute.idl_name}}AttributeSetter
 {% endif %}
-
-    // The location of the property is determined as follows:
-{% if attribute.on_interface %}
-    // 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->
-{% elif attribute.on_instance %}
-    // 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->
-{% else %}
-    // Otherwise, the property exists solely on the interface's interface
-    // prototype object.
-    prototype_template->
-{% endif %}
-        SetAccessorProperty(
-            name,
-            getter,
-            setter,
-            attributes);
+                  );
 
 {% else %} {#- not attribute.is_constructor_attribute #}
     {
@@ -701,7 +638,6 @@
 {% endif %}
       );
     }
-
 {% endif %} {#- not attribute.is_constructor_attribute #}
   }
 {% if attribute.conditional %}
diff --git a/src/cobalt/bindings/v8c/templates/macros.cc.template b/src/cobalt/bindings/v8c/templates/macros.cc.template
index a1b23c8..9566390 100644
--- a/src/cobalt/bindings/v8c/templates/macros.cc.template
+++ b/src/cobalt/bindings/v8c/templates/macros.cc.template
@@ -18,12 +18,7 @@
  # Checks if object implements interface.
  #}
 {% macro check_if_object_implements_interface() %}
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
-  if (!WrapperPrivate::HasWrapperPrivate(object) ||
-      !{{binding_class}}::GetTemplate(isolate)->HasInstance(object)) {
-    V8cExceptionState exception(isolate);
-    exception.SetSimpleException(script::kDoesNotImplementInterface);
+  if (!script::v8c::shared_bindings::object_implements_interface({{binding_class}}::GetTemplate(isolate), isolate, object)) {
     return;
   }
 {%- endmacro %}
@@ -262,9 +257,8 @@
 {% do arguments_list.append('&exception_state') if raises_exception %}
 {%- endmacro %}
 
-{% macro call_nonvoid_function(return_type, function_name, arguments,
-                               impl_class, is_static) %}
-  if (!exception_state.is_exception_set()) {
+{% macro call_nonvoid_function_internal(function_name, arguments,
+                                impl_class, is_static) %}
 {% if not is_static %}
     ToJSValue(isolate,
               impl->{{function_name}}({{arguments|join(", ")}}),
@@ -274,28 +268,50 @@
               {{impl_class}}::{{function_name}}({{arguments|join(', ')}}),
               &result_value);
 {% endif %}
-  }
 {%- endmacro %}
 
-{% macro call_void_function(function_name, arguments, impl_class, is_static,
-                            cobalt_impl_prefix) %}
+{% macro call_void_function_internal(function_name, arguments,
+                                impl_class, is_static, cobalt_impl_prefix) %}
 {% if not is_static %}
   {{cobalt_impl_prefix}}impl->{{function_name}}({{arguments|join(", ")}});
 {% else %}
   {{impl_class}}::{{function_name}}({{arguments|join(', ')}});
 {% endif %}
+{%- endmacro %}
+
+{% macro get_cobalt_value(impl_class, cobalt_type, function_name, arguments,
+                                raises_exception, call_with, is_static, cobalt_impl_prefix) %}
+  {{ add_extra_arguments(arguments, raises_exception, call_with) }}
+{% if cobalt_type == "void" %}
+  {{call_void_function_internal(function_name, arguments,
+                                impl_class, is_static) -}}
+{% else %}
+  {{call_nonvoid_function_internal(function_name, arguments,
+                                impl_class, is_static) -}}
+{% endif %}
+{%- endmacro %}
+
+{% macro call_nonvoid_function(return_type, function_name, arguments,
+                               impl_class, is_static) %}
+  if (!exception_state.is_exception_set()) {
+
+  {{call_nonvoid_function_internal(function_name, arguments,
+                                impl_class, is_static) -}}
+  }
+{%- endmacro %}
+
+{% macro call_void_function(function_name, arguments, impl_class, is_static,
+                            cobalt_impl_prefix) %}
+{{ call_void_function_internal(function_name, arguments, impl_class, is_static,
+                      cobalt_impl_prefix) -}}
   result_value = v8::Undefined(isolate);
 {%- endmacro %}
 
 {% macro get_impl_class_instance(impl_class) %}
-  WrapperPrivate* wrapper_private =
-      WrapperPrivate::GetFromWrapperObject(object);
-  if (!wrapper_private) {
-    NOTIMPLEMENTED();
+  {{impl_class}}* impl = script::v8c::shared_bindings::get_impl_from_object<{{impl_class}}>(object);
+  if (!impl) {
     return;
   }
-  {{impl_class}}* impl =
-      wrapper_private->wrappable<{{impl_class}}>().get();
 {%- endmacro %}
 
 {% macro static_function_prologue() %}
@@ -373,9 +389,7 @@
 {# In the case there is only one resolution condition, we do not need the arg. #}
 {% if resolution_tests|length > 1 %}
       v8::Local<v8::Value> arg = info[{{distinguishing_argument_index}}];
-      V8cGlobalEnvironment* global_environment =
-          V8cGlobalEnvironment::GetFromIsolate(isolate);
-      WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+      WrapperFactory* wrapper_factory = V8cGlobalEnvironment::GetFromIsolate(isolate)->wrapper_factory();
       v8::Local<v8::Object> object;
       if (arg->IsObject()) {
         object = arg->ToObject();
diff --git a/src/cobalt/browser/cobalt_evergreen.gypi b/src/cobalt/browser/cobalt_evergreen.gypi
index 54605d9..8809ec3 100644
--- a/src/cobalt/browser/cobalt_evergreen.gypi
+++ b/src/cobalt/browser/cobalt_evergreen.gypi
@@ -38,8 +38,8 @@
     '-nostdlib',
   ],
   'defines' : [
-    # This flag is set assuming the system uses pthread.
-    '_LIBCPP_HAS_THREAD_API_PTHREAD',
+    # Ensure that the Starboardized __external_threading file is included.
+    '_LIBCPP_HAS_THREAD_API_EXTERNAL',
     '_LIBCPP_HAS_MUSL_LIBC',
   ],
 }
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 2099216..d86e0d5 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-221008
\ No newline at end of file
+221987
\ No newline at end of file
diff --git a/src/cobalt/css_parser/css_parser.gyp b/src/cobalt/css_parser/css_parser.gyp
index d3f2715..3c1f6c6 100644
--- a/src/cobalt/css_parser/css_parser.gyp
+++ b/src/cobalt/css_parser/css_parser.gyp
@@ -14,7 +14,6 @@
 
 {
   'variables': {
-    'optimize_target_for_speed': 1,
     'sb_pedantic_warnings': 1,
 
     # Define the platform specific Bison binary.
diff --git a/src/cobalt/doc/device_authentication.md b/src/cobalt/doc/device_authentication.md
index d121237..297f82b 100644
--- a/src/cobalt/doc/device_authentication.md
+++ b/src/cobalt/doc/device_authentication.md
@@ -1,7 +1,7 @@
 # Device Authentication
 
 Starting in Cobalt 20, initial URL requests will now have query parameters
-appended to them signed by the platform's secret key, provided to them during
+appended to them signed by the platform's secret key.  The key is provided during
 the certification process.  The key must be stored in secure storage on the
 device.
 
diff --git a/src/cobalt/dom/html_media_element_eme_01b.idl b/src/cobalt/dom/html_media_element_eme_01b.idl
deleted file mode 100644
index e273655..0000000
--- a/src/cobalt/dom/html_media_element_eme_01b.idl
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#extensions
-
-partial interface HTMLMediaElement {
-  [RaisesException] void generateKeyRequest(DOMString keySystem,
-                                            Uint8Array? initData);
-  [RaisesException] void addKey(DOMString keySystem, Uint8Array key,
-                                Uint8Array? initData, DOMString? sessionId);
-  [RaisesException] void cancelKeyRequest(DOMString keySystem,
-                                          DOMString? sessionId);
-};
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 403fc1a..4eff827 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -207,6 +207,56 @@
              : Vector2dLayoutUnit();
 }
 
+RectLayoutUnit Box::GetTransformedBoxFromRoot(
+    const RectLayoutUnit& box_from_margin_box) const {
+  return GetTransformedBoxFromContainingBlock(nullptr, box_from_margin_box);
+}
+
+RectLayoutUnit Box::GetTransformedBoxFromContainingBlock(
+    const ContainerBox* containing_block,
+    const RectLayoutUnit& box_from_margin_box) const {
+  // Get the transform for the margin box from the containing block and
+  // add the box offset from the margin box to the beginning of the transform.
+  math::Matrix3F transform =
+      GetMarginBoxTransformFromContainingBlock(containing_block) *
+      math::TranslateMatrix(box_from_margin_box.x().toFloat(),
+                            box_from_margin_box.y().toFloat());
+
+  // Transform the box.
+  const int kNumPoints = 4;
+  math::PointF box_corners[kNumPoints] = {
+      {0.0f, 0.0f},
+      {box_from_margin_box.width().toFloat(), 0.0f},
+      {0.0f, box_from_margin_box.height().toFloat()},
+      {box_from_margin_box.width().toFloat(),
+       box_from_margin_box.height().toFloat()},
+  };
+
+  for (int i = 0; i < kNumPoints; ++i) {
+    box_corners[i] = transform * box_corners[i];
+  }
+
+  // Return the bounding box for the transformed points.
+  math::PointF min_corner(box_corners[0]);
+  math::PointF max_corner(box_corners[0]);
+  for (int i = 1; i < kNumPoints; ++i) {
+    min_corner.SetToMin(box_corners[i]);
+    max_corner.SetToMax(box_corners[i]);
+  }
+
+  return RectLayoutUnit(LayoutUnit(min_corner.x()), LayoutUnit(min_corner.y()),
+                        LayoutUnit(max_corner.x() - min_corner.x()),
+                        LayoutUnit(max_corner.y() - min_corner.y()));
+}
+
+RectLayoutUnit Box::GetTransformedBoxFromContainingBlockContentBox(
+    const ContainerBox* containing_block,
+    const RectLayoutUnit& box_from_margin_box) const {
+  return GetContainingBlockOffsetFromItsContentBox(containing_block) +
+         GetTransformedBoxFromContainingBlock(containing_block,
+                                              box_from_margin_box);
+}
+
 void Box::SetStaticPositionLeftFromParent(LayoutUnit left) {
   if (left != static_position_offset_from_parent_.x()) {
     static_position_offset_from_parent_.set_x(left);
@@ -374,46 +424,6 @@
                         GetBorderBoxWidth(), GetBorderBoxHeight());
 }
 
-RectLayoutUnit Box::GetTransformedBorderBoxFromRoot() const {
-  return GetTransformedBorderBoxFromContainingBlock(nullptr);
-}
-
-RectLayoutUnit Box::GetTransformedBorderBoxFromContainingBlock(
-    const ContainerBox* containing_block) const {
-  // Get the transform for the margin box from the containing block and
-  // add the border box offset to the beginning of the transform.
-  Vector2dLayoutUnit border_box_offset = GetBorderBoxOffsetFromMarginBox();
-  math::Matrix3F transform =
-      GetMarginBoxTransformFromContainingBlock(containing_block) *
-      math::TranslateMatrix(border_box_offset.x().toFloat(),
-                            border_box_offset.y().toFloat());
-
-  // Transform the border box.
-  const int kNumPoints = 4;
-  math::PointF border_box_corners[kNumPoints] = {
-    { 0.0f, 0.0f },
-    { GetBorderBoxWidth().toFloat(), 0.0f },
-    { 0.0f, GetBorderBoxHeight().toFloat() },
-    { GetBorderBoxWidth().toFloat(), GetBorderBoxHeight().toFloat() },
-  };
-
-  for (int i = 0; i < kNumPoints; ++i) {
-    border_box_corners[i] = transform * border_box_corners[i];
-  }
-
-  // Return the bounding box for the transformed points.
-  math::PointF min_corner(border_box_corners[0]);
-  math::PointF max_corner(border_box_corners[0]);
-  for (int i = 1; i < kNumPoints; ++i) {
-    min_corner.SetToMin(border_box_corners[i]);
-    max_corner.SetToMax(border_box_corners[i]);
-  }
-
-  return RectLayoutUnit(LayoutUnit(min_corner.x()), LayoutUnit(min_corner.y()),
-                        LayoutUnit(max_corner.x() - min_corner.x()),
-                        LayoutUnit(max_corner.y() - min_corner.y()));
-}
-
 LayoutUnit Box::GetBorderBoxWidth() const {
   return border_left_width() + GetPaddingBoxWidth() + border_right_width();
 }
@@ -431,6 +441,11 @@
                         std::max(LayoutUnit(0), GetBorderBoxHeight()));
 }
 
+RectLayoutUnit Box::GetBorderBoxFromMarginBox() const {
+  return RectLayoutUnit(margin_left(), margin_top(), GetBorderBoxWidth(),
+                        GetBorderBoxHeight());
+}
+
 Vector2dLayoutUnit Box::GetBorderBoxOffsetFromRoot(
     bool transform_forms_root) const {
   return GetMarginBoxOffsetFromRoot(transform_forms_root) +
@@ -441,19 +456,6 @@
   return Vector2dLayoutUnit(margin_left(), margin_top());
 }
 
-Vector2dLayoutUnit Box::GetBorderBoxOffsetFromContainingBlock() const {
-  return Vector2dLayoutUnit(GetBorderBoxLeftEdgeOffsetFromContainingBlock(),
-                            GetBorderBoxTopEdgeOffsetFromContainingBlock());
-}
-
-LayoutUnit Box::GetBorderBoxLeftEdgeOffsetFromContainingBlock() const {
-  return left() + GetBorderBoxOffsetFromMarginBox().x();
-}
-
-LayoutUnit Box::GetBorderBoxTopEdgeOffsetFromContainingBlock() const {
-  return top() + GetBorderBoxOffsetFromMarginBox().y();
-}
-
 LayoutUnit Box::GetPaddingBoxWidth() const {
   return padding_left() + width() + padding_right();
 }
@@ -471,6 +473,11 @@
                         std::max(LayoutUnit(0), GetPaddingBoxHeight()));
 }
 
+RectLayoutUnit Box::GetPaddingBoxFromMarginBox() const {
+  return RectLayoutUnit(GetPaddingBoxLeftEdgeOffsetFromMarginBox(),
+                        GetPaddingBoxTopEdgeOffsetFromMarginBox(),
+                        GetPaddingBoxWidth(), GetPaddingBoxHeight());
+}
 Vector2dLayoutUnit Box::GetPaddingBoxOffsetFromRoot(
     bool transform_forms_root) const {
   return GetBorderBoxOffsetFromRoot(transform_forms_root) +
@@ -489,17 +496,10 @@
   return margin_top() + border_top_width();
 }
 
-Vector2dLayoutUnit Box::GetPaddingBoxOffsetFromContainingBlock() const {
-  return Vector2dLayoutUnit(GetPaddingBoxLeftEdgeOffsetFromContainingBlock(),
-                            GetPaddingBoxTopEdgeOffsetFromContainingBlock());
-}
-
-LayoutUnit Box::GetPaddingBoxLeftEdgeOffsetFromContainingBlock() const {
-  return left() + GetPaddingBoxLeftEdgeOffsetFromMarginBox();
-}
-
-LayoutUnit Box::GetPaddingBoxTopEdgeOffsetFromContainingBlock() const {
-  return top() + GetPaddingBoxTopEdgeOffsetFromMarginBox();
+RectLayoutUnit Box::GetContentBoxFromMarginBox() const {
+  return RectLayoutUnit(GetContentBoxLeftEdgeOffsetFromMarginBox(),
+                        GetContentBoxTopEdgeOffsetFromMarginBox(), width(),
+                        height());
 }
 
 Vector2dLayoutUnit Box::GetContentBoxOffsetFromRoot(
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 596b2b4..3d661e2 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -252,6 +252,17 @@
   Vector2dLayoutUnit GetContainingBlockOffsetFromItsContentBox(
       const ContainerBox* containing_block) const;
 
+  // Returns boxes relative to the root or containing block, that take into
+  // account transforms.
+  RectLayoutUnit GetTransformedBoxFromRoot(
+      const RectLayoutUnit& box_from_margin_box) const;
+  RectLayoutUnit GetTransformedBoxFromContainingBlock(
+      const ContainerBox* containing_block,
+      const RectLayoutUnit& box_from_margin_box) const;
+  RectLayoutUnit GetTransformedBoxFromContainingBlockContentBox(
+      const ContainerBox* containing_block,
+      const RectLayoutUnit& box_from_margin_box) const;
+
   // Used values of "left" and "top" are publicly readable and writable so that
   // they can be calculated and adjusted by the formatting context of the parent
   // box.
@@ -325,40 +336,34 @@
 
   // Border box.
   RectLayoutUnit GetBorderBoxFromRoot(bool transform_forms_root) const;
-  RectLayoutUnit GetTransformedBorderBoxFromRoot() const;
-  RectLayoutUnit GetTransformedBorderBoxFromContainingBlock(
-      const ContainerBox* containing_block) const;
 
   LayoutUnit GetBorderBoxWidth() const;
   LayoutUnit GetBorderBoxHeight() const;
   SizeLayoutUnit GetClampedBorderBoxSize() const;
 
+  RectLayoutUnit GetBorderBoxFromMarginBox() const;
   Vector2dLayoutUnit GetBorderBoxOffsetFromRoot(
       bool transform_forms_root) const;
   Vector2dLayoutUnit GetBorderBoxOffsetFromMarginBox() const;
-  Vector2dLayoutUnit GetBorderBoxOffsetFromContainingBlock() const;
-  LayoutUnit GetBorderBoxLeftEdgeOffsetFromContainingBlock() const;
-  LayoutUnit GetBorderBoxTopEdgeOffsetFromContainingBlock() const;
 
   // Padding box.
   LayoutUnit GetPaddingBoxWidth() const;
   LayoutUnit GetPaddingBoxHeight() const;
   SizeLayoutUnit GetClampedPaddingBoxSize() const;
 
+  RectLayoutUnit GetPaddingBoxFromMarginBox() const;
   Vector2dLayoutUnit GetPaddingBoxOffsetFromRoot(
       bool transform_forms_root) const;
   Vector2dLayoutUnit GetPaddingBoxOffsetFromBorderBox() const;
   LayoutUnit GetPaddingBoxLeftEdgeOffsetFromMarginBox() const;
   LayoutUnit GetPaddingBoxTopEdgeOffsetFromMarginBox() const;
-  Vector2dLayoutUnit GetPaddingBoxOffsetFromContainingBlock() const;
-  LayoutUnit GetPaddingBoxLeftEdgeOffsetFromContainingBlock() const;
-  LayoutUnit GetPaddingBoxTopEdgeOffsetFromContainingBlock() const;
 
   // Content box.
   LayoutUnit width() const { return content_size_.width(); }
   LayoutUnit height() const { return content_size_.height(); }
   const SizeLayoutUnit& content_box_size() const { return content_size_; }
 
+  RectLayoutUnit GetContentBoxFromMarginBox() const;
   Vector2dLayoutUnit GetContentBoxOffsetFromRoot(
       bool transform_forms_root) const;
   Vector2dLayoutUnit GetContentBoxOffsetFromMarginBox() const;
diff --git a/src/cobalt/layout/flex_container_box.cc b/src/cobalt/layout/flex_container_box.cc
index b62921d..ae418bd 100644
--- a/src/cobalt/layout/flex_container_box.cc
+++ b/src/cobalt/layout/flex_container_box.cc
@@ -112,12 +112,12 @@
     }
   }
 
-  if (!main_space && main_space_depends_on_containing_block) {
-    // Otherwise, subtract the flex container's margin, border, and padding
-    // from the space available to the flex container in that dimension and use
-    // that value.
+  if (GetLevel() == kBlockLevel) {
     if (main_direction_is_horizontal) {
-      if (GetLevel() == kBlockLevel) {
+      if (!main_space && main_space_depends_on_containing_block) {
+        // Otherwise, subtract the flex container's margin, border, and padding
+        // from the space available to the flex container in that dimension and
+        // use that value.
         base::Optional<LayoutUnit> margin_main_start =
             GetUsedMarginLeftIfNotAuto(computed_style(),
                                        layout_params.containing_block_size);
@@ -131,40 +131,25 @@
                      padding_left() - padding_right();
       }
     } else {
-      if (GetLevel() == kInlineLevel) {
-        base::Optional<LayoutUnit> margin_main_start =
-            GetUsedMarginTopIfNotAuto(computed_style(),
-                                      layout_params.containing_block_size);
-        base::Optional<LayoutUnit> margin_main_end =
-            GetUsedMarginBottomIfNotAuto(computed_style(),
-                                         layout_params.containing_block_size);
-        main_space = layout_params.containing_block_size.width() -
-                     margin_main_start.value_or(LayoutUnit()) -
-                     margin_main_end.value_or(LayoutUnit()) -
-                     border_top_width() - border_bottom_width() -
-                     padding_top() - padding_bottom();
+      if (!cross_space) {
+        // Otherwise, subtract the flex container's margin, border, and padding
+        // from the space available to the flex container in that dimension and
+        // use that value.
+        base::Optional<LayoutUnit> margin_cross_start =
+            GetUsedMarginLeftIfNotAuto(computed_style(),
+                                       layout_params.containing_block_size);
+        base::Optional<LayoutUnit> margin_cross_end =
+            GetUsedMarginRightIfNotAuto(computed_style(),
+                                        layout_params.containing_block_size);
+        cross_space = layout_params.containing_block_size.width() -
+                      margin_cross_start.value_or(LayoutUnit()) -
+                      margin_cross_end.value_or(LayoutUnit()) -
+                      border_left_width() - border_right_width() -
+                      padding_left() - padding_right();
       }
     }
   }
 
-  if (main_space) {
-    if (max_main_space_ && *main_space > *max_main_space_) {
-      main_space = max_main_space_;
-    }
-    if (min_main_space_ && *main_space < *min_main_space_) {
-      main_space = min_main_space_;
-    }
-  }
-
-  if (cross_space) {
-    if (max_cross_space_ && *cross_space_ > *max_cross_space_) {
-      cross_space = max_cross_space_;
-    }
-    if (min_cross_space_ && *cross_space < *min_cross_space_) {
-      cross_space = *min_cross_space_;
-    }
-  }
-
   main_space_ = main_space;
   cross_space_ = cross_space;
 }
@@ -224,6 +209,7 @@
     }
   }
 
+  LayoutUnit main_size = LayoutUnit();
   // 4. Determine the main size of the flex container using the rules of the
   // formatting context in which it participates.
   if (main_direction_is_horizontal) {
@@ -245,40 +231,23 @@
           width_depends_on_containing_block, maybe_left, maybe_right,
           maybe_margin_left, maybe_margin_right, main_space_, cross_space_);
     }
-    flex_formatting_context.set_main_size(width());
+    main_size = width();
   } else {
     if (!layout_params.freeze_height) {
-      base::Optional<LayoutUnit> maybe_top = GetUsedTopIfNotAuto(
-          computed_style(), layout_params.containing_block_size);
-      base::Optional<LayoutUnit> maybe_bottom = GetUsedBottomIfNotAuto(
-          computed_style(), layout_params.containing_block_size);
-      base::Optional<LayoutUnit> maybe_margin_top = GetUsedMarginTopIfNotAuto(
-          computed_style(), layout_params.containing_block_size);
-      base::Optional<LayoutUnit> maybe_margin_bottom =
-          GetUsedMarginBottomIfNotAuto(computed_style(),
-                                       layout_params.containing_block_size);
-
-      UpdateContentHeightAndMargins(layout_params.containing_block_size,
-                                    maybe_top, maybe_bottom, maybe_margin_top,
-                                    maybe_margin_bottom, main_space_);
+      main_size =
+          main_space_.value_or(flex_formatting_context.fit_content_main_size());
+    } else {
+      main_size = height();
     }
-    flex_formatting_context.set_main_size(height());
   }
 
-  LayoutUnit main_size = flex_formatting_context.main_size();
   if (max_main_space_ && main_size > *max_main_space_) {
     main_size = *max_main_space_;
   }
   if (min_main_space_ && main_size < *min_main_space_) {
     main_size = *min_main_space_;
   }
-  flex_formatting_context.set_main_size(main_size);
 
-  if (main_direction_is_horizontal) {
-    set_width(main_size);
-  } else {
-    set_height(main_size);
-  }
   flex_formatting_context.SetContainerMainSize(main_size);
 
   // Main Size Determination:
@@ -286,7 +255,19 @@
   flex_formatting_context.set_multi_line(ContainerIsMultiLine());
   for (auto& item : items) {
     DCHECK(!item->box()->IsAbsolutelyPositioned());
-    flex_formatting_context.CollectItemIntoLine(std::move(item));
+    flex_formatting_context.CollectItemIntoLine(main_size, std::move(item));
+  }
+
+  if (main_direction_is_horizontal) {
+    set_width(main_size);
+  } else {
+    if (!main_space_) {
+      // For vertical containers with indefinite main space, us the fit content
+      // main size. Note: For horizontal containers, this sizing is already
+      // handled by UpdateContentWidthAndMargins().
+      main_size = flex_formatting_context.fit_content_main_size();
+    }
+    set_height(main_size);
   }
 
   // Perform remaining steps of the layout of the items.
diff --git a/src/cobalt/layout/flex_formatting_context.cc b/src/cobalt/layout/flex_formatting_context.cc
index b106ffe..7a7a9e7 100644
--- a/src/cobalt/layout/flex_formatting_context.cc
+++ b/src/cobalt/layout/flex_formatting_context.cc
@@ -29,7 +29,8 @@
                                              bool direction_is_reversed)
     : layout_params_(layout_params),
       main_direction_is_horizontal_(main_direction_is_horizontal),
-      direction_is_reversed_(direction_is_reversed) {}
+      direction_is_reversed_(direction_is_reversed),
+      fit_content_main_size_(LayoutUnit()) {}
 
 FlexFormattingContext::~FlexFormattingContext() {}
 
@@ -49,29 +50,38 @@
   //   https://www.w3.org/TR/css-flexbox-1/#intrinsic-sizes
   // Note that for column flex-direction, this is the intrinsic cross size.
 
-  // TODO handle !main_direction_is_horizontal_
-  set_shrink_to_fit_width(shrink_to_fit_width() + child_box->width() +
-                          child_box->GetContentToMarginHorizontal());
+  if (main_direction_is_horizontal_) {
+    fit_content_main_size_ = shrink_to_fit_width() + child_box->width() +
+                             child_box->GetContentToMarginHorizontal();
+    set_shrink_to_fit_width(fit_content_main_size_);
+  } else {
+    fit_content_main_size_ +=
+        child_box->height() + child_box->GetContentToMarginVertical();
+  }
   set_auto_height(child_box->height());
 }
 
 void FlexFormattingContext::CollectItemIntoLine(
-    std::unique_ptr<FlexItem>&& item) {
+    LayoutUnit main_space, std::unique_ptr<FlexItem>&& item) {
   // Collect flex items into flex lines:
   //   https://www.w3.org/TR/css-flexbox-1/#algo-line-break
   if (lines_.empty()) {
     lines_.emplace_back(new FlexLine(layout_params_,
                                      main_direction_is_horizontal_,
-                                     direction_is_reversed_, main_size_));
+                                     direction_is_reversed_, main_space));
+    fit_content_main_size_ = LayoutUnit();
   }
 
+  DCHECK(!lines_.empty());
   if (multi_line_ && !lines_.back()->CanAddItem(*item)) {
     lines_.emplace_back(new FlexLine(layout_params_,
                                      main_direction_is_horizontal_,
-                                     direction_is_reversed_, main_size_));
+                                     direction_is_reversed_, main_space));
   }
 
   lines_.back()->AddItem(std::move(item));
+  fit_content_main_size_ =
+      std::max(fit_content_main_size_, lines_.back()->items_outer_main_size());
 }
 
 void FlexFormattingContext::ResolveFlexibleLengthsAndCrossSizes(
@@ -201,11 +211,6 @@
 }
 
 LayoutUnit FlexFormattingContext::GetBaseline() {
-  if (!main_direction_is_horizontal_) {
-    // TODO: implement this for column flex containers.
-    NOTIMPLEMENTED() << "Column flex boxes not yet implemented.";
-  }
-
   LayoutUnit baseline = cross_size_;
   if (!lines_.empty()) {
     if (direction_is_reversed_ && !main_direction_is_horizontal_) {
diff --git a/src/cobalt/layout/flex_formatting_context.h b/src/cobalt/layout/flex_formatting_context.h
index 9df264e..0638721 100644
--- a/src/cobalt/layout/flex_formatting_context.h
+++ b/src/cobalt/layout/flex_formatting_context.h
@@ -38,7 +38,8 @@
   void UpdateRect(Box* child_box);
 
   // Collects the flex item into a flex line.
-  void CollectItemIntoLine(std::unique_ptr<FlexItem>&& item);
+  void CollectItemIntoLine(LayoutUnit main_space,
+                           std::unique_ptr<FlexItem>&& item);
 
   // layout flex items and determine cross size.
   void ResolveFlexibleLengthsAndCrossSizes(
@@ -47,8 +48,6 @@
       const base::Optional<LayoutUnit>& max_cross_space,
       const scoped_refptr<cssom::PropertyValue>& align_content);
 
-  LayoutUnit main_size() const { return main_size_; }
-  void set_main_size(LayoutUnit value) { main_size_ = value; }
   LayoutUnit cross_size() const { return cross_size_; }
 
   const LayoutParams& layout_params() const { return layout_params_; }
@@ -69,14 +68,18 @@
 
   LayoutUnit GetBaseline();
 
+  // Used to calculate the "auto" size in the main direction of the box that
+  // establishes this formatting context.
+  LayoutUnit fit_content_main_size() const { return fit_content_main_size_; }
+
  private:
   LayoutParams layout_params_;
   const bool main_direction_is_horizontal_;
   const bool direction_is_reversed_;
   bool multi_line_ = false;
 
-  LayoutUnit main_size_;
   LayoutUnit cross_size_;
+  LayoutUnit fit_content_main_size_;
 
   std::vector<std::unique_ptr<FlexLine>> lines_;
 
diff --git a/src/cobalt/layout/flex_line.h b/src/cobalt/layout/flex_line.h
index 1cca9e5..65a99d7 100644
--- a/src/cobalt/layout/flex_line.h
+++ b/src/cobalt/layout/flex_line.h
@@ -54,6 +54,8 @@
 
   LayoutUnit GetBaseline();
 
+  LayoutUnit items_outer_main_size() { return items_outer_main_size_; }
+
  private:
   LayoutUnit GetOuterMainSizeOfBox(Box* box,
                                    LayoutUnit content_main_size) const;
diff --git a/src/cobalt/layout/intersection_observer_target.cc b/src/cobalt/layout/intersection_observer_target.cc
index 24741e2..a82d1f2 100644
--- a/src/cobalt/layout/intersection_observer_target.cc
+++ b/src/cobalt/layout/intersection_observer_target.cc
@@ -48,7 +48,8 @@
 
   // Let targetRect be target's bounding border box.
   RectLayoutUnit target_transformed_border_box(
-      target_box->GetTransformedBorderBoxFromRoot());
+      target_box->GetTransformedBoxFromRoot(
+          target_box->GetBorderBoxFromMarginBox()));
   math::RectF target_rect =
       math::RectF(target_transformed_border_box.x().toFloat(),
                   target_transformed_border_box.y().toFloat(),
@@ -141,7 +142,8 @@
     // Otherwise, it's the result of running the getBoundingClientRect()
     // algorithm on the intersection root.
     RectLayoutUnit root_transformed_border_box(
-        root_box->GetTransformedBorderBoxFromRoot());
+        root_box->GetTransformedBoxFromRoot(
+            root_box->GetBorderBoxFromMarginBox()));
     root_bounds_without_margins =
         math::RectF(root_transformed_border_box.x().toFloat(),
                     root_transformed_border_box.y().toFloat(),
@@ -189,10 +191,14 @@
   math::RectF intersection_rect = target_rect;
 
   // Let container be the containing block of the target.
-  math::Vector2dF total_offset_from_containing_block =
-      target_box->GetBorderBoxOffsetFromContainingBlock();
   const ContainerBox* prev_container = target_box;
   const ContainerBox* container = prev_container->GetContainingBlock();
+  RectLayoutUnit box_from_containing_block =
+      target_box->GetTransformedBoxFromContainingBlock(
+          container, target_box->GetBorderBoxFromMarginBox());
+  math::Vector2dF total_offset_from_containing_block =
+      math::Vector2dF(box_from_containing_block.x().toFloat(),
+                      box_from_containing_block.y().toFloat());
 
   // While container is not the intersection root:
   while (container != root_box) {
@@ -225,12 +231,18 @@
     // container. (Note: The containing block of an element with 'position:
     // absolute' is formed by the padding edge of the ancestor.
     // https://www.w3.org/TR/CSS2/visudet.html)
-    math::Vector2dF next_offset_from_containing_block =
+    RectLayoutUnit next_box_from_containing_block =
         prev_container->computed_style()->position() ==
                 cssom::KeywordValue::GetAbsolute()
-            ? container->GetPaddingBoxOffsetFromContainingBlock()
-            : container->GetContentBoxOffsetFromContainingBlockContentBox(
-                  container->GetContainingBlock());
+            ? container->GetTransformedBoxFromContainingBlock(
+                  container->GetContainingBlock(),
+                  container->GetPaddingBoxFromMarginBox())
+            : container->GetTransformedBoxFromContainingBlockContentBox(
+                  container->GetContainingBlock(),
+                  container->GetContentBoxFromMarginBox());
+    math::Vector2dF next_offset_from_containing_block =
+        math::Vector2dF(next_box_from_containing_block.x().toFloat(),
+                        next_box_from_containing_block.y().toFloat());
     total_offset_from_containing_block += next_offset_from_containing_block;
 
     prev_container = container;
@@ -242,14 +254,17 @@
   // (Note: The containing block of an element with 'position: absolute'
   // is formed by the padding edge of the ancestor.
   // https://www.w3.org/TR/CSS2/visudet.html)
-  math::Vector2dF containing_block_offset_from_origin =
+  RectLayoutUnit containing_block_box_from_origin =
       prev_container->computed_style()->position() ==
                   cssom::KeywordValue::GetAbsolute() &&
               !IsOverflowCropped(container->computed_style())
-          ? container->GetPaddingBoxOffsetFromRoot(
-                false /*transform_forms_root*/)
-          : container->GetContentBoxOffsetFromRoot(
-                false /*transform_forms_root*/);
+          ? container->GetTransformedBoxFromRoot(
+                container->GetPaddingBoxFromMarginBox())
+          : container->GetTransformedBoxFromRoot(
+                container->GetContentBoxFromMarginBox());
+  math::Vector2dF containing_block_offset_from_origin =
+      math::Vector2dF(containing_block_box_from_origin.x().toFloat(),
+                      containing_block_box_from_origin.y().toFloat());
 
   intersection_rect.set_x(total_offset_from_containing_block.x() +
                           containing_block_offset_from_origin.x());
diff --git a/src/cobalt/layout/layout_boxes.cc b/src/cobalt/layout/layout_boxes.cc
index da333da..df06332 100644
--- a/src/cobalt/layout/layout_boxes.cc
+++ b/src/cobalt/layout/layout_boxes.cc
@@ -56,7 +56,9 @@
   for (Boxes::const_iterator box_iterator = client_rect_boxes.begin();
        box_iterator != client_rect_boxes.end(); ++box_iterator) {
     RectLayoutUnit transformed_border_box(
-        (*box_iterator)->GetTransformedBorderBoxFromRoot());
+        (*box_iterator)
+            ->GetTransformedBoxFromRoot(
+                (*box_iterator)->GetBorderBoxFromMarginBox()));
     dom_rect_list->AppendDOMRect(
         new dom::DOMRect(transformed_border_box.x().toFloat(),
                          transformed_border_box.y().toFloat(),
@@ -181,7 +183,8 @@
           if (container == container_box) {
             // Add this box's border box to the scroll area.
             RectLayoutUnit border_box =
-                box->GetTransformedBorderBoxFromContainingBlock(container_box);
+                box->GetTransformedBoxFromContainingBlock(
+                    container_box, box->GetBorderBoxFromMarginBox());
             scroll_area.Union(math::RectF(border_box.x().toFloat(),
                                           border_box.y().toFloat(),
                                           border_box.width().toFloat(),
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/combined-container-sizing-edge-cases-expected.png b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-container-sizing-edge-cases-expected.png
new file mode 100644
index 0000000..5b2e951
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-container-sizing-edge-cases-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/combined-container-sizing-edge-cases.html b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-container-sizing-edge-cases.html
new file mode 100644
index 0000000..7031bc3
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-container-sizing-edge-cases.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<!--
+ | Combined tests for basic functionality of CSS Flexible Box Layout Module.
+ |   https://www.w3.org/TR/css-flexbox-1
+ | This exercises some edge cases for container box sizes.
+ -->
+<html>
+<head>
+<style>
+  body {
+    margin: 0px;
+    font-family: Roboto;
+    background-color: #808080;
+    font-size: 10px;
+  }
+  .vsized {
+    height: 40px;
+  }
+  .hsized {
+    width: 40px;
+  }
+  .flex {
+    display: flex;
+  }
+  .inlineflex {
+    display: inline-flex;
+  }
+  .row {
+    flex-flow: row wrap;
+   }
+  .column {
+    flex-flow: column wrap;
+   }
+  .container {
+    color: #99b9f3;
+    background-color: #ffee00;
+    opacity: 0.75;
+    padding: 2px;
+    border: 2px solid #000060;
+    outline: 2px solid #00f000;
+    margin: 6px;
+    border-top-width: 5px;
+  }
+  div > span {
+    background-color: #0047ab;
+  }
+</style>
+</head>
+<body>
+
+<div class="vsized flex column container">
+  <span style="align-self: flex-end;">end</span>
+  <span style="align-self: flex-start;">start</span>
+  <span style="align-self: center;">center</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+</div>
+<div class="hsized flex column container">
+  <span style="align-self: flex-end;">end</span>
+  <span style="align-self: flex-start;">start</span>
+  <span style="align-self: center;">center</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+</div>
+-hg
+<div class="vsized inlineflex column container">
+  <span style="align-self: flex-end;">end</span>
+  <span style="align-self: flex-start;">start</span>
+  <span style="align-self: center;">center</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+</div>
+hg
+<div class="hsized inlineflex column container" style="height: 96px;">
+  <span style="align-self: flex-end; height: 5%;">end</span>
+  <span style="align-self: flex-start; height: 5%;">start</span>
+  <span style="align-self: center; height: 5%;">center</span>
+  <span class="vsized" style="align-self: stretch; height: 5%;">stretch</span>
+  <span class="vsized" style="align-self: stretch; height: 5%;">stretch</span>
+  <span class="vsized" style="align-self: stretch; height: 5%;">stretch</span>
+</div>
+hg
+<div class="hsized inlineflex column container" style="height: 96px;">
+  <span style="align-self: flex-end;">end</span>
+  <span style="align-self: flex-start;">start</span>
+  <span style="align-self: center;">center</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+</div>
+hg
+<div class="hsized inlineflex column container" style="max-height: 96px;">
+  <span style="align-self: flex-end;">end</span>
+  <span style="align-self: flex-start;">start</span>
+  <span style="align-self: center;">center</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+</div>
+hg
+<div class="inlineflex column container" style="width:16px; max-height: 96px;">
+  <span style="align-self: flex-end;">end</span>
+  <span style="align-self: flex-start;">start</span>
+  <span style="align-self: center;">center</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+</div>
+hg
+<div class="hsized inlineflex column container">
+  <span style="align-self: flex-end;">end</span>
+  <span style="align-self: flex-start;">start</span>
+  <span style="align-self: center;">center</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+  <span class="vsized" style="align-self: stretch;">stretch</span>
+</div>
+hg
+
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/combined-with-baselines-percentages.html b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-with-baselines-percentages.html
index 5629e51..dc516bd 100644
--- a/src/cobalt/layout_tests/testdata/css3-flexbox/combined-with-baselines-percentages.html
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/combined-with-baselines-percentages.html
@@ -42,7 +42,7 @@
     flex-flow: row wrap;
     justify-content: space-around;
     align-items: center;
-    align-content: flex-end; #space-between;
+    align-content: flex-end;
     min-width: 2px;
     padding: 2px;
     border: 2px solid #000060;
diff --git a/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt
index da59b48..0249f80 100644
--- a/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-flexbox/layout_tests.txt
@@ -1,4 +1,5 @@
 combined-baseline
+combined-container-sizing-edge-cases
 combined-order-and-multiline
 combined-positioning-tests
 combined-shrinking-and-justify-content
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-has-rotate-transform-expected.png b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-has-rotate-transform-expected.png
new file mode 100644
index 0000000..b9fe389
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-has-rotate-transform-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-has-rotate-transform.html b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-has-rotate-transform.html
new file mode 100644
index 0000000..1495268
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-has-rotate-transform.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<!--
+ | This test checks that the intersection is correctly computed when an element
+ | in the containing block chain undergoes a transform. One side of the border
+ | box of the containing block is disproportionately large to check that the
+ | rotations are being calculated relative to the border box and not any other
+ | others (i.e. padding).
+ | The containing block element turns yellow only if the intersection between
+ | the target and the viewport is correctly computed to be 0.5.
+ |   https://www.w3.org/TR/intersection-observer/
+ -->
+<html>
+<head>
+  <style>
+    #moving {
+      border-left-width: 100px;
+      border-style: solid;
+      background-color: red;
+      width: 100px;
+      height: 100px;
+      left: 0px;
+      top: 0px;
+      position: absolute;
+    }
+    #target {
+      background-color: green;
+      width: 100px;
+      height: 100px;
+      left: -100px;
+      top: 0px;
+      position: relative;
+    }
+  </style>
+</head>
+<body>
+  <div id="moving">
+    <div id="target">
+    </div>
+  </div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.addEventListener("load", function() {
+      var movingElement = document.querySelector('#moving');
+      var targetElement = document.querySelector('#target');
+      var expectedRatio = 0.5;
+      // leave room for some rounding differences due to rotation calculations
+      var epsilon = 0.02;
+
+      function buildThresholdsList() {
+        var thresholds = []
+        var numSteps = 100;
+        for (var i = 0; i < numSteps; i++) {
+          thresholds.push(i / numSteps);
+        }
+        return thresholds;
+      }
+
+      function handleIntersect(entries, observer) {
+        entries.forEach(function(entry) {
+          console.log(entry.intersectionRatio);
+          if (Math.abs(entry.intersectionRatio - expectedRatio) < epsilon) {
+            movingElement.style.backgroundColor = "yellow";
+          }
+        });
+      }
+
+      function createObserver() {
+        var options = {
+          root: null,
+          rootMargin: "0px",
+          threshold: buildThresholdsList()
+        };
+
+        var observer = new IntersectionObserver(handleIntersect, options);
+        observer.observe(targetElement);
+      }
+
+      createObserver();
+
+      movingElement.style.transform = "rotate(90deg)";
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+        window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-undergoes-transition-expected.png b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-undergoes-transition-expected.png
new file mode 100644
index 0000000..5675795
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-undergoes-transition-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-undergoes-transition.html b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-undergoes-transition.html
new file mode 100644
index 0000000..41f4e1c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/containing-block-undergoes-transition.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<!--
+ | This test checks that intersection observer events get fired when
+ | intersection information changes as the result of an element in the
+ | containing block chain undergoing a transition.
+ | The target element is initially green when it intersects with the root
+ | element, but when it stops intersecting (as it should after the transition)
+ | it should turn yellow.
+ |   https://www.w3.org/TR/intersection-observer/
+ -->
+<html>
+<head>
+  <style>
+    div {
+      margin: 50px;
+    }
+    #static {
+      background-color: red;
+      width: 200px;
+      height: 200px;
+      position: absolute;
+    }
+    #moving {
+      background-color: blue;
+      width: 200px;
+      height: 200px;
+      position: absolute;
+      transition: transform 1s linear;
+    }
+    #target {
+      background-color: green;
+      width: 200px;
+      height: 200px;
+    }
+  </style>
+</head>
+<body>
+  <div id="static">
+    <div id="moving">
+      <div id="target">
+      </div>
+    </div>
+  </div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.addEventListener("load", function() {
+      var staticElement = document.querySelector('#static');
+      var movingElement = document.querySelector('#moving');
+      var targetElement = document.querySelector('#target');
+
+      function handleIntersect(entries, observer) {
+        entries.forEach(function(entry) {
+          console.log(entry.intersectionRatio);
+          if (entry.intersectionRatio == 0) {
+            entry.target.style.backgroundColor = "yellow";
+          }
+        });
+      }
+
+      function createObserver() {
+        var options = {
+          root: staticElement,
+          rootMargin: "0px",
+          threshold: 0.0
+        };
+
+        var observer = new IntersectionObserver(handleIntersect, options);
+        observer.observe(targetElement);
+      }
+
+      createObserver();
+
+      movingElement.style.transform = "translate(110px, 0)";
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+        window.testRunner.AdvanceClockByMs(1000);
+        window.testRunner.DoNonMeasuredLayout();
+        window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt b/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
index f449fdd..767d972 100644
--- a/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
@@ -1,3 +1,5 @@
+containing-block-has-rotate-transform
+containing-block-undergoes-transition
 element-in-containing-block-chain-has-overflow-clip-with-padding-and-border
 element-in-containing-block-chain-has-overflow-clip-without-padding-or-border
 intersection-ratio-is-nonzero-but-is-intersecting-is-false
@@ -13,4 +15,5 @@
 target-has-nonzero-padding-and-border
 target-with-nonzero-area-is-edge-adjacent-to-root
 target-with-zero-area-is-edge-adjacent-to-root
+target-undergoes-transition
 unobserved-targets-do-not-get-included-in-next-update
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/target-undergoes-transition-expected.png b/src/cobalt/layout_tests/testdata/intersection-observer/target-undergoes-transition-expected.png
new file mode 100644
index 0000000..28b8528
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/target-undergoes-transition-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/target-undergoes-transition.html b/src/cobalt/layout_tests/testdata/intersection-observer/target-undergoes-transition.html
new file mode 100644
index 0000000..52b3592
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/target-undergoes-transition.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<!--
+ | This test checks that intersection observer events get fired when
+ | intersection information changes as the result of the target undergoing a
+ | transition.
+ | The target element is initially green when it intersects completely with the
+ | viewport, but when it moves partially offscreen (as it should after the
+ | transition) it should turn yellow.
+ |   https://www.w3.org/TR/intersection-observer/
+ -->
+<html>
+<head>
+  <style>
+    div {
+      margin: 50px;
+    }
+    #static1 {
+      background-color: red;
+      width: 200px;
+      height: 200px;
+      position: absolute;
+    }
+    #static2 {
+      background-color: blue;
+      width: 200px;
+      height: 200px;
+      position: absolute;
+    }
+    #moving {
+      background-color: green;
+      width: 200px;
+      height: 200px;
+      transition: transform 1s linear;
+    }
+  </style>
+</head>
+<body>
+  <div id="static1">
+    <div id="static2">
+      <div id="moving">
+      </div>
+    </div>
+  </div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.addEventListener("load", function() {
+      var firstStaticElement = document.querySelector('#static1');
+      var secondStaticElement = document.querySelector('#static2');
+      var movingElement = document.querySelector('#moving');
+
+      function handleIntersect(entries, observer) {
+        entries.forEach(function(entry) {
+          console.log(entry.intersectionRatio);
+          if (entry.intersectionRatio < 1) {
+            entry.target.style.backgroundColor = "yellow";
+          }
+        });
+      }
+
+      function createObserver() {
+        var options = {
+          root: null,
+          rootMargin: "0px",
+          threshold: 1
+        };
+
+        var observer = new IntersectionObserver(handleIntersect, options);
+        observer.observe(movingElement);
+      }
+
+      createObserver();
+
+      movingElement.style.transform = "translate(-200px, 0)";
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+        window.testRunner.AdvanceClockByMs(1000);
+        window.testRunner.DoNonMeasuredLayout();
+        window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/loader/embedded_resources/cobalt_splash_screen.css b/src/cobalt/loader/embedded_resources/cobalt_splash_screen.css
index 58b938a..36e5480 100644
--- a/src/cobalt/loader/embedded_resources/cobalt_splash_screen.css
+++ b/src/cobalt/loader/embedded_resources/cobalt_splash_screen.css
@@ -21,7 +21,7 @@
 
 #splash {
   background-color: #ffffff;
-  background-image: url("h5vcc-embedded://cobalt_word_logo_1024.png");
+  background-image: url("h5vcc-embedded://cobalt_word_logo_356.png");
   background-position: center center;
   background-repeat: no-repeat;
   background-size: auto 33%;
diff --git a/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html b/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html
index b6a369f..7d59798 100644
--- a/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html
+++ b/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html
@@ -20,14 +20,14 @@
   <meta http-equiv="Content-Security-Policy" content="default-src 'none';
       script-src h5vcc-embedded://*/splash_screen.js;
       style-src h5vcc-embedded://*/cobalt_splash_screen.css;
-      img-src h5vcc-embedded://*/cobalt_word_logo_1024.png;">
+      img-src h5vcc-embedded://*/cobalt_word_logo_356.png;">
 <link rel="stylesheet" type="text/css"
     href="h5vcc-embedded://cobalt_splash_screen.css">
 </head>
 
 <body>
   <div id="splash">
-    <img src="h5vcc-embedded://cobalt_word_logo_1024.png" class="hidden">
+    <img src="h5vcc-embedded://cobalt_word_logo_356.png" class="hidden">
   </div>
   <div id="loading">
     <div id="spinner">
diff --git a/src/cobalt/loader/embedded_resources/cobalt_word_logo_1024.png b/src/cobalt/loader/embedded_resources/cobalt_word_logo_1024.png
deleted file mode 100644
index 2a1d64b..0000000
--- a/src/cobalt/loader/embedded_resources/cobalt_word_logo_1024.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/loader/embedded_resources/cobalt_word_logo_356.png b/src/cobalt/loader/embedded_resources/cobalt_word_logo_356.png
new file mode 100644
index 0000000..8f05d31
--- /dev/null
+++ b/src/cobalt/loader/embedded_resources/cobalt_word_logo_356.png
Binary files differ
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index cde58c0..9618b0b 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -56,6 +56,7 @@
       frame_provider_(new FrameProvider()) {
   TRACE_EVENT0("cobalt::loader::image",
                "AnimatedWebPImage::AnimatedWebPImage()");
+  DETACH_FROM_THREAD(task_runner_thread_checker_);
 }
 
 scoped_refptr<AnimatedImage::FrameProvider>
@@ -68,27 +69,32 @@
 void AnimatedWebPImage::Play(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::Play()");
-  base::AutoLock lock(lock_);
-
-  if (is_playing_) {
-    return;
+  // This function may be called from a thread that is not the task runner
+  // thread, but it must be consistent (and consistent with Stop() also).
+  // This ensures that it's safe to set |task_runner_| without holding a lock.
+  DCHECK_CALLED_ON_VALID_THREAD(task_runner_thread_checker_);
+  if (!task_runner_) {
+    task_runner_ = task_runner;
+  } else {
+    DCHECK_EQ(task_runner_, task_runner);
   }
-  is_playing_ = true;
 
-  task_runner_ = task_runner;
-  if (received_first_frame_) {
-    PlayInternal();
-  }
+  task_runner_->PostTask(FROM_HERE, base::Bind(&AnimatedWebPImage::PlayInternal,
+                                               base::Unretained(this)));
 }
 
 void AnimatedWebPImage::Stop() {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::Stop()");
-  base::AutoLock lock(lock_);
-  if (is_playing_) {
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&AnimatedWebPImage::StopInternal, base::Unretained(this)));
+  // This function may be called from a thread that is not the task runner
+  // thread, but it must be consistent (and consistent with Play() also).
+  DCHECK_CALLED_ON_VALID_THREAD(task_runner_thread_checker_);
+  if (!task_runner_) {
+    // The image has not started playing yet.
+    return;
   }
+
+  task_runner_->PostTask(FROM_HERE, base::Bind(&AnimatedWebPImage::StopInternal,
+                                               base::Unretained(this)));
 }
 
 void AnimatedWebPImage::AppendChunk(const uint8* data, size_t size) {
@@ -120,7 +126,7 @@
                                (background_color >> 0 & 0xff) / 255.0f);
 
     if (is_playing_) {
-      PlayInternal();
+      StartDecoding();
     }
   }
   frame_count_ = new_frame_count;
@@ -129,46 +135,75 @@
 AnimatedWebPImage::~AnimatedWebPImage() {
   TRACE_EVENT0("cobalt::loader::image",
                "AnimatedWebPImage::~AnimatedWebPImage()");
-  Stop();
-  bool is_playing = false;
-  {
-    base::AutoLock lock(lock_);
-    is_playing = is_playing_;
-  }
-  if (is_playing) {
+  if (task_runner_) {
+    Stop();
     task_runner_->WaitForFence();
   }
+
   WebPDemuxDelete(demux_);
 }
 
+void AnimatedWebPImage::PlayInternal() {
+  TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::PlayInternal()");
+  base::AutoLock lock(lock_);
+
+  if (is_playing_) {
+    return;
+  }
+  is_playing_ = true;
+
+  if (received_first_frame_) {
+    StartDecoding();
+  }
+}
+
 void AnimatedWebPImage::StopInternal() {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::StopInternal()");
   DCHECK(task_runner_->BelongsToCurrentThread());
   base::AutoLock lock(lock_);
+  if (!is_playing_) {
+    return;
+  }
+
   if (!decode_closure_.callback().is_null()) {
     is_playing_ = false;
     decode_closure_.Cancel();
   }
 }
 
-void AnimatedWebPImage::PlayInternal() {
-  TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::PlayInternal()");
+void AnimatedWebPImage::StartDecoding() {
+  TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::StartDecoding()");
+  lock_.AssertAcquired();
   current_frame_time_ = base::TimeTicks::Now();
-  task_runner_->PostTask(FROM_HERE, base::Bind(&AnimatedWebPImage::DecodeFrames,
-                                               base::Unretained(this)));
+  if (task_runner_->BelongsToCurrentThread()) {
+    DecodeFrames();
+  } else {
+    task_runner_->PostTask(FROM_HERE,
+                           base::Bind(&AnimatedWebPImage::LockAndDecodeFrames,
+                                      base::Unretained(this)));
+  }
+}
+
+void AnimatedWebPImage::LockAndDecodeFrames() {
+  TRACE_EVENT0("cobalt::loader::image",
+               "AnimatedWebPImage::LockAndDecodeFrames()");
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  base::AutoLock lock(lock_);
+  DecodeFrames();
 }
 
 void AnimatedWebPImage::DecodeFrames() {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeFrames()");
   TRACK_MEMORY_SCOPE("Rendering");
+  lock_.AssertAcquired();
   DCHECK(is_playing_ && received_first_frame_);
   DCHECK(task_runner_->BelongsToCurrentThread());
 
-  base::AutoLock lock(lock_);
-
   if (decode_closure_.callback().is_null()) {
     decode_closure_.Reset(
-        base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+        base::Bind(&AnimatedWebPImage::LockAndDecodeFrames,
+                   base::Unretained(this)));
   }
 
   if (AdvanceFrame()) {
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index 862b77e..34cf56b 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -64,14 +64,23 @@
  private:
   ~AnimatedWebPImage() override;
 
+  // Starts playback of the animated image, or sets a flag indicating that we
+  // would like to start playing as soon as we can.
+  void PlayInternal();
+
   // To be called the decoding thread, to cancel future decodings.
   void StopInternal();
 
-  void PlayInternal();
+  // Starts the process of decoding frames.  It assumes frames are available to
+  // decode.
+  void StartDecoding();
 
-  // Decodes all frames until current time.
+  // Decodes all frames until current time.  Assumes |lock_| is acquired.
   void DecodeFrames();
 
+  // Acquires |lock_| and calls DecodeFrames().
+  void LockAndDecodeFrames();
+
   // Decodes the frame with the given index, returns if it succeeded.
   bool DecodeOneFrame(int frame_index);
 
@@ -109,6 +118,11 @@
   scoped_refptr<render_tree::Image> current_canvas_;
   scoped_refptr<FrameProvider> frame_provider_;
   base::Lock lock_;
+
+  // Makes sure that the thread that sets the task_runner is always consistent.
+  // This is the thread sending Play()/Stop() calls, and is not necessarily
+  // the same thread that the task_runner itself is running on.
+  THREAD_CHECKER(task_runner_thread_checker_);
 };
 
 }  // namespace image
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc
index bd9e244..6e03611 100644
--- a/src/cobalt/loader/image/image_decoder_test.cc
+++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -68,8 +68,8 @@
   void ExpectCallWithError(const base::Optional<std::string>& error);
 
  protected:
-  ::testing::StrictMock<MockImageDecoderCallback> image_decoder_callback_;
   render_tree::ResourceProviderStub resource_provider_;
+  ::testing::StrictMock<MockImageDecoderCallback> image_decoder_callback_;
   std::unique_ptr<Decoder> image_decoder_;
 };
 
@@ -785,6 +785,9 @@
 
 // Test that we can properly decode animated WEBP image.
 TEST(ImageDecoderTest, DecodeAnimatedWEBPImage) {
+  base::Thread thread("AnimatedWebP test");
+  thread.Start();
+
   MockImageDecoder image_decoder;
   image_decoder.ExpectCallWithError(base::nullopt);
 
@@ -799,11 +802,8 @@
           image_decoder.image().get());
   ASSERT_TRUE(animated_webp_image);
 
-  base::Thread thread("AnimatedWebP test");
-  thread.Start();
   animated_webp_image->Play(thread.task_runner());
   animated_webp_image->Stop();
-  thread.Stop();
 
   // The image should contain the whole undecoded data from the file.
   EXPECT_EQ(4261474u, animated_webp_image->GetEstimatedSizeInBytes());
@@ -814,6 +814,9 @@
 
 // Test that we can properly decode animated WEBP image in multiple chunks.
 TEST(ImageDecoderTest, DecodeAnimatedWEBPImageWithMultipleChunks) {
+  base::Thread thread("AnimatedWebP test");
+  thread.Start();
+
   MockImageDecoder image_decoder;
   image_decoder.ExpectCallWithError(base::nullopt);
 
@@ -831,11 +834,8 @@
           image_decoder.image().get());
   ASSERT_TRUE(animated_webp_image);
 
-  base::Thread thread("AnimatedWebP test");
-  thread.Start();
   animated_webp_image->Play(thread.task_runner());
   animated_webp_image->Stop();
-  thread.Stop();
 
   // The image should contain the whole undecoded data from the file.
   EXPECT_EQ(4261474u, animated_webp_image->GetEstimatedSizeInBytes());
diff --git a/src/cobalt/loader/image/image_encoder.cc b/src/cobalt/loader/image/image_encoder.cc
index 7c4ed21..beebfa2 100644
--- a/src/cobalt/loader/image/image_encoder.cc
+++ b/src/cobalt/loader/image/image_encoder.cc
@@ -43,12 +43,22 @@
           kPitchSizeInBytes, &num_bytes);
       break;
     }
+
     case ImageFormat::kJPEG: {
+// jpeg_utils pulls in libjpeg-turbo as an additional dependency, which
+// increases the size of the cobalt binary. Because jpeg_utils is only used for
+// screencasting, which is only needed for non-gold builds, we can disable this
+// logic for gold builds.
+#if !defined(COBALT_BUILD_TYPE_GOLD)
       compressed_data = renderer::test::jpeg_utils::EncodeRGBAToBuffer(
           image_data, dimensions.width(), dimensions.height(),
           kPitchSizeInBytes, &num_bytes);
       break;
+#else
+      NOTREACHED();
+#endif
     }
+
     case ImageFormat::kWEBP:
       NOTIMPLEMENTED();
       return nullptr;
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index 912678a..cb1fbe5 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -106,7 +106,6 @@
         '<(DEPTH)/cobalt/loader/origin.gyp:origin',
         '<(DEPTH)/cobalt/network/network.gyp:network',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
-        '<(DEPTH)/cobalt/renderer/test/jpeg_utils/jpeg_utils.gyp:jpeg_utils',
         '<(DEPTH)/cobalt/renderer/test/png_utils/png_utils.gyp:png_utils',
         '<(DEPTH)/url/url.gyp:url',
         '<(DEPTH)/third_party/libjpeg/libjpeg.gyp:libjpeg',
@@ -115,6 +114,11 @@
         'embed_resources_as_header_files',
       ],
       'conditions': [
+        ['cobalt_config != "gold"', {
+          'dependencies': [
+            '<(DEPTH)/cobalt/renderer/test/jpeg_utils/jpeg_utils.gyp:jpeg_utils',
+          ],
+        }],
         ['enable_about_scheme == 1', {
           'defines': [ 'ENABLE_ABOUT_SCHEME' ],
           'sources': [
@@ -206,6 +210,7 @@
         '<(input_directory)/black_splash_screen.html',
         '<(input_directory)/cobalt_splash_screen.css',
         '<(input_directory)/cobalt_splash_screen.html',
+        '<(input_directory)/cobalt_word_logo_356.png',
         '<(input_directory)/dialog.css',
         '<(input_directory)/dialog.js',
         '<(input_directory)/equirectangular_40_40.msh',
diff --git a/src/cobalt/media/base/shell_audio_bus.cc b/src/cobalt/media/base/shell_audio_bus.cc
index fbeb07c..5140d01 100644
--- a/src/cobalt/media/base/shell_audio_bus.cc
+++ b/src/cobalt/media/base/shell_audio_bus.cc
@@ -362,25 +362,51 @@
 void ShellAudioBus::Mix(const ShellAudioBus& source,
                         const std::vector<float>& matrix) {
   DCHECK_EQ(channels() * source.channels(), matrix.size());
-  DCHECK_EQ(sample_type_, kFloat32);
-  DCHECK_EQ(source.sample_type_, kFloat32);
+  DCHECK_EQ(sample_type_, source.sample_type_);
+
   if (channels() * source.channels() != matrix.size() ||
-      sample_type_ != kFloat32 || source.sample_type_ != kFloat32) {
+      sample_type_ != source.sample_type_) {
     ZeroAllFrames();
     return;
   }
 
-  size_t frames = std::min(frames_, source.frames_);
-  for (size_t dest_channel = 0; dest_channel < channels_; ++dest_channel) {
-    for (size_t frame = 0; frame < frames; ++frame) {
-      float mixed_sample = 0.f;
-      for (size_t src_channel = 0; src_channel < source.channels_;
-           ++src_channel) {
-        mixed_sample += source.GetFloat32Sample(src_channel, frame) *
-                        matrix[dest_channel * source.channels_ + src_channel];
+  if (sample_type_ == kFloat32) {
+    size_t frames = std::min(frames_, source.frames_);
+    for (size_t dest_channel = 0; dest_channel < channels_; ++dest_channel) {
+      for (size_t frame = 0; frame < frames; ++frame) {
+        float mixed_sample = 0.f;
+        for (size_t src_channel = 0; src_channel < source.channels_;
+             ++src_channel) {
+          float val = source.GetFloat32Sample(src_channel, frame) *
+                      matrix[dest_channel * source.channels_ + src_channel];
+          mixed_sample += val;
+        }
+        mixed_sample += GetFloat32Sample(dest_channel, frame);
+        SetFloat32Sample(dest_channel, frame, mixed_sample);
       }
-      mixed_sample += GetFloat32Sample(dest_channel, frame);
-      SetFloat32Sample(dest_channel, frame, mixed_sample);
+    }
+  } else {
+    DCHECK_EQ(sample_type_, kInt16);
+    size_t frames = std::min(frames_, source.frames_);
+    for (size_t dest_channel = 0; dest_channel < channels_; ++dest_channel) {
+      for (size_t frame = 0; frame < frames; ++frame) {
+        int mixed_sample = 0;
+        for (size_t src_channel = 0; src_channel < source.channels_;
+             ++src_channel) {
+          mixed_sample += source.GetInt16Sample(src_channel, frame) *
+                          matrix[dest_channel * source.channels_ + src_channel];
+        }
+        mixed_sample += GetInt16Sample(dest_channel, frame);
+        if (mixed_sample > std::numeric_limits<int16>::max()) {
+          SetInt16Sample(dest_channel, frame,
+                         std::numeric_limits<int16>::max());
+        } else if (mixed_sample < std::numeric_limits<int16>::min()) {
+          SetInt16Sample(dest_channel, frame,
+                         std::numeric_limits<int16>::min());
+        } else {
+          SetInt16Sample(dest_channel, frame, mixed_sample);
+        }
+      }
     }
   }
 }
diff --git a/src/cobalt/media/base/shell_audio_bus.h b/src/cobalt/media/base/shell_audio_bus.h
index 405dab8..cdd4668 100644
--- a/src/cobalt/media/base/shell_audio_bus.h
+++ b/src/cobalt/media/base/shell_audio_bus.h
@@ -144,6 +144,10 @@
     DCHECK_EQ(sample_type_, kFloat32);
     *reinterpret_cast<float*>(GetSamplePtr(channel, frame)) = sample;
   }
+  void SetInt16Sample(size_t channel, size_t frame, int16 sample) {
+    DCHECK_EQ(sample_type_, kInt16);
+    *reinterpret_cast<int16*>(GetSamplePtr(channel, frame)) = sample;
+  }
   uint8* GetSamplePtr(size_t channel, size_t frame);
   const uint8* GetSamplePtr(size_t channel, size_t frame) const;
 
diff --git a/src/cobalt/renderer/rasterizer/skia/common.gyp b/src/cobalt/renderer/rasterizer/skia/common.gyp
index 5b07ed0..0bb0473 100644
--- a/src/cobalt/renderer/rasterizer/skia/common.gyp
+++ b/src/cobalt/renderer/rasterizer/skia/common.gyp
@@ -13,6 +13,9 @@
 # limitations under the License.
 
 {
+  'variables': {
+    'optimize_target_for_speed': 1,
+  },
   'targets': [
     {
       'target_name': 'common',
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h b/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h
index f25e8bf..0c3da7c 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h
@@ -207,9 +207,13 @@
 #define GR_MAX_OFFSCREEN_AA_DIM 512
 
 // Log the file and line number for assertions.
+#if defined(COBALT_BUILD_TYPE_GOLD)
+#define SkDebugf(...) (void)0
+#else
 #define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, false, __VA_ARGS__)
 SK_API void SkDebugf_FileLine(const char* file, int line, bool fatal,
                               const char* format, ...);
+#endif  // defined(COBALT_BUILD_TYPE_GOLD)
 
 // Marking the debug print as "fatal" will cause a debug break, so we don't need
 // a separate crash call here.
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
index 8dd1a1e..e61806a 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
@@ -3,9 +3,6 @@
 # found in the LICENSE file.
 
 {
-  'variables': {
-    'optimize_target_for_speed': 1,
-  },
   'targets': [
     {
       # This is the target that all Cobalt modules should depend on if they
@@ -24,6 +21,14 @@
       'export_dependent_settings': [
         'skia_library',
       ],
+      'conditions': [
+        # Skia should normally be optimized for speed. However, the direct-gles
+        # rasterizer rarely uses skia and normally caches its output anyway, so
+        # optimize skia for size in its case.
+        ['rasterizer_type != "direct-gles"', {
+          'variables': { 'optimize_target_for_speed': 1 },
+        }],
+      ],
     },
 
     {
@@ -35,6 +40,14 @@
         'skia_library.gypi',
         'skia_sksl.gypi',
       ],
+      'conditions': [
+        # Skia should normally be optimized for speed. However, the direct-gles
+        # rasterizer rarely uses skia and normally caches its output anyway, so
+        # optimize skia for size in its case.
+        ['rasterizer_type != "direct-gles"', {
+          'variables': { 'optimize_target_for_speed': 1 },
+        }],
+      ],
     },
 
     {
diff --git a/src/cobalt/renderer/rasterizer/skia/software_rasterizer.gyp b/src/cobalt/renderer/rasterizer/skia/software_rasterizer.gyp
index 9ceba1d..be89a66 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/skia/software_rasterizer.gyp
@@ -13,6 +13,9 @@
 # limitations under the License.
 
 {
+  'variables': {
+    'optimize_target_for_speed': 1,
+  },
   'targets': [
     {
       'target_name': 'software_rasterizer',
@@ -38,4 +41,4 @@
       ],
     },
   ],
-}
\ No newline at end of file
+}
diff --git a/src/cobalt/script/v8c/common_v8c_bindings_code.cc b/src/cobalt/script/v8c/common_v8c_bindings_code.cc
new file mode 100644
index 0000000..e79443d
--- /dev/null
+++ b/src/cobalt/script/v8c/common_v8c_bindings_code.cc
@@ -0,0 +1,111 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/script/v8c/common_v8c_bindings_code.h"
+
+#include "cobalt/script/v8c/conversion_helpers.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
+#include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/wrapper_private.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace script {
+namespace v8c {
+namespace shared_bindings {
+// All code here are intended to be used by v8c bindings code.
+
+bool object_implements_interface(
+    v8::Local<v8::FunctionTemplate> function_template, v8::Isolate* isolate,
+    v8::Local<v8::Object> object) {
+  V8cGlobalEnvironment* global_environment =
+      V8cGlobalEnvironment::GetFromIsolate(isolate);
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!WrapperPrivate::HasWrapperPrivate(object) ||
+      !function_template->HasInstance(object)) {
+    V8cExceptionState exception(isolate);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  return true;
+}
+
+void set_intrinsic_error_prototype_interface_template(
+    v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> function_template) {
+  // 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);
+}
+
+void set_property_for_nonconstructor_attribute(
+    v8::Isolate* isolate, bool configurable, bool has_setter, bool on_interface,
+    bool on_instance, v8::Local<v8::FunctionTemplate> function_template,
+    v8::Local<v8::ObjectTemplate> instance_template,
+    v8::Local<v8::ObjectTemplate> prototype_template, const char* idl_name,
+    v8::FunctionCallback IDLGetter, v8::FunctionCallback IDLSetter) {
+  // The name of the property is the identifier of the attribute.
+  v8::Local<v8::String> name = NewInternalString(isolate, 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;
+  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.
+  v8::Local<v8::FunctionTemplate> getter =
+      v8::FunctionTemplate::New(isolate, IDLGetter);
+  v8::Local<v8::FunctionTemplate> setter;
+  if (has_setter) {
+    setter = v8::FunctionTemplate::New(isolate, IDLSetter);
+  }
+
+  // The location of the property is determined as follows:
+  if (on_interface) {
+    // 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->SetAccessorProperty(name, getter, setter, attributes);
+  } else if (on_instance) {
+    // 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->SetAccessorProperty(name, getter, setter, attributes);
+  } else {
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessorProperty(name, getter, setter, attributes);
+  }
+}
+
+}  // namespace shared_bindings
+}  // namespace v8c
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/v8c/common_v8c_bindings_code.h b/src/cobalt/script/v8c/common_v8c_bindings_code.h
new file mode 100644
index 0000000..3ef81a6
--- /dev/null
+++ b/src/cobalt/script/v8c/common_v8c_bindings_code.h
@@ -0,0 +1,163 @@
+// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SCRIPT_V8C_COMMON_V8C_BINDINGS_CODE_H_
+#define COBALT_SCRIPT_V8C_COMMON_V8C_BINDINGS_CODE_H_
+
+#include "base/logging.h"
+#include "cobalt/script/exception_message.h"
+#include "cobalt/script/v8c/conversion_helpers.h"
+#include "cobalt/script/v8c/helpers.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
+#include "cobalt/script/v8c/wrapper_private.h"
+#include "v8/include/v8.h"
+
+namespace cobalt {
+namespace script {
+namespace v8c {
+namespace shared_bindings {
+// All code here are intended to be used by v8c bindings code only.
+
+bool object_implements_interface(
+    v8::Local<v8::FunctionTemplate> function_template, v8::Isolate* isolate,
+    v8::Local<v8::Object> object);
+
+
+template <typename ImplClass, typename BindingClass>
+ImplClass* get_impl_class_from_info(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.Holder();
+  V8cExceptionState exception_state(isolate);
+
+  if (!object_implements_interface(BindingClass::GetTemplate(isolate), isolate,
+                                   object)) {
+    return nullptr;
+  }
+
+  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 nullptr;
+  }
+
+  ImplClass* impl = wrapper_private->wrappable<ImplClass>().get();
+  if (!impl) {
+    exception_state.SetSimpleException(cobalt::script::kStringifierProblem);
+    NOTREACHED();
+    return nullptr;
+  }
+  return impl;
+}
+
+void set_intrinsic_error_prototype_interface_template(
+    v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> function_template);
+
+void set_property_for_nonconstructor_attribute(
+    v8::Isolate* isolate, bool configurable, bool has_setter, bool on_interface,
+    bool on_instance, v8::Local<v8::FunctionTemplate> function_template,
+    v8::Local<v8::ObjectTemplate> instance_template,
+    v8::Local<v8::ObjectTemplate> prototype_template, const char* idl_name,
+    v8::FunctionCallback IDLGetter = v8::FunctionCallback(),
+    v8::FunctionCallback IDLSetter = v8::FunctionCallback());
+
+template <class ImplClass>
+ImplClass* get_impl_from_object(v8::Local<v8::Object> object) {
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return nullptr;
+  }
+  return wrapper_private->wrappable<ImplClass>().get();
+}
+
+template <class ImplClass, class BindingClass>
+void AttributeGetterImpl(
+    const v8::FunctionCallbackInfo<v8::Value>& info, bool is_static,
+    bool is_void_function,
+    void (*get_cobalt_value)(v8::Isolate*, ImplClass*,
+                             cobalt::script::ExceptionState&,
+                             v8::Local<v8::Value>&)) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.Holder();
+
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+  ImplClass* impl = nullptr;
+  if (!is_static) {
+    if (!script::v8c::shared_bindings::object_implements_interface(
+            BindingClass::GetTemplate(isolate), isolate, object)) {
+      return;
+    }
+    impl = get_impl_from_object<ImplClass>(object);
+    if (!impl) {
+      return;
+    }
+  }
+
+  if (is_void_function) {
+    // Call the actual function
+    get_cobalt_value(isolate, impl, exception_state, result_value);
+    result_value = v8::Undefined(isolate);
+  } else {
+    if (!exception_state.is_exception_set()) {
+      get_cobalt_value(isolate, impl, exception_state, result_value);
+    }
+  }
+
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+template <class ImplClass, class BindingClass>
+void AttributeSetterImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
+                         bool is_static, bool is_void_function,
+                         void (*set_attribute_implementation)(
+                             v8::Isolate*, ImplClass*, V8cExceptionState&,
+                             v8::Local<v8::Value>&, v8::Local<v8::Value>)) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.Holder();
+  v8::Local<v8::Value> v8_value = info[0];
+
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+  ImplClass* impl = nullptr;
+  if (!is_static) {
+    if (!script::v8c::shared_bindings::object_implements_interface(
+            BindingClass::GetTemplate(isolate), isolate, object)) {
+      return;
+    }
+    impl = get_impl_from_object<ImplClass>(object);
+    if (!impl) {
+      return;
+    }
+  }
+
+  set_attribute_implementation(isolate, impl, exception_state, result_value,
+                               v8_value);
+}
+
+}  // namespace shared_bindings
+}  // namespace v8c
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_V8C_COMMON_V8C_BINDINGS_CODE_H_
diff --git a/src/cobalt/script/v8c/v8c.gyp b/src/cobalt/script/v8c/v8c.gyp
index 9ebae66..9951691 100644
--- a/src/cobalt/script/v8c/v8c.gyp
+++ b/src/cobalt/script/v8c/v8c.gyp
@@ -23,6 +23,8 @@
         'callback_function_conversion.h',
         'cobalt_platform.cc',
         'cobalt_platform.h',
+        'common_v8c_bindings_code.cc',
+        'common_v8c_bindings_code.h',
         'conversion_helpers.cc',
         'conversion_helpers.h',
         'entry_scope.h',
diff --git a/src/cobalt/updater/BRANDING b/src/cobalt/updater/BRANDING
new file mode 100644
index 0000000..49eb4d5
--- /dev/null
+++ b/src/cobalt/updater/BRANDING
@@ -0,0 +1,4 @@
+COMPANY_FULLNAME=Google LLC
+COMPANY_SHORTNAME=Google
+PRODUCT_FULLNAME=GoogleUpdater
+COPYRIGHT=Copyright 2019 The Chromium Authors. All rights reserved.
diff --git a/src/cobalt/updater/BUILD.gn b/src/cobalt/updater/BUILD.gn
new file mode 100644
index 0000000..6de5b53
--- /dev/null
+++ b/src/cobalt/updater/BUILD.gn
@@ -0,0 +1,102 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chrome_build.gni")
+import("//build/config/sanitizers/sanitizers.gni")
+import("//build/util/process_version.gni")
+import("//testing/test.gni")
+
+group("updater") {
+  if (is_win) {
+    deps = [
+      "//chrome/updater/win",
+    ]
+  }
+  if (is_mac) {
+    deps = [
+      "//chrome/updater/mac",
+    ]
+  }
+}
+
+# Conditional build is needed, otherwise the analyze script on Linux
+# requires all targets and it is going to include the targets below.
+if (is_win || is_mac) {
+  source_set("common") {
+    sources = [
+      "configurator.cc",
+      "configurator.h",
+      "crash_client.cc",
+      "crash_client.h",
+      "crash_reporter.cc",
+      "crash_reporter.h",
+      "installer.cc",
+      "installer.h",
+      "patcher.cc",
+      "patcher.h",
+      "prefs.cc",
+      "prefs.h",
+      "unzipper.cc",
+      "unzipper.h",
+      "updater.cc",
+      "updater.h",
+      "updater_constants.cc",
+      "updater_constants.h",
+      "util.cc",
+      "util.h",
+    ]
+
+    deps = [
+      ":version_header",
+      "//base",
+      "//components/crash/core/common:crash_key",
+      "//components/prefs",
+      "//components/update_client",
+      "//components/version_info",
+      "//courgette",
+      "//third_party/crashpad/crashpad/client",
+      "//third_party/crashpad/crashpad/handler",
+      "//third_party/zlib/google:zip",
+      "//url",
+    ]
+  }
+
+  process_version("version_header") {
+    sources = [
+      "//chrome/VERSION",
+      "BRANDING",
+    ]
+    template_file = "updater_version.h.in"
+    output = "$target_gen_dir/updater_version.h"
+  }
+
+  source_set("updater_tests") {
+    testonly = true
+
+    sources = [
+      "updater_unittest.cc",
+    ]
+
+    deps = [
+      ":common",
+      ":updater",
+      "//base/test:test_support",
+      "//testing/gtest",
+    ]
+
+    if (is_win) {
+      deps += [ "//chrome/updater/win:updater_tests" ]
+
+      data_deps = [
+        "//chrome/updater/win:updater",
+      ]
+    }
+
+    if (is_mac) {
+      data_deps = [
+        "//chrome/updater/mac:updater",
+      ]
+    }
+  }
+}
diff --git a/src/cobalt/updater/README.md b/src/cobalt/updater/README.md
new file mode 100644
index 0000000..67d0c46
--- /dev/null
+++ b/src/cobalt/updater/README.md
@@ -0,0 +1,4 @@
+This is the code for the client updater that will soon be used by
+desktop Chrome, on macOS and Windows.
+
+Please join chrome-updates-dev@chromium.org for topics related to this project.
diff --git a/src/cobalt/updater/configurator.cc b/src/cobalt/updater/configurator.cc
new file mode 100644
index 0000000..8e372ad
--- /dev/null
+++ b/src/cobalt/updater/configurator.cc
@@ -0,0 +1,167 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/configurator.h"
+
+#include <utility>
+#include "base/version.h"
+#include "build/build_config.h"
+#include "chrome/updater/patcher.h"
+#include "chrome/updater/prefs.h"
+#include "chrome/updater/unzipper.h"
+#include "chrome/updater/updater_constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/update_client/network.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/protocol_handler.h"
+#include "components/update_client/unzipper.h"
+#include "components/version_info/version_info.h"
+#include "url/gurl.h"
+
+#if defined(OS_WIN)
+#include "chrome/updater/win/net/network.h"
+#endif
+
+namespace {
+
+// Default time constants.
+const int kDelayOneMinute = 60;
+const int kDelayOneHour = kDelayOneMinute * 60;
+
+}  // namespace
+
+namespace updater {
+
+Configurator::Configurator()
+    : pref_service_(CreatePrefService()),
+      unzip_factory_(base::MakeRefCounted<UnzipperFactory>()),
+      patch_factory_(base::MakeRefCounted<PatcherFactory>()) {}
+Configurator::~Configurator() = default;
+
+int Configurator::InitialDelay() const {
+  return 0;
+}
+
+int Configurator::NextCheckDelay() const {
+  return 5 * kDelayOneHour;
+}
+
+int Configurator::OnDemandDelay() const {
+  return 0;
+}
+
+int Configurator::UpdateDelay() const {
+  return 0;
+}
+
+std::vector<GURL> Configurator::UpdateUrl() const {
+  return std::vector<GURL>{GURL(kUpdaterJSONDefaultUrl)};
+}
+
+std::vector<GURL> Configurator::PingUrl() const {
+  return UpdateUrl();
+}
+
+std::string Configurator::GetProdId() const {
+  return "updater";
+}
+
+base::Version Configurator::GetBrowserVersion() const {
+  return version_info::GetVersion();
+}
+
+std::string Configurator::GetChannel() const {
+  return {};
+}
+
+std::string Configurator::GetBrand() const {
+  return {};
+}
+
+std::string Configurator::GetLang() const {
+  return "en-US";
+}
+
+std::string Configurator::GetOSLongName() const {
+  return version_info::GetOSType();
+}
+
+base::flat_map<std::string, std::string> Configurator::ExtraRequestParams()
+    const {
+  return {{"testrequest", "1"}, {"testsource", "dev"}};
+}
+
+std::string Configurator::GetDownloadPreference() const {
+  return {};
+}
+
+scoped_refptr<update_client::NetworkFetcherFactory>
+Configurator::GetNetworkFetcherFactory() {
+#if defined(OS_WIN)
+  if (!network_fetcher_factory_) {
+    network_fetcher_factory_ = base::MakeRefCounted<NetworkFetcherFactory>();
+  }
+  return network_fetcher_factory_;
+#else
+  return nullptr;
+#endif
+}
+
+scoped_refptr<update_client::UnzipperFactory>
+Configurator::GetUnzipperFactory() {
+  return unzip_factory_;
+}
+
+scoped_refptr<update_client::PatcherFactory> Configurator::GetPatcherFactory() {
+  return patch_factory_;
+}
+
+bool Configurator::EnabledDeltas() const {
+  return false;
+}
+
+bool Configurator::EnabledComponentUpdates() const {
+  return false;
+}
+
+bool Configurator::EnabledBackgroundDownloader() const {
+  return false;
+}
+
+bool Configurator::EnabledCupSigning() const {
+  return true;
+}
+
+PrefService* Configurator::GetPrefService() const {
+  return pref_service_.get();
+}
+
+update_client::ActivityDataService* Configurator::GetActivityDataService()
+    const {
+  return nullptr;
+}
+
+bool Configurator::IsPerUserInstall() const {
+  return true;
+}
+
+std::vector<uint8_t> Configurator::GetRunActionKeyHash() const {
+  return {};
+}
+
+std::string Configurator::GetAppGuid() const {
+  return {};
+}
+
+std::unique_ptr<update_client::ProtocolHandlerFactory>
+Configurator::GetProtocolHandlerFactory() const {
+  return std::make_unique<update_client::ProtocolHandlerFactoryJSON>();
+}
+
+update_client::RecoveryCRXElevator Configurator::GetRecoveryCRXElevator()
+    const {
+  return {};
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/configurator.h b/src/cobalt/updater/configurator.h
new file mode 100644
index 0000000..d5bbd3c
--- /dev/null
+++ b/src/cobalt/updater/configurator.h
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_CONFIGURATOR_H_
+#define CHROME_UPDATER_CONFIGURATOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/configurator.h"
+
+class GURL;
+class PrefService;
+
+namespace base {
+class Version;
+}  // namespace base
+
+namespace update_client {
+class ActivityDataService;
+class NetworkFetcherFactory;
+class ProtocolHandlerFactory;
+}  // namespace update_client
+
+namespace updater {
+
+class Configurator : public update_client::Configurator {
+ public:
+  Configurator();
+
+  // Configurator for update_client::Configurator.
+  int InitialDelay() const override;
+  int NextCheckDelay() const override;
+  int OnDemandDelay() const override;
+  int UpdateDelay() const override;
+  std::vector<GURL> UpdateUrl() const override;
+  std::vector<GURL> PingUrl() const override;
+  std::string GetProdId() const override;
+  base::Version GetBrowserVersion() const override;
+  std::string GetChannel() const override;
+  std::string GetBrand() const override;
+  std::string GetLang() const override;
+  std::string GetOSLongName() const override;
+  base::flat_map<std::string, std::string> ExtraRequestParams() const override;
+  std::string GetDownloadPreference() const override;
+  scoped_refptr<update_client::NetworkFetcherFactory> GetNetworkFetcherFactory()
+      override;
+  scoped_refptr<update_client::UnzipperFactory> GetUnzipperFactory() override;
+  scoped_refptr<update_client::PatcherFactory> GetPatcherFactory() override;
+  bool EnabledDeltas() const override;
+  bool EnabledComponentUpdates() const override;
+  bool EnabledBackgroundDownloader() const override;
+  bool EnabledCupSigning() const override;
+  PrefService* GetPrefService() const override;
+  update_client::ActivityDataService* GetActivityDataService() const override;
+  bool IsPerUserInstall() const override;
+  std::vector<uint8_t> GetRunActionKeyHash() const override;
+  std::string GetAppGuid() const override;
+  std::unique_ptr<update_client::ProtocolHandlerFactory>
+  GetProtocolHandlerFactory() const override;
+  update_client::RecoveryCRXElevator GetRecoveryCRXElevator() const override;
+
+ private:
+  friend class base::RefCountedThreadSafe<Configurator>;
+  ~Configurator() override;
+
+  std::unique_ptr<PrefService> pref_service_;
+  scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_;
+  scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
+  scoped_refptr<update_client::PatcherFactory> patch_factory_;
+  DISALLOW_COPY_AND_ASSIGN(Configurator);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_CONFIGURATOR_H_
diff --git a/src/cobalt/updater/crash_client.cc b/src/cobalt/updater/crash_client.cc
new file mode 100644
index 0000000..d1d9fc0
--- /dev/null
+++ b/src/cobalt/updater/crash_client.cc
@@ -0,0 +1,159 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/crash_client.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+#include "chrome/updater/util.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/client/prune_crash_reports.h"
+#include "third_party/crashpad/crashpad/client/settings.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "base/win/wrapped_window_proc.h"
+#endif
+
+namespace {
+
+#if defined(OS_WIN)
+
+int __cdecl HandleWinProcException(EXCEPTION_POINTERS* exception_pointers) {
+  crashpad::CrashpadClient::DumpAndCrash(exception_pointers);
+  return EXCEPTION_CONTINUE_SEARCH;
+}
+
+#endif
+
+}  // namespace
+
+namespace updater {
+
+CrashClient::CrashClient() = default;
+CrashClient::~CrashClient() = default;
+
+// static
+CrashClient* CrashClient::GetInstance() {
+  static base::NoDestructor<CrashClient> crash_client;
+  return crash_client.get();
+}
+
+bool CrashClient::InitializeDatabaseOnly() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::FilePath handler_path;
+  base::PathService::Get(base::FILE_EXE, &handler_path);
+
+  base::FilePath database_path;
+  if (!GetProductDirectory(&database_path)) {
+    LOG(ERROR) << "Failed to get the database path.";
+    return false;
+  }
+
+  database_ = crashpad::CrashReportDatabase::Initialize(database_path);
+  if (!database_) {
+    LOG(ERROR) << "Failed to initialize Crashpad database.";
+    return false;
+  }
+
+  return true;
+}
+
+bool CrashClient::InitializeCrashReporting() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!InitializeDatabaseOnly())
+    return false;
+
+#if defined(OS_WIN)
+  // Catch exceptions thrown from a window procedure.
+  base::win::WinProcExceptionFilter exception_filter =
+      base::win::SetWinProcExceptionFilter(&HandleWinProcException);
+  LOG_IF(DFATAL, exception_filter) << "Exception filter already present";
+#endif  // OS_WIN
+
+  std::vector<crashpad::CrashReportDatabase::Report> reports_completed;
+  const crashpad::CrashReportDatabase::OperationStatus status_completed =
+      database_->GetCompletedReports(&reports_completed);
+  if (status_completed == crashpad::CrashReportDatabase::kNoError) {
+    VLOG(1) << "Found " << reports_completed.size()
+            << " completed crash reports";
+    for (const auto& report : reports_completed) {
+      VLOG(1) << "Crash since last run: ID \"" << report.id << "\", created at "
+              << report.creation_time << ", " << report.upload_attempts
+              << " upload attempts, file path \"" << report.file_path
+              << "\", unique ID \"" << report.uuid.ToString()
+              << "\"; uploaded: " << (report.uploaded ? "yes" : "no");
+    }
+  } else {
+    LOG(ERROR) << "Failed to fetch completed crash reports: "
+               << status_completed;
+  }
+
+  std::vector<crashpad::CrashReportDatabase::Report> reports_pending;
+  const crashpad::CrashReportDatabase::OperationStatus status_pending =
+      database_->GetPendingReports(&reports_pending);
+  if (status_pending == crashpad::CrashReportDatabase::kNoError) {
+    VLOG(1) << "Found " << reports_pending.size() << " pending crash reports";
+    for (const auto& report : reports_pending) {
+      VLOG(1) << "Crash since last run: (pending), created at "
+              << report.creation_time << ", " << report.upload_attempts
+              << " upload attempts, file path \"" << report.file_path
+              << "\", unique ID \"" << report.uuid.ToString() << "\"";
+    }
+  } else {
+    LOG(ERROR) << "Failed to fetch pending crash reports: " << status_pending;
+  }
+
+  // TODO(sorin): fix before shipping to users, crbug.com/940098.
+  crashpad::Settings* crashpad_settings = database_->GetSettings();
+  DCHECK(crashpad_settings);
+  crashpad_settings->SetUploadsEnabled(true);
+
+  return true;
+}
+
+// static
+std::string CrashClient::GetClientId() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(GetInstance()->sequence_checker_);
+  DCHECK(GetInstance()->database_) << "Crash reporting not initialized";
+  crashpad::Settings* settings = GetInstance()->database_->GetSettings();
+  DCHECK(settings);
+
+  crashpad::UUID uuid;
+  if (!settings->GetClientID(&uuid)) {
+    LOG(ERROR) << "Unable to retrieve client ID from Crashpad database";
+    return {};
+  }
+
+  std::string uuid_string = uuid.ToString();
+  base::ReplaceSubstringsAfterOffset(&uuid_string, 0, "-", "");
+  return uuid_string;
+}
+
+// static
+bool CrashClient::IsUploadEnabled() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(GetInstance()->sequence_checker_);
+  DCHECK(GetInstance()->database_) << "Crash reporting not initialized";
+  crashpad::Settings* settings = GetInstance()->database_->GetSettings();
+  DCHECK(settings);
+
+  bool upload_enabled = false;
+  if (!settings->GetUploadsEnabled(&upload_enabled)) {
+    LOG(ERROR) << "Unable to verify if crash uploads are enabled or not";
+    return false;
+  }
+  return upload_enabled;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/crash_client.h b/src/cobalt/updater/crash_client.h
new file mode 100644
index 0000000..c5fc117
--- /dev/null
+++ b/src/cobalt/updater/crash_client.h
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_CRASH_CLIENT_H_
+#define CHROME_UPDATER_CRASH_CLIENT_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+
+namespace base {
+template <typename T>
+class NoDestructor;
+}  // namespace base
+
+namespace crashpad {
+class CrashReportDatabase;
+}  // namespace crashpad
+
+namespace updater {
+
+// This class manages interaction with the crash reporter.
+class CrashClient {
+ public:
+  static CrashClient* GetInstance();
+
+  // Retrieves the current guid associated with crashes. The value may be empty
+  // if no guid is associated.
+  static std::string GetClientId();
+
+  // Returns true if the upload of crashes is enabled.
+  static bool IsUploadEnabled();
+
+  // Initializes collection and upload of crash reports.
+  bool InitializeCrashReporting();
+
+  // Initializes the crash database only. Used in the crash reporter, which
+  // cannot connect to itself to upload its own crashes.
+  bool InitializeDatabaseOnly();
+
+  crashpad::CrashReportDatabase* database() { return database_.get(); }
+
+ private:
+  friend class base::NoDestructor<CrashClient>;
+
+  CrashClient();
+  ~CrashClient();
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  std::unique_ptr<crashpad::CrashReportDatabase> database_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrashClient);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_CRASH_CLIENT_H_
diff --git a/src/cobalt/updater/crash_reporter.cc b/src/cobalt/updater/crash_reporter.cc
new file mode 100644
index 0000000..41d7138
--- /dev/null
+++ b/src/cobalt/updater/crash_reporter.cc
@@ -0,0 +1,146 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/crash_reporter.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/stl_util.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/updater_version.h"
+#include "chrome/updater/util.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/handler/handler_main.h"
+
+namespace {
+
+// True if the current process is connected to a crash handler process.
+bool g_is_connected_to_crash_handler = false;
+
+crashpad::CrashpadClient* GetCrashpadClient() {
+  static auto* crashpad_client = new crashpad::CrashpadClient();
+  return crashpad_client;
+}
+
+void RemoveSwitchIfExisting(const char* switch_to_remove,
+                            std::vector<base::CommandLine::StringType>* argv) {
+  const std::string pattern = base::StrCat({"--", switch_to_remove});
+  auto matches_switch =
+      [&pattern](const base::CommandLine::StringType& argument) -> bool {
+#if defined(OS_WIN)
+    return base::StartsWith(argument, base::UTF8ToUTF16(pattern),
+                            base::CompareCase::SENSITIVE);
+#else
+    return base::StartsWith(argument, pattern, base::CompareCase::SENSITIVE);
+#endif  // OS_WIN
+  };
+  base::EraseIf(*argv, matches_switch);
+}
+
+}  // namespace
+
+namespace updater {
+
+void StartCrashReporter(const std::string& version) {
+  static bool started = false;
+  DCHECK(!started);
+  started = true;
+
+  base::FilePath handler_path;
+  base::PathService::Get(base::FILE_EXE, &handler_path);
+
+  base::FilePath database_path;
+  if (!GetProductDirectory(&database_path)) {
+    LOG(DFATAL) << "Failed to get the database path.";
+    return;
+  }
+
+  std::map<std::string, std::string> annotations;  // Crash keys.
+  annotations["ver"] = version;
+  annotations["prod"] = PRODUCT_FULLNAME_STRING;
+
+  std::vector<std::string> arguments;
+  arguments.push_back(base::StrCat({"--", kCrashHandlerSwitch}));
+
+  crashpad::CrashpadClient* client = GetCrashpadClient();
+  if (!client->StartHandler(handler_path, database_path,
+                            /*metrics_dir=*/base::FilePath(),
+                            kCrashStagingUploadURL, annotations, arguments,
+                            /*restartable=*/true,
+                            /*asynchronous_start=*/false)) {
+    LOG(DFATAL) << "Failed to start handler.";
+    return;
+  }
+
+  g_is_connected_to_crash_handler = true;
+  VLOG(1) << "Crash handler launched and ready.";
+}
+
+int CrashReporterMain() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  DCHECK(command_line->HasSwitch(kCrashHandlerSwitch));
+
+  // Disable rate-limiting until this is fixed:
+  //   https://bugs.chromium.org/p/crashpad/issues/detail?id=23
+  command_line->AppendSwitch(kNoRateLimitSwitch);
+
+  std::vector<base::CommandLine::StringType> argv = command_line->argv();
+
+  // Because of https://bugs.chromium.org/p/crashpad/issues/detail?id=82,
+  // Crashpad fails on the presence of flags it doesn't handle.
+  RemoveSwitchIfExisting(kCrashHandlerSwitch, &argv);
+
+  // |storage| must be declared before |argv_as_utf8|, to ensure it outlives
+  // |argv_as_utf8|, which will hold pointers into |storage|.
+  std::vector<std::string> storage;
+  std::unique_ptr<char*[]> argv_as_utf8(new char*[argv.size() + 1]);
+  storage.reserve(argv.size());
+  for (size_t i = 0; i < argv.size(); ++i) {
+#if defined(OS_WIN)
+    storage.push_back(base::UTF16ToUTF8(argv[i]));
+#else
+    storage.push_back(argv[i]);
+#endif
+    argv_as_utf8[i] = &storage[i][0];
+  }
+  argv_as_utf8[argv.size()] = nullptr;
+
+  return crashpad::HandlerMain(static_cast<int>(argv.size()),
+                               argv_as_utf8.get(),
+                               /*user_stream_sources=*/nullptr);
+}
+
+#if defined(OS_WIN)
+
+base::string16 GetCrashReporterIPCPipeName() {
+  return g_is_connected_to_crash_handler
+             ? GetCrashpadClient()->GetHandlerIPCPipe()
+             : base::string16();
+}
+
+void UseCrashReporter(const base::string16& ipc_pipe_name) {
+  DCHECK(!ipc_pipe_name.empty());
+  crashpad::CrashpadClient* crashpad_client = GetCrashpadClient();
+  if (!crashpad_client->SetHandlerIPCPipe(ipc_pipe_name)) {
+    LOG(DFATAL) << "Failed to set handler IPC pipe name: " << ipc_pipe_name;
+    return;
+  }
+
+  g_is_connected_to_crash_handler = true;
+  VLOG(1) << "Crash handler is ready.";
+}
+
+#endif  // OS_WIN
+
+}  // namespace updater
diff --git a/src/cobalt/updater/crash_reporter.h b/src/cobalt/updater/crash_reporter.h
new file mode 100644
index 0000000..82b53fd
--- /dev/null
+++ b/src/cobalt/updater/crash_reporter.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_CRASH_REPORTER_H_
+#define CHROME_UPDATER_CRASH_REPORTER_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+
+namespace updater {
+
+// Starts a new instance of this executable running as the crash reporter
+// process.
+void StartCrashReporter(const std::string& version);
+
+// Runs the crash reporter message loop within the current process. On return,
+// the current process should exit.
+int CrashReporterMain();
+
+#if defined(OS_WIN)
+
+// Returns the name of the IPC pipe that is used to communicate with the
+// crash reporter process, or an empty string if the current process is
+// not connected to a crash reporter process.
+base::string16 GetCrashReporterIPCPipeName();
+
+// Uses the crash reporter with the specified |ipc_pipe_name|, instead of
+// starting a new crash reporter process.
+void UseCrashReporter(const base::string16& ipc_pipe_name);
+
+#endif  // OS_WIN
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_CRASH_REPORTER_H_
diff --git a/src/cobalt/updater/installer.cc b/src/cobalt/updater/installer.cc
new file mode 100644
index 0000000..904c9ed
--- /dev/null
+++ b/src/cobalt/updater/installer.cc
@@ -0,0 +1,189 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/installer.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/util.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+
+namespace updater {
+
+namespace {
+
+// Version "0" corresponds to no installed version.
+const char kNullVersion[] = "0.0.0.0";
+
+// Returns the full path to the installation directory for the application
+// identified by the |crx_id|.
+base::FilePath GetAppInstallDir(const std::string& crx_id) {
+  base::FilePath app_install_dir;
+  if (GetProductDirectory(&app_install_dir)) {
+    app_install_dir = app_install_dir.AppendASCII(kAppsDir);
+    app_install_dir = app_install_dir.AppendASCII(crx_id);
+  }
+  return app_install_dir;
+}
+
+}  // namespace
+
+Installer::InstallInfo::InstallInfo() : version(kNullVersion) {}
+Installer::InstallInfo::~InstallInfo() = default;
+
+Installer::Installer(const std::vector<uint8_t>& pk_hash)
+    : pk_hash_(pk_hash),
+      crx_id_(update_client::GetCrxIdFromPublicKeyHash(pk_hash)),
+      install_info_(std::make_unique<InstallInfo>()) {}
+
+Installer::~Installer() = default;
+
+update_client::CrxComponent Installer::MakeCrxComponent() {
+  update_client::CrxComponent component;
+  component.installer = scoped_refptr<Installer>(this);
+  component.requires_network_encryption = false;
+  component.crx_format_requirement =
+      crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF;
+  component.pk_hash = pk_hash_;
+  component.name = crx_id_;
+  component.version = install_info_->version;
+  component.fingerprint = install_info_->fingerprint;
+  return component;
+}
+
+void Installer::FindInstallOfApp() {
+  VLOG(1) << __func__ << " for " << crx_id_;
+
+  const base::FilePath app_install_dir = GetAppInstallDir(crx_id_);
+  if (app_install_dir.empty() || !base::PathExists(app_install_dir)) {
+    install_info_ = std::make_unique<InstallInfo>();
+    return;
+  }
+
+  base::Version latest_version(kNullVersion);
+  base::FilePath latest_path;
+  std::vector<base::FilePath> older_paths;
+  base::FileEnumerator file_enumerator(app_install_dir, false,
+                                       base::FileEnumerator::DIRECTORIES);
+  for (auto path = file_enumerator.Next(); !path.value().empty();
+       path = file_enumerator.Next()) {
+    const base::Version version(path.BaseName().MaybeAsASCII());
+
+    // Ignore folders that don't have valid version names.
+    if (!version.IsValid())
+      continue;
+
+    // The |version| not newer than the latest found version is marked for
+    // removal. |kNullVersion| is also removed.
+    if (version.CompareTo(latest_version) <= 0) {
+      older_paths.push_back(path);
+      continue;
+    }
+
+    // New valid |version| folder found.
+    if (!latest_path.empty())
+      older_paths.push_back(latest_path);
+
+    latest_version = version;
+    latest_path = path;
+  }
+
+  install_info_->version = latest_version;
+  install_info_->install_dir = latest_path;
+  install_info_->manifest = update_client::ReadManifest(latest_path);
+  base::ReadFileToString(latest_path.AppendASCII("manifest.fingerprint"),
+                         &install_info_->fingerprint);
+
+  for (const auto& older_path : older_paths)
+    base::DeleteFile(older_path, true);
+}
+
+Installer::Result Installer::InstallHelper(const base::FilePath& unpack_path) {
+  auto local_manifest = update_client::ReadManifest(unpack_path);
+  if (!local_manifest)
+    return Result(update_client::InstallError::BAD_MANIFEST);
+
+  std::string version_ascii;
+  local_manifest->GetStringASCII("version", &version_ascii);
+  const base::Version manifest_version(version_ascii);
+
+  VLOG(1) << "Installed version=" << install_info_->version.GetString()
+          << ", installing version=" << manifest_version.GetString();
+
+  if (!manifest_version.IsValid())
+    return Result(update_client::InstallError::INVALID_VERSION);
+
+  if (install_info_->version.CompareTo(manifest_version) > 0)
+    return Result(update_client::InstallError::VERSION_NOT_UPGRADED);
+
+  const base::FilePath app_install_dir = GetAppInstallDir(crx_id_);
+  if (app_install_dir.empty())
+    return Result(update_client::InstallError::NO_DIR_COMPONENT_USER);
+  if (!base::CreateDirectory(app_install_dir)) {
+    return Result(
+        static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) +
+        kCustomInstallErrorCreateAppInstallDirectory);
+  }
+
+  const auto versioned_install_dir =
+      app_install_dir.AppendASCII(manifest_version.GetString());
+  if (base::PathExists(versioned_install_dir)) {
+    if (!base::DeleteFile(versioned_install_dir, true))
+      return Result(update_client::InstallError::CLEAN_INSTALL_DIR_FAILED);
+  }
+
+  VLOG(1) << "Install_path=" << versioned_install_dir.AsUTF8Unsafe();
+
+  if (!base::Move(unpack_path, versioned_install_dir)) {
+    PLOG(ERROR) << "Move failed.";
+    base::DeleteFile(versioned_install_dir, true);
+    return Result(update_client::InstallError::MOVE_FILES_ERROR);
+  }
+
+  DCHECK(!base::PathExists(unpack_path));
+  DCHECK(base::PathExists(versioned_install_dir));
+
+  install_info_->manifest = std::move(local_manifest);
+  install_info_->version = manifest_version;
+  install_info_->install_dir = versioned_install_dir;
+  base::ReadFileToString(
+      versioned_install_dir.AppendASCII("manifest.fingerprint"),
+      &install_info_->fingerprint);
+
+  return Result(update_client::InstallError::NONE);
+}
+
+void Installer::OnUpdateError(int error) {
+  LOG(ERROR) << "updater error: " << error << " for " << crx_id_;
+}
+
+void Installer::Install(const base::FilePath& unpack_path,
+                        const std::string& public_key,
+                        Callback callback) {
+  std::unique_ptr<base::DictionaryValue> manifest;
+  base::Version version;
+  base::FilePath install_path;
+
+  const auto result = InstallHelper(unpack_path);
+  base::DeleteFile(unpack_path, true);
+  std::move(callback).Run(result);
+}
+
+bool Installer::GetInstalledFile(const std::string& file,
+                                 base::FilePath* installed_file) {
+  return false;
+}
+
+bool Installer::Uninstall() {
+  return false;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/installer.h b/src/cobalt/updater/installer.h
new file mode 100644
index 0000000..ef97e84
--- /dev/null
+++ b/src/cobalt/updater/installer.h
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_INSTALLER_H_
+#define CHROME_UPDATER_INSTALLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "components/update_client/update_client.h"
+
+namespace updater {
+
+class Installer final : public update_client::CrxInstaller {
+ public:
+  struct InstallInfo {
+    InstallInfo();
+    ~InstallInfo();
+
+    base::FilePath install_dir;
+    base::Version version;
+    std::string fingerprint;
+    std::unique_ptr<base::DictionaryValue> manifest;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(InstallInfo);
+  };
+
+  explicit Installer(const std::vector<uint8_t>& pk_hash);
+
+  const std::string crx_id() const { return crx_id_; }
+
+  // Finds the highest version install of the app, and updates the install
+  // info for this installer instance.
+  void FindInstallOfApp();
+
+  // Returns a CrxComponent instance that describes the current install
+  // state of the app.
+  update_client::CrxComponent MakeCrxComponent();
+
+ private:
+  ~Installer() override;
+
+  // Overrides from update_client::CrxInstaller.
+  void OnUpdateError(int error) override;
+  void Install(const base::FilePath& unpack_path,
+               const std::string& public_key,
+               Callback callback) override;
+  bool GetInstalledFile(const std::string& file,
+                        base::FilePath* installed_file) override;
+  bool Uninstall() override;
+
+  Result InstallHelper(const base::FilePath& unpack_path);
+
+  const std::vector<uint8_t> pk_hash_;
+  const std::string crx_id_;
+  std::unique_ptr<InstallInfo> install_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(Installer);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_INSTALLER_H_
diff --git a/src/cobalt/updater/mac/BUILD.gn b/src/cobalt/updater/mac/BUILD.gn
new file mode 100644
index 0000000..cb2dcb5
--- /dev/null
+++ b/src/cobalt/updater/mac/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("mac") {
+  deps = [
+    ":updater",
+  ]
+}
+
+executable("updater") {
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//chrome/updater:common",
+  ]
+}
diff --git a/src/cobalt/updater/mac/main.cc b/src/cobalt/updater/mac/main.cc
new file mode 100644
index 0000000..7473bdb
--- /dev/null
+++ b/src/cobalt/updater/mac/main.cc
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/updater.h"
+
+int main(int argc, const char* argv[]) {
+  return updater::UpdaterMain(argc, argv);
+}
diff --git a/src/cobalt/updater/patcher.cc b/src/cobalt/updater/patcher.cc
new file mode 100644
index 0000000..cfff4d9
--- /dev/null
+++ b/src/cobalt/updater/patcher.cc
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/patcher.h"
+
+#include <utility>
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "courgette/courgette.h"
+#include "courgette/third_party/bsdiff/bsdiff.h"
+
+namespace updater {
+
+namespace {
+
+class PatcherImpl : public update_client::Patcher {
+ public:
+  PatcherImpl() = default;
+
+  void PatchBsdiff(const base::FilePath& input_path,
+                   const base::FilePath& patch_path,
+                   const base::FilePath& output_path,
+                   PatchCompleteCallback callback) const override {
+    base::File input_file(input_path,
+                          base::File::FLAG_OPEN | base::File::FLAG_READ);
+    base::File patch_file(patch_path,
+                          base::File::FLAG_OPEN | base::File::FLAG_READ);
+    base::File output_file(output_path, base::File::FLAG_CREATE |
+                                            base::File::FLAG_WRITE |
+                                            base::File::FLAG_EXCLUSIVE_WRITE);
+    if (!input_file.IsValid() || !patch_file.IsValid() ||
+        !output_file.IsValid()) {
+      std::move(callback).Run(-1);
+      return;
+    }
+    std::move(callback).Run(bsdiff::ApplyBinaryPatch(
+        std::move(input_file), std::move(patch_file), std::move(output_file)));
+  }
+
+  void PatchCourgette(const base::FilePath& input_path,
+                      const base::FilePath& patch_path,
+                      const base::FilePath& output_path,
+                      PatchCompleteCallback callback) const override {
+    base::File input_file(input_path,
+                          base::File::FLAG_OPEN | base::File::FLAG_READ);
+    base::File patch_file(patch_path,
+                          base::File::FLAG_OPEN | base::File::FLAG_READ);
+    base::File output_file(output_path, base::File::FLAG_CREATE |
+                                            base::File::FLAG_WRITE |
+                                            base::File::FLAG_EXCLUSIVE_WRITE);
+    if (!input_file.IsValid() || !patch_file.IsValid() ||
+        !output_file.IsValid()) {
+      std::move(callback).Run(-1);
+      return;
+    }
+    std::move(callback).Run(courgette::ApplyEnsemblePatch(
+        std::move(input_file), std::move(patch_file), std::move(output_file)));
+  }
+
+ protected:
+  ~PatcherImpl() override = default;
+};
+
+}  // namespace
+
+PatcherFactory::PatcherFactory() = default;
+
+scoped_refptr<update_client::Patcher> PatcherFactory::Create() const {
+  return base::MakeRefCounted<PatcherImpl>();
+}
+
+PatcherFactory::~PatcherFactory() = default;
+
+}  // namespace updater
diff --git a/src/cobalt/updater/patcher.h b/src/cobalt/updater/patcher.h
new file mode 100644
index 0000000..9af8c27
--- /dev/null
+++ b/src/cobalt/updater/patcher.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_PATCHER_H_
+#define CHROME_UPDATER_PATCHER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/patcher.h"
+
+namespace updater {
+
+class PatcherFactory : public update_client::PatcherFactory {
+ public:
+  PatcherFactory();
+
+  scoped_refptr<update_client::Patcher> Create() const override;
+
+ protected:
+  ~PatcherFactory() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PatcherFactory);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_PATCHER_H_
diff --git a/src/cobalt/updater/prefs.cc b/src/cobalt/updater/prefs.cc
new file mode 100644
index 0000000..eb9f6b4
--- /dev/null
+++ b/src/cobalt/updater/prefs.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/prefs.h"
+
+#include "base/files/file_path.h"
+#include "base/memory/scoped_refptr.h"
+#include "chrome/updater/util.h"
+#include "components/prefs/json_pref_store.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_service_factory.h"
+#include "components/update_client/update_client.h"
+
+namespace updater {
+
+std::unique_ptr<PrefService> CreatePrefService() {
+  base::FilePath product_data_dir;
+  if (!GetProductDirectory(&product_data_dir))
+    return nullptr;
+
+  PrefServiceFactory pref_service_factory;
+  pref_service_factory.set_user_prefs(base::MakeRefCounted<JsonPrefStore>(
+      product_data_dir.Append(FILE_PATH_LITERAL("prefs.json"))));
+
+  auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
+  update_client::RegisterPrefs(pref_registry.get());
+
+  return pref_service_factory.Create(pref_registry);
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/prefs.h b/src/cobalt/updater/prefs.h
new file mode 100644
index 0000000..9a08eae
--- /dev/null
+++ b/src/cobalt/updater/prefs.h
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_PREFS_H_
+#define CHROME_UPDATER_PREFS_H_
+
+#include <memory>
+
+class PrefService;
+
+namespace updater {
+
+std::unique_ptr<PrefService> CreatePrefService();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_PREFS_H_
diff --git a/src/cobalt/updater/unzipper.cc b/src/cobalt/updater/unzipper.cc
new file mode 100644
index 0000000..b218965
--- /dev/null
+++ b/src/cobalt/updater/unzipper.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/unzipper.h"
+
+#include <utility>
+#include "base/files/file_path.h"
+#include "third_party/zlib/google/zip.h"
+
+namespace updater {
+
+namespace {
+
+class UnzipperImpl : public update_client::Unzipper {
+ public:
+  UnzipperImpl() = default;
+
+  void Unzip(const base::FilePath& zip_path,
+             const base::FilePath& output_path,
+             UnzipCompleteCallback callback) override {
+    std::move(callback).Run(zip::Unzip(zip_path, output_path));
+  }
+};
+
+}  // namespace
+
+UnzipperFactory::UnzipperFactory() = default;
+
+std::unique_ptr<update_client::Unzipper> UnzipperFactory::Create() const {
+  return std::make_unique<UnzipperImpl>();
+}
+
+UnzipperFactory::~UnzipperFactory() = default;
+
+}  // namespace updater
diff --git a/src/cobalt/updater/unzipper.h b/src/cobalt/updater/unzipper.h
new file mode 100644
index 0000000..2b63d8e
--- /dev/null
+++ b/src/cobalt/updater/unzipper.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_UNZIPPER_H_
+#define CHROME_UPDATER_UNZIPPER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/unzipper.h"
+
+namespace updater {
+
+class UnzipperFactory : public update_client::UnzipperFactory {
+ public:
+  UnzipperFactory();
+
+  std::unique_ptr<update_client::Unzipper> Create() const override;
+
+ protected:
+  ~UnzipperFactory() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UnzipperFactory);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_UNZIPPER_H_
diff --git a/src/cobalt/updater/updater.cc b/src/cobalt/updater/updater.cc
new file mode 100644
index 0000000..18fa3c5
--- /dev/null
+++ b/src/cobalt/updater/updater.cc
@@ -0,0 +1,270 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/updater.h"
+
+#include <stdint.h>
+
+#include <iterator>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/message_loop/message_pump_type.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/task/post_task.h"
+#include "base/task/single_thread_task_executor.h"
+#include "base/task/thread_pool/thread_pool.h"
+#include "base/task_runner.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/updater/configurator.h"
+#include "chrome/updater/crash_client.h"
+#include "chrome/updater/crash_reporter.h"
+#include "chrome/updater/installer.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/updater_version.h"
+#include "chrome/updater/util.h"
+#include "components/crash/core/common/crash_key.h"
+#include "components/prefs/pref_service.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/update_client.h"
+
+#if defined(OS_WIN)
+#include "chrome/updater/win/setup/setup.h"
+#include "chrome/updater/win/setup/uninstall.h"
+#endif
+
+// To install the updater, run:
+// "updater.exe --install --enable-logging --v=1 --vmodule=*/chrome/updater/*"
+// from the build directory. The program needs a number of dependencies which
+// are available in the build out directory.
+// To uninstall, run "updater.exe --uninstall" from its install directory or
+// from the build out directory. Doing this will make the program delete its
+// install directory using a shim cmd script.
+namespace updater {
+
+namespace {
+
+// For now, use the Flash CRX for testing.
+// CRX id is mimojjlkmoijpicakmndhoigimigcmbb.
+const uint8_t mimo_hash[] = {0xc8, 0xce, 0x99, 0xba, 0xce, 0x89, 0xf8, 0x20,
+                             0xac, 0xd3, 0x7e, 0x86, 0x8c, 0x86, 0x2c, 0x11,
+                             0xb9, 0x40, 0xc5, 0x55, 0xaf, 0x08, 0x63, 0x70,
+                             0x54, 0xf9, 0x56, 0xd3, 0xe7, 0x88, 0xba, 0x8c};
+
+void ThreadPoolStart() {
+  base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Updater");
+}
+
+void ThreadPoolStop() {
+  base::ThreadPoolInstance::Get()->Shutdown();
+}
+
+void QuitLoop(base::OnceClosure quit_closure) {
+  std::move(quit_closure).Run();
+}
+
+class Observer : public update_client::UpdateClient::Observer {
+ public:
+  explicit Observer(scoped_refptr<update_client::UpdateClient> update_client)
+      : update_client_(update_client) {}
+
+  // Overrides for update_client::UpdateClient::Observer.
+  void OnEvent(Events event, const std::string& id) override {
+    update_client_->GetCrxUpdateState(id, &crx_update_item_);
+  }
+
+  const update_client::CrxUpdateItem& crx_update_item() const {
+    return crx_update_item_;
+  }
+
+ private:
+  scoped_refptr<update_client::UpdateClient> update_client_;
+  update_client::CrxUpdateItem crx_update_item_;
+  DISALLOW_COPY_AND_ASSIGN(Observer);
+};
+
+// The log file is created in DIR_LOCAL_APP_DATA or DIR_APP_DATA.
+void InitLogging(const base::CommandLine& command_line) {
+  logging::LoggingSettings settings;
+  base::FilePath log_dir;
+  GetProductDirectory(&log_dir);
+  const auto log_file = log_dir.Append(FILE_PATH_LITERAL("updater.log"));
+  settings.log_file_path = log_file.value().c_str();
+  settings.logging_dest = logging::LOG_TO_ALL;
+  logging::InitLogging(settings);
+  logging::SetLogItems(true,    // enable_process_id
+                       true,    // enable_thread_id
+                       true,    // enable_timestamp
+                       false);  // enable_tickcount
+  VLOG(1) << "Log file " << settings.log_file_path;
+}
+
+void InitializeUpdaterMain() {
+  crash_reporter::InitializeCrashKeys();
+
+  static crash_reporter::CrashKeyString<16> crash_key_process_type(
+      "process_type");
+  crash_key_process_type.Set("updater");
+
+  if (CrashClient::GetInstance()->InitializeCrashReporting())
+    VLOG(1) << "Crash reporting initialized.";
+  else
+    VLOG(1) << "Crash reporting is not available.";
+
+  StartCrashReporter(UPDATER_VERSION_STRING);
+
+  ThreadPoolStart();
+}
+
+void TerminateUpdaterMain() {
+  ThreadPoolStop();
+}
+
+int UpdaterInstall() {
+#if defined(OS_WIN)
+  return Setup();
+#else
+  return -1;
+#endif
+}
+
+int UpdaterUninstall() {
+#if defined(OS_WIN)
+  return Uninstall();
+#else
+  return -1;
+#endif
+}
+
+int UpdaterUpdateApps() {
+  auto installer = base::MakeRefCounted<Installer>(
+      std::vector<uint8_t>(std::cbegin(mimo_hash), std::cend(mimo_hash)));
+  installer->FindInstallOfApp();
+  const auto component = installer->MakeCrxComponent();
+
+  base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
+  base::RunLoop runloop;
+  DCHECK(base::ThreadTaskRunnerHandle::IsSet());
+
+  auto config = base::MakeRefCounted<Configurator>();
+  {
+    base::ScopedDisallowBlocking no_blocking_allowed;
+
+    auto update_client = update_client::UpdateClientFactory(config);
+
+    Observer observer(update_client);
+    update_client->AddObserver(&observer);
+
+    const std::vector<std::string> ids = {installer->crx_id()};
+    update_client->Update(
+        ids,
+        base::BindOnce(
+            [](const update_client::CrxComponent& component,
+               const std::vector<std::string>& ids)
+                -> std::vector<base::Optional<update_client::CrxComponent>> {
+              DCHECK_EQ(1u, ids.size());
+              return {component};
+            },
+            component),
+        true,
+        base::BindOnce(
+            [](base::OnceClosure closure, update_client::Error error) {
+              base::ThreadTaskRunnerHandle::Get()->PostTask(
+                  FROM_HERE, base::BindOnce(&QuitLoop, std::move(closure)));
+            },
+            runloop.QuitWhenIdleClosure()));
+
+    runloop.Run();
+
+    const auto& update_item = observer.crx_update_item();
+    switch (update_item.state) {
+      case update_client::ComponentState::kUpdated:
+        VLOG(1) << "Update success.";
+        break;
+      case update_client::ComponentState::kUpToDate:
+        VLOG(1) << "No updates.";
+        break;
+      case update_client::ComponentState::kUpdateError:
+        VLOG(1) << "Updater error: " << update_item.error_code << ".";
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+    update_client->RemoveObserver(&observer);
+    update_client = nullptr;
+  }
+
+  {
+    base::RunLoop runloop;
+    config->GetPrefService()->CommitPendingWrite(base::BindOnce(
+        [](base::OnceClosure quit_closure) { std::move(quit_closure).Run(); },
+        runloop.QuitWhenIdleClosure()));
+    runloop.Run();
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+int HandleUpdaterCommands(const base::CommandLine* command_line) {
+  DCHECK(!command_line->HasSwitch(kCrashHandlerSwitch));
+
+  if (command_line->HasSwitch(kCrashMeSwitch)) {
+    int* ptr = nullptr;
+    return *ptr;
+  }
+
+  if (command_line->HasSwitch(kInstallSwitch)) {
+    return UpdaterInstall();
+  }
+
+  if (command_line->HasSwitch(kUninstallSwitch)) {
+    return UpdaterUninstall();
+  }
+
+  if (command_line->HasSwitch(kUpdateAppsSwitch)) {
+    return UpdaterUpdateApps();
+  }
+
+  VLOG(1) << "Unknown command line switch.";
+  return -1;
+}
+
+int UpdaterMain(int argc, const char* const* argv) {
+  base::PlatformThread::SetName("UpdaterMain");
+  base::AtExitManager exit_manager;
+
+  base::CommandLine::Init(argc, argv);
+  const auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(kTestSwitch))
+    return 0;
+
+  InitLogging(*command_line);
+
+  if (command_line->HasSwitch(kCrashHandlerSwitch))
+    return CrashReporterMain();
+
+  InitializeUpdaterMain();
+  const auto result = HandleUpdaterCommands(command_line);
+  TerminateUpdaterMain();
+  return result;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/updater.h b/src/cobalt/updater/updater.h
new file mode 100644
index 0000000..1c96663
--- /dev/null
+++ b/src/cobalt/updater/updater.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_UPDATER_H_
+#define CHROME_UPDATER_UPDATER_H_
+
+namespace updater {
+
+int UpdaterMain(int argc, const char* const* argv);
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_UPDATER_H_
diff --git a/src/cobalt/updater/updater_constants.cc b/src/cobalt/updater/updater_constants.cc
new file mode 100644
index 0000000..d726e79
--- /dev/null
+++ b/src/cobalt/updater/updater_constants.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/updater_constants.h"
+
+namespace updater {
+
+const char kCrashMeSwitch[] = "crash-me";
+const char kCrashHandlerSwitch[] = "crash-handler";
+const char kInstallSwitch[] = "install";
+const char kUninstallSwitch[] = "uninstall";
+const char kUpdateAppsSwitch[] = "ua";
+const char kTestSwitch[] = "test";
+const char kInitDoneNotifierSwitch[] = "init-done-notifier";
+const char kNoRateLimitSwitch[] = "no-rate-limit";
+const char kEnableLoggingSwitch[] = "enable-logging";
+const char kLoggingLevelSwitch[] = "v";
+const char kLoggingModuleSwitch[] = "vmodule";
+
+const char kUpdaterJSONDefaultUrl[] =
+    "https://update.googleapis.com/service/update2/json";
+const char kCrashUploadURL[] = "https://clients2.google.com/cr/report";
+const char kCrashStagingUploadURL[] =
+    "https://clients2.google.com/cr/staging_report";
+
+extern const char kAppsDir[] = "apps";
+extern const char kUninstallScript[] = "uninstall.cmd";
+
+}  // namespace updater
diff --git a/src/cobalt/updater/updater_constants.h b/src/cobalt/updater/updater_constants.h
new file mode 100644
index 0000000..5f47325
--- /dev/null
+++ b/src/cobalt/updater/updater_constants.h
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_UPDATER_CONSTANTS_H_
+#define CHROME_UPDATER_UPDATER_CONSTANTS_H_
+
+namespace updater {
+
+// Command line switches.
+//
+// Crash the program for testing purposes.
+extern const char kCrashMeSwitch[];
+
+// Runs as the Crashpad handler.
+extern const char kCrashHandlerSwitch[];
+
+// Installs the updater.
+extern const char kInstallSwitch[];
+
+// Uninstalls the updater.
+extern const char kUninstallSwitch[];
+
+// Updates all apps registered with the updater.
+extern const char kUpdateAppsSwitch[];
+
+// Runs in test mode. Currently, it exits right away.
+extern const char kTestSwitch[];
+
+// Disables throttling for the crash reported until the following bug is fixed:
+// https://bugs.chromium.org/p/crashpad/issues/detail?id=23
+extern const char kNoRateLimitSwitch[];
+
+// The handle of an event to signal when the initialization of the main process
+// is complete.
+extern const char kInitDoneNotifierSwitch[];
+
+// Enables logging.
+extern const char kEnableLoggingSwitch[];
+
+// Specifies the logging level.
+extern const char kLoggingLevelSwitch[];
+
+// Specifies the logging module filter.
+extern const char kLoggingModuleSwitch[];
+
+// URLs.
+//
+// Omaha server end point.
+extern const char kUpdaterJSONDefaultUrl[];
+
+// The URL where crash reports are uploaded.
+extern const char kCrashUploadURL[];
+extern const char kCrashStagingUploadURL[];
+
+// Paths.
+//
+// The directory name where CRX apps get installed. This is provided for demo
+// purposes, since products installed by this updater will be installed in
+// their specific locations.
+extern const char kAppsDir[];
+
+// The name of the uninstall script which is invoked by the --uninstall switch.
+extern const char kUninstallScript[];
+
+// Errors.
+//
+// The install directory for the application could not be created.
+const int kCustomInstallErrorCreateAppInstallDirectory = 0;
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_UPDATER_CONSTANTS_H_
diff --git a/src/cobalt/updater/updater_unittest.cc b/src/cobalt/updater/updater_unittest.cc
new file mode 100644
index 0000000..fd2d7dd
--- /dev/null
+++ b/src/cobalt/updater/updater_unittest.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#define EXECUTABLE_EXTENSION ".exe"
+#else
+#define EXECUTABLE_EXTENSION ""
+#endif
+
+// Tests the updater process returns 0 when run with --test argument.
+TEST(UpdaterTest, UpdaterExitCode) {
+  base::FilePath this_executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &this_executable_path));
+  const base::FilePath updater = this_executable_path.DirName().Append(
+      FILE_PATH_LITERAL("updater" EXECUTABLE_EXTENSION));
+  base::LaunchOptions options;
+#if defined(OS_WIN)
+  options.start_hidden = true;
+#endif
+  base::CommandLine command_line(updater);
+  command_line.AppendSwitch("test");
+  auto process = base::LaunchProcess(command_line, options);
+  ASSERT_TRUE(process.IsValid());
+  int exit_code = -1;
+  EXPECT_TRUE(process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(60),
+                                             &exit_code));
+  EXPECT_EQ(0, exit_code);
+}
diff --git a/src/cobalt/updater/updater_version.h.in b/src/cobalt/updater/updater_version.h.in
new file mode 100644
index 0000000..242c533
--- /dev/null
+++ b/src/cobalt/updater/updater_version.h.in
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Version Information
+
+#define UPDATER_VERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@
+#define UPDATER_VERSION_STRING "@MAJOR@.@MINOR@.@BUILD@.@PATCH@"
+
+// Branding Information
+#define COMPANY_FULLNAME_STRING "@COMPANY_FULLNAME@"
+#define COMPANY_SHORTNAME_STRING "@COMPANY_SHORTNAME@"
+#define PRODUCT_FULLNAME_STRING "@PRODUCT_FULLNAME@"
+#define OFFICIAL_BUILD_STRING "@OFFICIAL_BUILD@"
diff --git a/src/cobalt/updater/util.cc b/src/cobalt/updater/util.cc
new file mode 100644
index 0000000..ced4a55
--- /dev/null
+++ b/src/cobalt/updater/util.cc
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/util.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "build/build_config.h"
+#include "chrome/updater/updater_version.h"
+
+namespace updater {
+
+bool GetProductDirectory(base::FilePath* path) {
+  constexpr int kPathKey =
+#if defined(OS_WIN)
+      base::DIR_LOCAL_APP_DATA;
+#elif defined(OS_MACOSX)
+      base::DIR_APP_DATA;
+#endif
+
+  base::FilePath app_data_dir;
+  if (!base::PathService::Get(kPathKey, &app_data_dir)) {
+    LOG(ERROR) << "Can't retrieve local app data directory.";
+    return false;
+  }
+
+  const auto product_data_dir =
+      app_data_dir.AppendASCII(COMPANY_SHORTNAME_STRING)
+          .AppendASCII(PRODUCT_FULLNAME_STRING);
+  if (!base::CreateDirectory(product_data_dir)) {
+    LOG(ERROR) << "Can't create product directory.";
+    return false;
+  }
+
+  *path = product_data_dir;
+  return true;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/util.h b/src/cobalt/updater/util.h
new file mode 100644
index 0000000..da5861e
--- /dev/null
+++ b/src/cobalt/updater/util.h
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_UTIL_H_
+#define CHROME_UPDATER_UTIL_H_
+
+namespace base {
+class FilePath;
+}
+
+namespace updater {
+
+// Returns a directory where updater files or its data is stored.
+bool GetProductDirectory(base::FilePath* path);
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_UTIL_H_
diff --git a/src/cobalt/updater/win/BUILD.gn b/src/cobalt/updater/win/BUILD.gn
new file mode 100644
index 0000000..d3c9fd2
--- /dev/null
+++ b/src/cobalt/updater/win/BUILD.gn
@@ -0,0 +1,125 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/process_version_rc_template.gni")
+import("//testing/test.gni")
+
+# This target builds the updater executable, its installer, and unittests.
+group("win") {
+  deps = [
+    ":updater",
+    "//chrome/updater/win/installer:installer",
+  ]
+}
+
+executable("updater") {
+  sources = [
+    "main.cc",
+    "updater.rc",
+  ]
+
+  configs += [ "//build/config/win:windowed" ]
+
+  libs = [ "winhttp.lib" ]
+  deps = [
+    ":code",
+    ":version_resources",
+    "//build/win:default_exe_manifest",
+    "//chrome/updater:common",
+  ]
+}
+
+copy("uninstall.cmd") {
+  sources = [
+    "setup/uninstall.cmd",
+  ]
+  outputs = [
+    "$target_gen_dir/uninstall.cmd",
+  ]
+}
+
+process_version_rc_template("version_resources") {
+  sources = [
+    "updater.ver",
+  ]
+  output = "$target_gen_dir/updater_exe.rc"
+}
+
+source_set("code") {
+  sources = [
+    "net/net_util.cc",
+    "net/net_util.h",
+    "net/network.h",
+    "net/network_fetcher.cc",
+    "net/network_fetcher.h",
+    "net/network_winhttp.cc",
+    "net/network_winhttp.h",
+    "net/scoped_hinternet.h",
+    "setup/setup.cc",
+    "setup/setup.h",
+    "setup/setup_util.cc",
+    "setup/setup_util.h",
+    "setup/uninstall.cc",
+    "setup/uninstall.h",
+    "task_scheduler.cc",
+    "task_scheduler.h",
+    "util.cc",
+    "util.h",
+  ]
+
+  defines = [ "SECURITY_WIN32" ]
+
+  libs = [
+    "secur32.lib",
+    "taskschd.lib",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome/installer/util:with_no_strings",
+    "//chrome/updater:common",
+    "//components/update_client",
+  ]
+}
+
+# Tests built into Chrome's unit_tests.exe.
+source_set("updater_tests") {
+  testonly = true
+
+  sources = [
+    "net/network_unittest.cc",
+    "util_unittest.cc",
+  ]
+
+  deps = [
+    ":code",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+
+  data_deps = [
+    ":updater_unittests",
+    "//chrome/updater/win/installer:installer_unittest",
+  ]
+}
+
+# Specific tests which must run in their own process due to COM, security, or
+# test isolation requirements.
+test("updater_unittests") {
+  testonly = true
+
+  sources = [
+    "//chrome/updater/win/test/test_main.cc",
+    "task_scheduler_unittest.cc",
+  ]
+
+  deps = [
+    ":code",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/updater/win/test:test_executables",
+    "//chrome/updater/win/test:test_strings",
+    "//testing/gtest",
+  ]
+}
diff --git a/src/cobalt/updater/win/installer/BUILD.gn b/src/cobalt/updater/win/installer/BUILD.gn
new file mode 100644
index 0000000..b7ae096
--- /dev/null
+++ b/src/cobalt/updater/win/installer/BUILD.gn
@@ -0,0 +1,143 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/process_version_rc_template.gni")
+import("//testing/test.gni")
+
+source_set("lib") {
+  sources = [
+    "configuration.cc",
+    "configuration.h",
+    "exit_code.h",
+    "installer.cc",
+    "installer.h",
+    "installer.rc",
+    "installer_constants.cc",
+    "installer_constants.h",
+    "installer_resource.h",
+    "pe_resource.cc",
+    "pe_resource.h",
+    "regkey.cc",
+    "regkey.h",
+    "string.cc",
+    "string.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+process_version_rc_template("version") {
+  template_file = "installer_version.rc.version"
+  output = "$root_out_dir/installer_version.rc"
+}
+
+# This target creats a list of runtime dependencies for the component
+# builds. This list is parsed by the |create_installer_archive| script, the
+# DLL paths extracted out from the list, and included in the archive.
+updater_runtime_deps = "$root_gen_dir/updater.runtime_deps"
+group("updater_runtime_deps") {
+  write_runtime_deps = updater_runtime_deps
+  data_deps = [
+    "//chrome/updater/win:updater",
+  ]
+}
+
+template("generate_installer") {
+  output_dir = invoker.out_dir
+  packed_files_rc_file = "$target_gen_dir/$target_name/packed_files.rc"
+  archive_name = target_name + "_archive"
+  staging_dir = "$target_gen_dir/$target_name"
+
+  action(archive_name) {
+    script = "create_installer_archive.py"
+
+    release_file = "updater.release"
+
+    inputs = [
+      release_file,
+    ]
+
+    outputs = [
+      "$output_dir/updater.packed.7z",
+      packed_files_rc_file,
+    ]
+
+    args = [
+      "--build_dir",
+      rebase_path(root_out_dir, root_build_dir),
+      "--staging_dir",
+      rebase_path(staging_dir, root_build_dir),
+      "--input_file",
+      rebase_path(release_file, root_build_dir),
+      "--resource_file_path",
+      rebase_path(packed_files_rc_file, root_build_dir),
+      "--output_dir",
+      rebase_path(output_dir, root_build_dir),
+      "--setup_runtime_deps",
+      rebase_path(updater_runtime_deps, root_build_dir),
+      "--output_name=updater",
+      "--verbose",
+    ]
+
+    deps = [
+      ":updater_runtime_deps",
+      "//chrome/updater/win:uninstall.cmd",
+      "//chrome/updater/win:updater",
+    ]
+
+    if (is_component_build) {
+      args += [ "--component_build=1" ]
+    }
+  }
+
+  executable(target_name) {
+    output_name = invoker.output_name
+
+    sources = [
+      "installer_main.cc",
+      packed_files_rc_file,
+    ]
+
+    configs += [ "//build/config/win:windowed" ]
+
+    libs = [ "setupapi.lib" ]
+
+    deps = [
+      ":$archive_name",
+      ":lib",
+      ":version",
+      "//build/win:default_exe_manifest",
+      "//chrome/installer/util:with_no_strings",
+    ]
+  }
+}
+
+generate_installer("installer") {
+  out_dir = root_out_dir
+  output_name = "UpdaterSetup"
+}
+
+test("installer_unittest") {
+  testonly = true
+
+  output_name = "updater_installer_unittest"
+
+  sources = [
+    "configuration_unittest.cc",
+    "run_all_unittests.cc",
+    "string_unittest.cc",
+  ]
+
+  public_deps = [
+    ":lib",
+  ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//chrome/installer/util:with_no_strings",
+    "//testing/gtest",
+  ]
+}
diff --git a/src/cobalt/updater/win/installer/configuration.cc b/src/cobalt/updater/win/installer/configuration.cc
new file mode 100644
index 0000000..9059273
--- /dev/null
+++ b/src/cobalt/updater/win/installer/configuration.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/installer/configuration.h"
+#include <shellapi.h>
+#include "chrome/updater/win/installer/string.h"
+
+namespace updater {
+
+namespace {
+
+// Returns true if GoogleUpdateIsMachine=1 is present in the environment.
+bool GetGoogleUpdateIsMachineEnvVar() {
+  constexpr DWORD kBufferSize = 2;
+  StackString<kBufferSize> value;
+  const auto length = ::GetEnvironmentVariableW(L"GoogleUpdateIsMachine",
+                                                value.get(), kBufferSize);
+  return length == 1 && *value.get() == L'1';
+}
+
+}  // namespace
+
+Configuration::Configuration() {
+  Clear();
+}
+
+Configuration::~Configuration() {
+  Clear();
+}
+
+bool Configuration::Initialize(HMODULE module) {
+  Clear();
+  return ParseCommandLine(::GetCommandLine());
+}
+
+void Configuration::Clear() {
+  if (args_ != nullptr) {
+    ::LocalFree(args_);
+    args_ = nullptr;
+  }
+  command_line_ = nullptr;
+  operation_ = INSTALL_PRODUCT;
+  argument_count_ = 0;
+  is_system_level_ = false;
+  has_invalid_switch_ = false;
+}
+
+// |command_line| is shared with this instance in the sense that this
+// instance may refer to it at will throughout its lifetime, yet it will
+// not release it.
+bool Configuration::ParseCommandLine(const wchar_t* command_line) {
+  command_line_ = command_line;
+  args_ = ::CommandLineToArgvW(command_line_, &argument_count_);
+  if (!args_)
+    return false;
+
+  for (int i = 1; i < argument_count_; ++i) {
+    if (0 == ::lstrcmpi(args_[i], L"--system-level"))
+      is_system_level_ = true;
+    else if (0 == ::lstrcmpi(args_[i], L"--cleanup"))
+      operation_ = CLEANUP;
+  }
+
+  if (!is_system_level_)
+    is_system_level_ = GetGoogleUpdateIsMachineEnvVar();
+
+  return true;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/installer/configuration.h b/src/cobalt/updater/win/installer/configuration.h
new file mode 100644
index 0000000..c47a00d
--- /dev/null
+++ b/src/cobalt/updater/win/installer/configuration.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_CONFIGURATION_H_
+#define CHROME_UPDATER_WIN_INSTALLER_CONFIGURATION_H_
+
+#include <windows.h>
+
+namespace updater {
+
+// A simple container of the updater's configuration, as defined by the
+// command line used to invoke it.
+class Configuration {
+ public:
+  enum Operation {
+    INSTALL_PRODUCT,
+    CLEANUP,
+  };
+
+  Configuration();
+  ~Configuration();
+
+  // Initializes this instance on the basis of the process's command line.
+  bool Initialize(HMODULE module);
+
+  // Returns the desired operation dictated by the command line options.
+  Operation operation() const { return operation_; }
+
+  // Returns true if --system-level is on the command line or if
+  // GoogleUpdateIsMachine=1 is set in the process's environment.
+  bool is_system_level() const { return is_system_level_; }
+
+  // Returns true if any invalid switch is found on the command line.
+  bool has_invalid_switch() const { return has_invalid_switch_; }
+
+ protected:
+  void Clear();
+  bool ParseCommandLine(const wchar_t* command_line);
+
+  wchar_t** args_ = nullptr;
+  const wchar_t* command_line_ = nullptr;
+  int argument_count_ = 0;
+  Operation operation_ = INSTALL_PRODUCT;
+  bool is_system_level_ = false;
+  bool has_invalid_switch_ = false;
+
+ private:
+  Configuration(const Configuration&) = delete;
+  Configuration& operator=(const Configuration&) = delete;
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_CONFIGURATION_H_
diff --git a/src/cobalt/updater/win/installer/configuration_unittest.cc b/src/cobalt/updater/win/installer/configuration_unittest.cc
new file mode 100644
index 0000000..54ec28b1
--- /dev/null
+++ b/src/cobalt/updater/win/installer/configuration_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/installer/configuration.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <memory>
+
+#include "base/environment.h"
+#include "base/macros.h"
+#include "base/test/test_reg_util_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+
+namespace {
+
+// A helper class to set the "GoogleUpdateIsMachine" environment variable.
+class ScopedGoogleUpdateIsMachine {
+ public:
+  explicit ScopedGoogleUpdateIsMachine(bool value)
+      : env_(base::Environment::Create()) {
+    env_->SetVar("GoogleUpdateIsMachine", value ? "1" : "0");
+  }
+
+  ~ScopedGoogleUpdateIsMachine() { env_->UnSetVar("GoogleUpdateIsMachine"); }
+
+ private:
+  std::unique_ptr<base::Environment> env_;
+};
+
+class TestConfiguration : public Configuration {
+ public:
+  explicit TestConfiguration(const wchar_t* command_line) {
+    EXPECT_TRUE(ParseCommandLine(command_line));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestConfiguration);
+};
+
+}  // namespace
+
+class UpdaterInstallerConfigurationTest : public ::testing::Test {
+ protected:
+  UpdaterInstallerConfigurationTest() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpdaterInstallerConfigurationTest);
+};
+
+// Test that the operation type is CLEANUP iff --cleanup is on the cmdline.
+TEST_F(UpdaterInstallerConfigurationTest, Operation) {
+  EXPECT_EQ(Configuration::INSTALL_PRODUCT,
+            TestConfiguration(L"spam.exe").operation());
+  EXPECT_EQ(Configuration::INSTALL_PRODUCT,
+            TestConfiguration(L"spam.exe --clean").operation());
+  EXPECT_EQ(Configuration::INSTALL_PRODUCT,
+            TestConfiguration(L"spam.exe --cleanupthis").operation());
+
+  EXPECT_EQ(Configuration::CLEANUP,
+            TestConfiguration(L"spam.exe --cleanup").operation());
+  EXPECT_EQ(Configuration::CLEANUP,
+            TestConfiguration(L"spam.exe --cleanup now").operation());
+}
+
+TEST_F(UpdaterInstallerConfigurationTest, IsSystemLevel) {
+  EXPECT_FALSE(TestConfiguration(L"spam.exe").is_system_level());
+  EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").is_system_level());
+  EXPECT_TRUE(TestConfiguration(L"spam.exe --system-level").is_system_level());
+
+  {
+    ScopedGoogleUpdateIsMachine env_setter(false);
+    EXPECT_FALSE(TestConfiguration(L"spam.exe").is_system_level());
+  }
+
+  {
+    ScopedGoogleUpdateIsMachine env_setter(true);
+    EXPECT_TRUE(TestConfiguration(L"spam.exe").is_system_level());
+  }
+}
+
+TEST_F(UpdaterInstallerConfigurationTest, HasInvalidSwitch) {
+  EXPECT_FALSE(TestConfiguration(L"spam.exe").has_invalid_switch());
+  EXPECT_TRUE(
+      TestConfiguration(L"spam.exe --chrome-frame").has_invalid_switch());
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/installer/create_installer_archive.py b/src/cobalt/updater/win/installer/create_installer_archive.py
new file mode 100644
index 0000000..c8dbb9c
--- /dev/null
+++ b/src/cobalt/updater/win/installer/create_installer_archive.py
@@ -0,0 +1,341 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Script to create the Chrome Updater Installer archive.
+
+  This script is used to create an archive of all the files required for a
+  Chrome Updater install in appropriate directory structure. It reads
+  updater.release file as input, creates updater.7z ucompressed archive, and
+  generates the updater.packed.7z compressed archive.
+
+"""
+
+import ConfigParser
+import glob
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+
+# Directory name inside the uncompressed archive where all the files are.
+UPDATER_DIR = "bin"
+
+# Suffix to uncompressed full archive file, appended to options.output_name.
+ARCHIVE_SUFFIX = ".7z"
+
+# compressed full archive suffix, will be prefixed by options.output_name.
+COMPRESSED_ARCHIVE_SUFFIX = ".packed.7z"
+TEMP_ARCHIVE_DIR = "temp_installer_archive"
+
+g_archive_inputs = []
+
+def CompressUsingLZMA(build_dir, compressed_file, input_file, verbose):
+  lzma_exec = GetLZMAExec(build_dir)
+  cmd = [lzma_exec,
+         'a', '-t7z',
+          # Flags equivalent to -mx9 (ultra) but with the bcj2 turned on (exe
+          # pre-filter). These arguments are the similar to what the Chrome mini
+          # installer is using.
+          '-m0=BCJ2',
+          '-m1=LZMA:d27:fb128',
+          '-m2=LZMA:d22:fb128:mf=bt2',
+          '-m3=LZMA:d22:fb128:mf=bt2',
+          '-mb0:1',
+          '-mb0s1:2',
+          '-mb0s2:3',
+          os.path.abspath(compressed_file),
+          os.path.abspath(input_file),]
+  if os.path.exists(compressed_file):
+    os.remove(compressed_file)
+  RunSystemCommand(cmd, verbose)
+
+
+def CopyAllFilesToStagingDir(config, staging_dir, build_dir):
+  """Copies the files required for installer archive.
+  """
+  CopySectionFilesToStagingDir(config, 'GENERAL', staging_dir, build_dir)
+
+
+def CopySectionFilesToStagingDir(config, section, staging_dir, src_dir):
+  """Copies installer archive files specified in section from src_dir to
+  staging_dir. This method reads section from config and copies all the
+  files specified from src_dir to staging dir.
+  """
+  for option in config.options(section):
+    src_subdir = option.replace('\\', os.sep)
+    dst_dir = os.path.join(staging_dir, config.get(section, option))
+    dst_dir = dst_dir.replace('\\', os.sep)
+    src_paths = glob.glob(os.path.join(src_dir, src_subdir))
+    if src_paths and not os.path.exists(dst_dir):
+      os.makedirs(dst_dir)
+    for src_path in src_paths:
+      print(src_path)
+      dst_path = os.path.join(dst_dir, os.path.basename(src_path))
+      if not os.path.exists(dst_path):
+        g_archive_inputs.append(src_path)
+        print('paths src_path={0}, dest_dir={1}'.format(src_path, dst_dir))
+        shutil.copy(src_path, dst_dir)
+
+def GetLZMAExec(build_dir):
+  if sys.platform == 'win32':
+    lzma_exec = os.path.join(build_dir, "..", "..", "third_party",
+                             "lzma_sdk", "Executable", "7za.exe")
+  else:
+    lzma_exec = '7zr'  # Use system 7zr.
+  return lzma_exec
+
+def MakeStagingDirectory(staging_dir):
+  """Creates a staging path for installer archive. If directory exists already,
+  deletes the existing directory.
+  """
+  file_path = os.path.join(staging_dir, TEMP_ARCHIVE_DIR)
+  if os.path.exists(file_path):
+    shutil.rmtree(file_path)
+  os.makedirs(file_path)
+  return file_path
+
+def Readconfig(input_file):
+  """Reads config information from input file after setting default value of
+  global variables.
+  """
+  variables = {}
+  variables['UpdaterDir'] = UPDATER_DIR
+  config = ConfigParser.SafeConfigParser(variables)
+  config.read(input_file)
+  return config
+
+def RunSystemCommand(cmd, verbose):
+  """Runs |cmd|, prints the |cmd| and its output if |verbose|; otherwise
+  captures its output and only emits it on failure.
+  """
+  if verbose:
+    print 'Running', cmd
+
+  try:
+    # Run |cmd|, redirecting stderr to stdout in order for captured errors to be
+    # inline with corresponding stdout.
+    output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+    if verbose:
+      print output
+  except subprocess.CalledProcessError as e:
+    raise Exception("Error while running cmd: %s\n"
+                    "Exit code: %s\n"
+                    "Command output:\n%s" %
+                    (e.cmd, e.returncode, e.output))
+
+def CreateArchiveFile(options, staging_dir):
+  """Creates a new installer archive file after deleting any existing old file.
+  """
+  # First create an uncompressed archive file for the current build (updater.7z)
+  lzma_exec = GetLZMAExec(options.build_dir)
+  archive_file = os.path.join(options.output_dir,
+                              options.output_name + ARCHIVE_SUFFIX)
+
+  if options.depfile:
+    # If a depfile was requested, do the glob of the staging dir and generate
+    # a list of dependencies in .d format. We list the files that were copied
+    # into the staging dir, not the files that are actually in the staging dir
+    # because the ones in the staging dir will never be edited, and we want
+    # to have the build be triggered when the thing-that-was-copied-there
+    # changes.
+
+    def PathFixup(path):
+      """Fixes path for depfile format: backslash to forward slash, and
+      backslash escaping for spaces."""
+      return path.replace('\\', '/').replace(' ', '\\ ')
+
+    # Gather the list of files in the staging dir that will be zipped up. We
+    # only gather this list to make sure that g_archive_inputs is complete (i.e.
+    # that there's not file copies that got missed).
+    staging_contents = []
+    for root, files in os.walk(os.path.join(staging_dir, UPDATER_DIR)):
+      for filename in files:
+        staging_contents.append(PathFixup(os.path.join(root, filename)))
+
+    # Make sure there's an archive_input for each staging dir file.
+    for staging_file in staging_contents:
+      for archive_input in g_archive_inputs:
+        archive_rel = PathFixup(archive_input)
+        if (os.path.basename(staging_file).lower() ==
+            os.path.basename(archive_rel).lower()):
+          break
+      else:
+        raise Exception('Did not find an archive input file for "%s"' %
+                        staging_file)
+
+    # Finally, write the depfile referencing the inputs.
+    with open(options.depfile, 'wb') as f:
+      f.write(PathFixup(os.path.relpath(archive_file, options.build_dir)) +
+              ': \\\n')
+      f.write('  ' + ' \\\n  '.join(PathFixup(x) for x in g_archive_inputs))
+
+  # It is important to use abspath to create the path to the directory because
+  # if you use a relative path without any .. sequences then 7za.exe uses the
+  # entire relative path as part of the file paths in the archive. If you have
+  # a .. sequence or an absolute path then only the last directory is stored as
+  # part of the file paths in the archive, which is what we want.
+  cmd = [lzma_exec,
+         'a',
+         '-t7z',
+         archive_file,
+         os.path.abspath(os.path.join(staging_dir, UPDATER_DIR)),
+         '-mx0',]
+  # There does not seem to be any way in 7za.exe to override existing file so
+  # we always delete before creating a new one.
+  if not os.path.exists(archive_file):
+    RunSystemCommand(cmd, options.verbose)
+  elif options.skip_rebuild_archive != "true":
+    os.remove(archive_file)
+    RunSystemCommand(cmd, options.verbose)
+
+  # Do not compress the archive when skip_archive_compression is specified.
+  if options.skip_archive_compression:
+    compressed_file = os.path.join(
+        options.output_dir, options.output_name + COMPRESSED_ARCHIVE_SUFFIX)
+    if os.path.exists(compressed_file):
+      os.remove(compressed_file)
+    return os.path.basename(archive_file)
+
+  compressed_archive_file = options.output_name + COMPRESSED_ARCHIVE_SUFFIX
+  compressed_archive_file_path = os.path.join(options.output_dir,
+                                              compressed_archive_file)
+  CompressUsingLZMA(options.build_dir, compressed_archive_file_path,
+                    archive_file, options.verbose)
+
+  return compressed_archive_file
+
+
+_RESOURCE_FILE_HEADER = """\
+// This file is automatically generated by create_installer_archive.py.
+// It contains the resource entries that are going to be linked inside the exe.
+// For each file to be linked there should be two lines:
+// - The first line contains the output filename (without path) and the
+// type of the resource ('BN' - not compressed , 'BL' - LZ compressed,
+// 'B7' - LZMA compressed)
+// - The second line contains the path to the input file. Uses '/' to
+// separate path components.
+"""
+
+def CreateResourceInputFile(
+    output_dir, archive_file, resource_file_path,
+    component_build, staging_dir):
+  """Creates resource input file for installer target."""
+
+  # An array of (file, type, path) tuples of the files to be included.
+  resources = [(archive_file, 'B7',
+                    os.path.join(output_dir, archive_file))]
+
+  with open(resource_file_path, 'w') as f:
+    f.write(_RESOURCE_FILE_HEADER)
+    for (file, type, path) in resources:
+      f.write('\n%s  %s\n    "%s"\n' % (file, type, path.replace("\\","/")))
+
+
+def ParseDLLsFromDeps(build_dir, runtime_deps_file):
+  """Parses the runtime_deps file and returns the set of DLLs in it, relative
+  to build_dir."""
+  build_dlls = set()
+  args = open(runtime_deps_file).read()
+  for l in args.splitlines():
+    if os.path.splitext(l)[1] == ".dll":
+      build_dlls.add(os.path.join(build_dir, l))
+  return build_dlls
+
+# Copies component build DLLs for the setup to be able to find those DLLs at
+# run-time.
+# This is meant for developer builds only and should never be used to package
+# an official build.
+def DoComponentBuildTasks(staging_dir, build_dir, setup_runtime_deps):
+  installer_dir = os.path.join(staging_dir, UPDATER_DIR)
+  if not os.path.exists(installer_dir):
+    os.mkdir(installer_dir)
+
+  setup_component_dlls = ParseDLLsFromDeps(build_dir, setup_runtime_deps)
+
+  for setup_component_dll in setup_component_dlls:
+    g_archive_inputs.append(setup_component_dll)
+    shutil.copy(setup_component_dll, installer_dir)
+
+def main(options):
+  """Main method that reads input file, creates archive file and writes
+  resource input file.
+  """
+  config = Readconfig(options.input_file)
+
+  staging_dir = MakeStagingDirectory(options.staging_dir)
+
+  # Copy the files from the build dir.
+  CopyAllFilesToStagingDir(config, staging_dir, options.build_dir)
+
+  if options.component_build == '1':
+    DoComponentBuildTasks(staging_dir, options.build_dir,
+                          options.setup_runtime_deps)
+
+  # Name of the archive file built (for example - updater.7z)
+  archive_file = CreateArchiveFile(options, staging_dir)
+  CreateResourceInputFile(options.output_dir,
+                          archive_file, options.resource_file_path,
+                          options.component_build == '1', staging_dir)
+
+def _ParseOptions():
+  parser = optparse.OptionParser()
+  parser.add_option('-i', '--input_file',
+      help='Input file describing which files to archive.')
+  parser.add_option('-b', '--build_dir',
+      help='Build directory. The paths in input_file are relative to this.')
+  parser.add_option('--staging_dir',
+      help='Staging directory where intermediate files and directories '
+           'will be created')
+  parser.add_option('-o', '--output_dir',
+      help='The output directory where the archives will be written. '
+            'Defaults to the build_dir.')
+  parser.add_option('--resource_file_path',
+      help='The path where the resource file will be output. ')
+  parser.add_option('-s', '--skip_rebuild_archive',
+      default="False", help='Skip re-building updater.7z archive if it exists.')
+  parser.add_option('-n', '--output_name', default='updater',
+      help='Name used to prefix names of generated archives.')
+  parser.add_option('--component_build', default='0',
+      help='Whether this archive is packaging a component build.')
+  parser.add_option('--skip_archive_compression',
+      action='store_true', default=False,
+      help='Turn off compression of updater.7z into updater.packed.7z and '
+           'helpfully delete any old updater.packed.7z in |output_dir|.')
+  parser.add_option('--depfile',
+      help='Generate a depfile with the given name listing the implicit inputs '
+           'to the archive process that can be used with a build system.')
+  parser.add_option('--setup_runtime_deps',
+      help='A file listing runtime dependencies for setup.exe. This will be '
+           'used to get a list of DLLs to archive in a component build.')
+  parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
+                    default=False)
+
+  options, _ = parser.parse_args()
+  if not options.build_dir:
+    parser.error('You must provide a build dir.')
+
+  options.build_dir = os.path.normpath(options.build_dir)
+
+  if not options.staging_dir:
+    parser.error('You must provide a staging dir.')
+
+  if not options.input_file:
+    parser.error('You must provide an input file')
+
+  is_component_build = options.component_build == '1'
+  if is_component_build and not options.setup_runtime_deps:
+    parser.error("updater_runtime_deps must be specified for a component build")
+
+  if not options.output_dir:
+    options.output_dir = options.build_dir
+
+  return options
+
+
+if '__main__' == __name__:
+  options = _ParseOptions()
+  if options.verbose:
+    print sys.argv
+  sys.exit(main(options))
diff --git a/src/cobalt/updater/win/installer/exit_code.h b/src/cobalt/updater/win/installer/exit_code.h
new file mode 100644
index 0000000..a970400
--- /dev/null
+++ b/src/cobalt/updater/win/installer/exit_code.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_EXIT_CODE_H_
+#define CHROME_UPDATER_WIN_INSTALLER_EXIT_CODE_H_
+
+namespace updater {
+
+// Installer process exit codes (the underlying type is uint32_t).
+enum ExitCode {
+  SUCCESS_EXIT_CODE = 0,
+  GENERIC_INITIALIZATION_FAILURE = 101,
+  COMMAND_STRING_OVERFLOW = 105,
+  WAIT_FOR_PROCESS_FAILED = 107,
+  PATH_STRING_OVERFLOW = 108,
+  UNABLE_TO_GET_WORK_DIRECTORY = 109,
+  UNABLE_TO_EXTRACT_ARCHIVE = 112,
+  UNABLE_TO_SET_DIRECTORY_ACL = 117,
+  INVALID_OPTION = 118,
+  RUN_SETUP_FAILED_FILE_NOT_FOUND = 122,            // ERROR_FILE_NOT_FOUND.
+  RUN_SETUP_FAILED_PATH_NOT_FOUND = 123,            // ERROR_PATH_NOT_FOUND.
+  RUN_SETUP_FAILED_COULD_NOT_CREATE_PROCESS = 124,  // All other errors.
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_EXIT_CODE_H_
diff --git a/src/cobalt/updater/win/installer/installer.cc b/src/cobalt/updater/win/installer/installer.cc
new file mode 100644
index 0000000..787a3da
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer.cc
@@ -0,0 +1,460 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// GoogleUpdateSetup.exe is the first exe that is run when chrome is being
+// installed. It has two main jobs:
+//   1) unpack the resources (possibly decompressing some)
+//   2) run the real installer (updater.exe) with appropriate flags (--install).
+//
+// All files needed by the updater are archived together as an uncompressed
+// LZMA file, which is further compressed as one file, and inserted as a
+// binary resource in the resource section of the setup program.
+
+#include "chrome/updater/win/installer/installer.h"
+
+// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036.  See the
+// "Community Additions" comment on MSDN here:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
+#define SystemFunction036 NTAPI SystemFunction036
+#include <NTSecAPI.h>
+#undef SystemFunction036
+
+#include <sddl.h>
+#include <shellapi.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <initializer_list>
+
+// TODO(sorin): remove the dependecies on //base/ to reduce the code size.
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "chrome/installer/util/lzma_util.h"
+#include "chrome/installer/util/self_cleaning_temp_dir.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/updater/win/installer/configuration.h"
+#include "chrome/updater/win/installer/installer_constants.h"
+#include "chrome/updater/win/installer/pe_resource.h"
+#include "chrome/updater/win/installer/regkey.h"
+
+namespace updater {
+
+namespace {
+
+// Initializes |temp_path| to "Temp" within the target directory, and
+// |unpack_path| to a random directory beginning with "source" within
+// |temp_path|. Returns false on error.
+bool CreateTemporaryAndUnpackDirectories(
+    installer::SelfCleaningTempDir* temp_path,
+    base::FilePath* unpack_path) {
+  DCHECK(temp_path && unpack_path);
+
+  base::FilePath temp_dir;
+  if (!base::PathService::Get(base::DIR_TEMP, &temp_dir))
+    return false;
+
+  if (!temp_path->Initialize(temp_dir, kTempPrefix)) {
+    PLOG(ERROR) << "Could not create temporary path.";
+    return false;
+  }
+  VLOG(1) << "Created path " << temp_path->path().value();
+
+  if (!base::CreateTemporaryDirInDir(temp_path->path(), L"source",
+                                     unpack_path)) {
+    PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+using PathString = StackString<MAX_PATH>;
+
+// This structure passes data back and forth for the processing
+// of resource callbacks.
+struct Context {
+  // Input to the call back method. Specifies the dir to save resources into.
+  const wchar_t* base_path = nullptr;
+
+  // First output from call back method. Specifies the path of resource archive.
+  PathString* updater_resource_path = nullptr;
+};
+
+// Calls CreateProcess with good default parameters and waits for the process to
+// terminate returning the process exit code. In case of CreateProcess failure,
+// returns a results object with the provided codes as follows:
+// - ERROR_FILE_NOT_FOUND: (file_not_found_code, attributes of setup.exe).
+// - ERROR_PATH_NOT_FOUND: (path_not_found_code, attributes of setup.exe).
+// - Otherwise: (generic_failure_code, CreateProcess error code).
+// In case of error waiting for the process to exit, returns a results object
+// with (WAIT_FOR_PROCESS_FAILED, last error code). Otherwise, returns a results
+// object with the subprocess's exit code.
+ProcessExitResult RunProcessAndWait(const wchar_t* exe_path, wchar_t* cmdline) {
+  STARTUPINFOW si = {sizeof(si)};
+  PROCESS_INFORMATION pi = {0};
+  if (!::CreateProcess(exe_path, cmdline, nullptr, nullptr, FALSE,
+                       CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) {
+    // Split specific failure modes. If the process couldn't be launched because
+    // its file/path couldn't be found, report its attributes in ExtraCode1.
+    // This will help diagnose the prevalence of launch failures due to Image
+    // File Execution Options tampering. See https://crbug.com/672813 for more
+    // details.
+    const DWORD last_error = ::GetLastError();
+    const DWORD attributes = ::GetFileAttributes(exe_path);
+    switch (last_error) {
+      case ERROR_FILE_NOT_FOUND:
+        return ProcessExitResult(RUN_SETUP_FAILED_FILE_NOT_FOUND, attributes);
+      case ERROR_PATH_NOT_FOUND:
+        return ProcessExitResult(RUN_SETUP_FAILED_PATH_NOT_FOUND, attributes);
+      default:
+        break;
+    }
+    // Lump all other errors into a distinct failure bucket.
+    return ProcessExitResult(RUN_SETUP_FAILED_COULD_NOT_CREATE_PROCESS,
+                             last_error);
+  }
+
+  ::CloseHandle(pi.hThread);
+
+  DWORD exit_code = SUCCESS_EXIT_CODE;
+  DWORD wr = ::WaitForSingleObject(pi.hProcess, INFINITE);
+  if (WAIT_OBJECT_0 != wr || !::GetExitCodeProcess(pi.hProcess, &exit_code)) {
+    // Note:  We've assumed that WAIT_OBJCT_0 != wr means a failure.  The call
+    // could return a different object but since we never spawn more than one
+    // sub-process at a time that case should never happen.
+    return ProcessExitResult(WAIT_FOR_PROCESS_FAILED, ::GetLastError());
+  }
+
+  ::CloseHandle(pi.hProcess);
+
+  return ProcessExitResult(exit_code);
+}
+
+// Windows defined callback used in the EnumResourceNames call. For each
+// matching resource found, the callback is invoked and at this point we write
+// it to disk. We expect resource names to start with the 'updater' prefix.
+// Any other name is treated as an error.
+BOOL CALLBACK OnResourceFound(HMODULE module,
+                              const wchar_t* type,
+                              wchar_t* name,
+                              LONG_PTR context) {
+  Context* ctx = reinterpret_cast<Context*>(context);
+  if (!ctx)
+    return FALSE;
+
+  if (!StrStartsWith(name, kUpdaterArchivePrefix))
+    return FALSE;
+
+  PEResource resource(name, type, module);
+  if (!resource.IsValid() || resource.Size() < 1)
+    return FALSE;
+
+  PathString full_path;
+  if (!full_path.assign(ctx->base_path) || !full_path.append(name) ||
+      !resource.WriteToDisk(full_path.get())) {
+    return FALSE;
+  }
+
+  if (!ctx->updater_resource_path->assign(full_path.get()))
+    return FALSE;
+
+  return TRUE;
+}
+
+// Finds and writes to disk resources of type 'B7' (7zip archive). Returns false
+// if there is a problem in writing any resource to disk.
+ProcessExitResult UnpackBinaryResources(const Configuration& configuration,
+                                        HMODULE module,
+                                        const wchar_t* base_path,
+                                        PathString* archive_path) {
+  // Prepare the input to OnResourceFound method that needs a location where
+  // it will write all the resources.
+  Context context = {base_path, archive_path};
+
+  // Get the resources of type 'B7' (7zip archive).
+  if (!::EnumResourceNames(module, kLZMAResourceType, OnResourceFound,
+                           reinterpret_cast<LONG_PTR>(&context))) {
+    return ProcessExitResult(UNABLE_TO_EXTRACT_ARCHIVE, ::GetLastError());
+  }
+
+  if (archive_path->length() == 0)
+    return ProcessExitResult(UNABLE_TO_EXTRACT_ARCHIVE);
+
+  ProcessExitResult exit_code = ProcessExitResult(SUCCESS_EXIT_CODE);
+
+  return exit_code;
+}
+
+// Executes updater.exe, waits for it to finish and returns the exit code.
+ProcessExitResult RunSetup(const Configuration& configuration,
+                           const wchar_t* setup_path) {
+  PathString setup_exe;
+
+  if (*setup_path != L'\0') {
+    if (!setup_exe.assign(setup_path))
+      return ProcessExitResult(COMMAND_STRING_OVERFLOW);
+  }
+
+  CommandString cmd_line;
+
+  // Put the quoted path to setup.exe in cmd_line first.
+  if (!cmd_line.assign(L"\"") || !cmd_line.append(setup_exe.get()) ||
+      !cmd_line.append(L"\"")) {
+    return ProcessExitResult(COMMAND_STRING_OVERFLOW);
+  }
+
+  if (!cmd_line.append(L" --install --enable-logging --v=1"))
+    return ProcessExitResult(COMMAND_STRING_OVERFLOW);
+
+  return RunProcessAndWait(setup_exe.get(), cmd_line.get());
+}
+
+// Returns true if the supplied path supports ACLs.
+bool IsAclSupportedForPath(const wchar_t* path) {
+  PathString volume;
+  DWORD flags = 0;
+  return ::GetVolumePathName(path, volume.get(),
+                             static_cast<DWORD>(volume.capacity())) &&
+         ::GetVolumeInformation(volume.get(), nullptr, 0, nullptr, nullptr,
+                                &flags, nullptr, 0) &&
+         (flags & FILE_PERSISTENT_ACLS);
+}
+
+// Retrieves the SID of the default owner for objects created by this user
+// token (accounting for different behavior under UAC elevation, etc.).
+// NOTE: On success the |sid| parameter must be freed with LocalFree().
+bool GetCurrentOwnerSid(wchar_t** sid) {
+  HANDLE token;
+  if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
+    return false;
+
+  DWORD size = 0;
+  bool result = false;
+  // We get the TokenOwner rather than the TokenUser because e.g. under UAC
+  // elevation we want the admin to own the directory rather than the user.
+  ::GetTokenInformation(token, TokenOwner, nullptr, 0, &size);
+  if (size && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+    if (TOKEN_OWNER* owner =
+            reinterpret_cast<TOKEN_OWNER*>(::LocalAlloc(LPTR, size))) {
+      if (::GetTokenInformation(token, TokenOwner, owner, size, &size))
+        result = !!::ConvertSidToStringSid(owner->Owner, sid);
+      ::LocalFree(owner);
+    }
+  }
+  ::CloseHandle(token);
+  return result;
+}
+
+// Populates |sd| suitable for use when creating directories within |path| with
+// ACLs allowing access to only the current owner, admin, and system.
+// NOTE: On success the |sd| parameter must be freed with LocalFree().
+bool SetSecurityDescriptor(const wchar_t* path, PSECURITY_DESCRIPTOR* sd) {
+  *sd = nullptr;
+  // We succeed without doing anything if ACLs aren't supported.
+  if (!IsAclSupportedForPath(path))
+    return true;
+
+  wchar_t* sid = nullptr;
+  if (!GetCurrentOwnerSid(&sid))
+    return false;
+
+  // The largest SID is under 200 characters, so 300 should give enough slack.
+  StackString<300> sddl;
+  bool result = sddl.append(
+                    L"D:PAI"         // Protected, auto-inherited DACL.
+                    L"(A;;FA;;;BA)"  // Admin: Full control.
+                    L"(A;OIIOCI;GA;;;BA)"
+                    L"(A;;FA;;;SY)"  // System: Full control.
+                    L"(A;OIIOCI;GA;;;SY)"
+                    L"(A;OIIOCI;GA;;;CO)"  // Owner: Full control.
+                    L"(A;;FA;;;") &&
+                sddl.append(sid) && sddl.append(L")");
+  if (result) {
+    result = !!::ConvertStringSecurityDescriptorToSecurityDescriptor(
+        sddl.get(), SDDL_REVISION_1, sd, nullptr);
+  }
+
+  ::LocalFree(sid);
+  return result;
+}
+
+// Creates a temporary directory under |base_path| and returns the full path
+// of created directory in |work_dir|. If successful return true, otherwise
+// false.  When successful, the returned |work_dir| will always have a trailing
+// backslash and this function requires that |base_path| always includes a
+// trailing backslash as well.
+// We do not use GetTempFileName here to avoid running into AV software that
+// might hold on to the temp file as soon as we create it and then we can't
+// delete it and create a directory in its place.  So, we use our own mechanism
+// for creating a directory with a hopefully-unique name.  In the case of a
+// collision, we retry a few times with a new name before failing.
+bool CreateWorkDir(const wchar_t* base_path,
+                   PathString* work_dir,
+                   ProcessExitResult* exit_code) {
+  *exit_code = ProcessExitResult(PATH_STRING_OVERFLOW);
+  if (!work_dir->assign(base_path) || !work_dir->append(kTempPrefix))
+    return false;
+
+  // Store the location where we'll append the id.
+  size_t end = work_dir->length();
+
+  // Check if we'll have enough buffer space to continue.
+  // The name of the directory will use up 11 chars and then we need to append
+  // the trailing backslash and a terminator.  We've already added the prefix
+  // to the buffer, so let's just make sure we've got enough space for the rest.
+  if ((work_dir->capacity() - end) < (_countof("fffff.tmp") + 1))
+    return false;
+
+  // Add an ACL if supported by the filesystem. Otherwise system-level installs
+  // are potentially vulnerable to file squatting attacks.
+  SECURITY_ATTRIBUTES sa = {};
+  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+  if (!SetSecurityDescriptor(base_path, &sa.lpSecurityDescriptor)) {
+    *exit_code =
+        ProcessExitResult(UNABLE_TO_SET_DIRECTORY_ACL, ::GetLastError());
+    return false;
+  }
+
+  unsigned int id;
+  *exit_code = ProcessExitResult(UNABLE_TO_GET_WORK_DIRECTORY);
+  for (int max_attempts = 10; max_attempts; --max_attempts) {
+    ::RtlGenRandom(&id, sizeof(id));  // Try a different name.
+
+    // This converts 'id' to a string in the format "78563412" on windows
+    // because of little endianness, but we don't care since it's just
+    // a name. Since we checked capaity at the front end, we don't need to
+    // duplicate it here.
+    HexEncode(&id, sizeof(id), work_dir->get() + end,
+              work_dir->capacity() - end);
+
+    // We only want the first 5 digits to remain within the 8.3 file name
+    // format (compliant with previous implementation).
+    work_dir->truncate_at(end + 5);
+
+    // for consistency with the previous implementation which relied on
+    // GetTempFileName, we append the .tmp extension.
+    work_dir->append(L".tmp");
+
+    if (::CreateDirectory(work_dir->get(),
+                          sa.lpSecurityDescriptor ? &sa : nullptr)) {
+      // Yay!  Now let's just append the backslash and we're done.
+      work_dir->append(L"\\");
+      *exit_code = ProcessExitResult(SUCCESS_EXIT_CODE);
+      break;
+    }
+  }
+
+  if (sa.lpSecurityDescriptor)
+    LocalFree(sa.lpSecurityDescriptor);
+  return exit_code->IsSuccess();
+}
+
+// Creates and returns a temporary directory in |work_dir| that can be used to
+// extract updater payload. |work_dir| ends with a path separator.
+bool GetWorkDir(HMODULE module,
+                PathString* work_dir,
+                ProcessExitResult* exit_code) {
+  PathString base_path;
+  DWORD len =
+      ::GetTempPath(static_cast<DWORD>(base_path.capacity()), base_path.get());
+  if (!len || len >= base_path.capacity() ||
+      !CreateWorkDir(base_path.get(), work_dir, exit_code)) {
+    // Problem creating the work dir under TEMP path, so try using the
+    // current directory as the base path.
+    len = ::GetModuleFileName(module, base_path.get(),
+                              static_cast<DWORD>(base_path.capacity()));
+    if (len >= base_path.capacity() || !len)
+      return false;  // Can't even get current directory? Return an error.
+
+    wchar_t* name = GetNameFromPathExt(base_path.get(), len);
+    if (name == base_path.get())
+      return false;  // There was no directory in the string!  Bail out.
+
+    *name = L'\0';
+
+    *exit_code = ProcessExitResult(SUCCESS_EXIT_CODE);
+    return CreateWorkDir(base_path.get(), work_dir, exit_code);
+  }
+  return true;
+}
+
+// Returns true for ".." and "." directories.
+bool IsCurrentOrParentDirectory(const wchar_t* dir) {
+  return dir && dir[0] == L'.' &&
+         (dir[1] == L'\0' || (dir[1] == L'.' && dir[2] == L'\0'));
+}
+
+ProcessExitResult WMain(HMODULE module) {
+  ProcessExitResult exit_code = ProcessExitResult(SUCCESS_EXIT_CODE);
+
+  // Parse configuration from the command line and resources.
+  Configuration configuration;
+  if (!configuration.Initialize(module))
+    return ProcessExitResult(GENERIC_INITIALIZATION_FAILURE, ::GetLastError());
+
+  // Exit early if an invalid switch was found on the command line.
+  if (configuration.has_invalid_switch())
+    return ProcessExitResult(INVALID_OPTION);
+
+  // First get a path where we can extract the resource payload, which is
+  // a compressed LZMA archive of a single file.
+  base::ScopedTempDir base_path_owner;
+  PathString base_path;
+  if (!GetWorkDir(module, &base_path, &exit_code))
+    return exit_code;
+  if (!base_path_owner.Set(base::FilePath(base_path.get()))) {
+    ::DeleteFile(base_path.get());
+    return ProcessExitResult(static_cast<DWORD>(installer::TEMP_DIR_FAILED));
+  }
+
+  PathString compressed_archive;
+  exit_code = UnpackBinaryResources(configuration, module, base_path.get(),
+                                    &compressed_archive);
+
+  // Create a temp folder where the archives are unpacked.
+  base::FilePath unpack_path;
+  installer::SelfCleaningTempDir temp_path;
+  if (!CreateTemporaryAndUnpackDirectories(&temp_path, &unpack_path))
+    return ProcessExitResult(static_cast<DWORD>(installer::TEMP_DIR_FAILED));
+
+  // Unpack the compressed archive to extract the uncompressed archive file.
+  UnPackStatus unpack_status = UNPACK_NO_ERROR;
+  int32_t ntstatus = 0;
+  auto lzma_result =
+      UnPackArchive(base::FilePath(compressed_archive.get()), unpack_path,
+                    nullptr, &unpack_status, &ntstatus);
+  if (lzma_result)
+    return ProcessExitResult(static_cast<DWORD>(installer::UNPACKING_FAILED));
+
+  // Unpack the uncompressed archive to extract the updater files.
+  base::FilePath uncompressed_archive =
+      unpack_path.Append(FILE_PATH_LITERAL("updater.7z"));
+  lzma_result = UnPackArchive(uncompressed_archive, unpack_path, nullptr,
+                              &unpack_status, &ntstatus);
+  if (lzma_result)
+    return ProcessExitResult(static_cast<DWORD>(installer::UNPACKING_FAILED));
+
+  // While unpacking the binaries, we paged in a whole bunch of memory that
+  // we don't need anymore.  Let's give it back to the pool before running
+  // setup.
+  ::SetProcessWorkingSetSize(::GetCurrentProcess(), static_cast<SIZE_T>(-1),
+                             static_cast<SIZE_T>(-1));
+
+  PathString setup_path;
+  if (!setup_path.assign(unpack_path.value().c_str()) ||
+      !setup_path.append(L"\\bin\\updater.exe")) {
+    exit_code = ProcessExitResult(PATH_STRING_OVERFLOW);
+  }
+
+  if (exit_code.IsSuccess())
+    exit_code = RunSetup(configuration, setup_path.get());
+
+  return exit_code;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/installer/installer.exe.manifest b/src/cobalt/updater/win/installer/installer.exe.manifest
new file mode 100644
index 0000000..c7ac2fa
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer.exe.manifest
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <!--
+    Have compatibility section here instead of using
+      build/win/compatibility.manifest
+    to work around crbug.com/272660.
+    TODO(yukawa): Use build/win/compatibility.manifest again.
+  -->
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!--The ID below indicates application support for Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+      <!--The ID below indicates application support for Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+      <!--The ID below indicates application support for Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+      <!--The ID below indicates application support for Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+      <!--The ID below indicates application support for Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+    </application>
+  </compatibility>
+  <ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
+    <ms_asmv2:security>
+      <ms_asmv2:requestedPrivileges>
+        <ms_asmv2:requestedExecutionLevel level="asInvoker" />
+      </ms_asmv2:requestedPrivileges>
+    </ms_asmv2:security>
+  </ms_asmv2:trustInfo>
+</assembly>
diff --git a/src/cobalt/updater/win/installer/installer.h b/src/cobalt/updater/win/installer/installer.h
new file mode 100644
index 0000000..0a7b75e
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_INSTALLER_H_
+#define CHROME_UPDATER_WIN_INSTALLER_INSTALLER_H_
+
+#include <windows.h>
+
+#include "chrome/updater/win/installer/exit_code.h"
+#include "chrome/updater/win/installer/string.h"
+
+namespace updater {
+
+// A container of a process exit code (eventually passed to ExitProcess) and
+// a Windows error code for cases where the exit code is non-zero.
+struct ProcessExitResult {
+  DWORD exit_code;
+  DWORD windows_error;
+
+  explicit ProcessExitResult(DWORD exit) : exit_code(exit), windows_error(0) {}
+  ProcessExitResult(DWORD exit, DWORD win)
+      : exit_code(exit), windows_error(win) {}
+
+  bool IsSuccess() const { return exit_code == SUCCESS_EXIT_CODE; }
+};
+
+// A stack-based string large enough to hold an executable to run
+// (which is a path), plus a few extra arguments.
+using CommandString = StackString<MAX_PATH * 4>;
+
+// Main function for the installer.
+ProcessExitResult WMain(HMODULE module);
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_INSTALLER_H_
diff --git a/src/cobalt/updater/win/installer/installer.ico b/src/cobalt/updater/win/installer/installer.ico
new file mode 100644
index 0000000..5ecfcbd
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer.ico
Binary files differ
diff --git a/src/cobalt/updater/win/installer/installer.rc b/src/cobalt/updater/win/installer/installer.rc
new file mode 100644
index 0000000..76cb035
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer.rc
@@ -0,0 +1,55 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "installer_resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_MINI_INSTALLER      ICON                    "installer.ico"
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "installer_resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+    "#include ""windows.h""\r\n"
+    "#undef APSTUDIO_HIDDEN_SYMBOL\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
diff --git a/src/cobalt/updater/win/installer/installer_constants.cc b/src/cobalt/updater/win/installer/installer_constants.cc
new file mode 100644
index 0000000..f102f7a
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer_constants.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/installer/installer_constants.h"
+
+namespace updater {
+
+// The prefix of the updater archive resource.
+const wchar_t kUpdaterArchivePrefix[] = L"updater";
+
+// Temp directory prefix that this process creates.
+const wchar_t kTempPrefix[] = L"UPDATER";
+
+// 7zip archive.
+const wchar_t kLZMAResourceType[] = L"B7";
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/installer/installer_constants.h b/src/cobalt/updater/win/installer/installer_constants.h
new file mode 100644
index 0000000..fc0bf77
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer_constants.h
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_INSTALLER_CONSTANTS_H_
+#define CHROME_UPDATER_WIN_INSTALLER_INSTALLER_CONSTANTS_H_
+
+namespace updater {
+
+// Various filenames and prefixes.
+extern const wchar_t kUpdaterArchivePrefix[];
+extern const wchar_t kTempPrefix[];
+
+// The resource types that would be unpacked from the mini installer.
+extern const wchar_t kLZMAResourceType[];
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_INSTALLER_CONSTANTS_H_
diff --git a/src/cobalt/updater/win/installer/installer_main.cc b/src/cobalt/updater/win/installer/installer_main.cc
new file mode 100644
index 0000000..8c181ca
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer_main.cc
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "chrome/updater/win/installer/installer.h"
+
+// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+int WINAPI wWinMain(HINSTANCE /* instance */,
+                    HINSTANCE /* previous_instance */,
+                    LPWSTR /* command_line */,
+                    int /* command_show */) {
+  updater::ProcessExitResult result =
+      updater::WMain(reinterpret_cast<HMODULE>(&__ImageBase));
+
+  return result.exit_code;
+}
diff --git a/src/cobalt/updater/win/installer/installer_resource.h b/src/cobalt/updater/win/installer/installer_resource.h
new file mode 100644
index 0000000..2bf5e97
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer_resource.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_INSTALLER_RESOURCE_H_
+#define CHROME_UPDATER_WIN_INSTALLER_INSTALLER_RESOURCE_H_
+
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_INSTALLER_RESOURCE_H_
diff --git a/src/cobalt/updater/win/installer/installer_version.rc.version b/src/cobalt/updater/win/installer/installer_version.rc.version
new file mode 100644
index 0000000..bbe41b4
--- /dev/null
+++ b/src/cobalt/updater/win/installer/installer_version.rc.version
@@ -0,0 +1,44 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+// Use the ordinal 1 here, to avoid needing to #include a header file
+// to use the VS_VERSION_INFO macro. This header file changes with different
+// SDK versions which causes headaches building in some environments. The
+// VERSIONINFO resource will always be at index 1.
+1 VERSIONINFO
+ FILEVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@
+ PRODUCTVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "@COMPANY_FULLNAME@"
+            VALUE "FileDescription", "@PRODUCT_INSTALLER_FULLNAME@"
+            VALUE "FileVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@"
+            VALUE "InternalName", "Chrome Updater"
+            VALUE "LegalCopyright", "@COPYRIGHT@"
+            VALUE "ProductName", "@PRODUCT_INSTALLER_FULLNAME@"
+            VALUE "ProductVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@"
+            VALUE "CompanyShortName", "@COMPANY_SHORTNAME@"
+            VALUE "ProductShortName", "@PRODUCT_INSTALLER_SHORTNAME@"
+            VALUE "LastChange", "@LASTCHANGE@"
+            VALUE "Official Build", "@OFFICIAL_BUILD@"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff --git a/src/cobalt/updater/win/installer/pe_resource.cc b/src/cobalt/updater/win/installer/pe_resource.cc
new file mode 100644
index 0000000..248a133
--- /dev/null
+++ b/src/cobalt/updater/win/installer/pe_resource.cc
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/installer/pe_resource.h"
+
+namespace updater {
+
+PEResource::PEResource(const wchar_t* name, const wchar_t* type, HMODULE module)
+    : resource_(nullptr), module_(module) {
+  resource_ = ::FindResource(module, name, type);
+}
+
+bool PEResource::IsValid() {
+  return nullptr != resource_;
+}
+
+size_t PEResource::Size() {
+  return ::SizeofResource(module_, resource_);
+}
+
+bool PEResource::WriteToDisk(const wchar_t* full_path) {
+  // Resource handles are not real HGLOBALs so do not attempt to close them.
+  // Windows frees them whenever there is memory pressure.
+  HGLOBAL data_handle = ::LoadResource(module_, resource_);
+  if (nullptr == data_handle)
+    return false;
+
+  void* data = ::LockResource(data_handle);
+  if (nullptr == data)
+    return false;
+
+  size_t resource_size = Size();
+  HANDLE out_file = ::CreateFile(full_path, GENERIC_WRITE, 0, nullptr,
+                                 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+  if (INVALID_HANDLE_VALUE == out_file)
+    return false;
+
+  DWORD written = 0;
+  if (!::WriteFile(out_file, data, static_cast<DWORD>(resource_size), &written,
+                   nullptr)) {
+    ::CloseHandle(out_file);
+    return false;
+  }
+  return ::CloseHandle(out_file) ? true : false;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/installer/pe_resource.h b/src/cobalt/updater/win/installer/pe_resource.h
new file mode 100644
index 0000000..e19531a
--- /dev/null
+++ b/src/cobalt/updater/win/installer/pe_resource.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_PE_RESOURCE_H_
+#define CHROME_UPDATER_WIN_INSTALLER_PE_RESOURCE_H_
+
+#include <stddef.h>
+#include <windows.h>
+
+namespace updater {
+
+// This class models a windows PE resource. It does not pretend to be a full
+// API wrapper and it is just concerned with loading it to memory and writing
+// it to disk. Each resource is unique only in the context of a loaded module,
+// that is why you need to specify one on each constructor.
+class PEResource {
+ public:
+  // Takes the resource name, the resource type, and the module where
+  // to look for the resource. If the resource is found IsValid() returns true.
+  PEResource(const wchar_t* name, const wchar_t* type, HMODULE module);
+
+  // Returns true if the resource is valid.
+  bool IsValid();
+
+  // Returns the size in bytes of the resource. Returns zero if the resource is
+  // not valid.
+  size_t Size();
+
+  // Creates a file in |path| with a copy of the resource. If the resource can
+  // not be loaded into memory or if it cannot be written to disk it returns
+  // false.
+  bool WriteToDisk(const wchar_t* path);
+
+ private:
+  HRSRC resource_ = nullptr;
+  HMODULE module_ = nullptr;
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_PE_RESOURCE_H_
diff --git a/src/cobalt/updater/win/installer/regkey.cc b/src/cobalt/updater/win/installer/regkey.cc
new file mode 100644
index 0000000..62b622d
--- /dev/null
+++ b/src/cobalt/updater/win/installer/regkey.cc
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/installer/regkey.h"
+
+#include "chrome/updater/win/installer/installer_constants.h"
+#include "chrome/updater/win/installer/string.h"
+
+namespace updater {
+
+LONG RegKey::Open(HKEY key, const wchar_t* sub_key, REGSAM access) {
+  Close();
+  return ::RegOpenKeyEx(key, sub_key, 0, access, &key_);
+}
+
+LONG RegKey::ReadSZValue(const wchar_t* value_name,
+                         wchar_t* value,
+                         size_t value_size) const {
+  DWORD type = 0;
+  DWORD byte_length = static_cast<DWORD>(value_size * sizeof(wchar_t));
+  LONG result = ::RegQueryValueEx(key_, value_name, nullptr, &type,
+                                  reinterpret_cast<BYTE*>(value), &byte_length);
+  if (result == ERROR_SUCCESS) {
+    if (type != REG_SZ) {
+      result = ERROR_NOT_SUPPORTED;
+    } else if (byte_length < 2) {
+      *value = L'\0';
+    } else if (value[byte_length / sizeof(wchar_t) - 1] != L'\0') {
+      if ((byte_length / sizeof(wchar_t)) < value_size)
+        value[byte_length / sizeof(wchar_t)] = L'\0';
+      else
+        result = ERROR_MORE_DATA;
+    }
+  }
+  return result;
+}
+
+LONG RegKey::ReadDWValue(const wchar_t* value_name, DWORD* value) const {
+  DWORD type = 0;
+  DWORD byte_length = sizeof(*value);
+  LONG result = ::RegQueryValueEx(key_, value_name, nullptr, &type,
+                                  reinterpret_cast<BYTE*>(value), &byte_length);
+  if (result == ERROR_SUCCESS) {
+    if (type != REG_DWORD) {
+      result = ERROR_NOT_SUPPORTED;
+    } else if (byte_length != sizeof(*value)) {
+      result = ERROR_NO_DATA;
+    }
+  }
+  return result;
+}
+
+LONG RegKey::WriteSZValue(const wchar_t* value_name, const wchar_t* value) {
+  return ::RegSetValueEx(key_, value_name, 0, REG_SZ,
+                         reinterpret_cast<const BYTE*>(value),
+                         (lstrlen(value) + 1) * sizeof(wchar_t));
+}
+
+LONG RegKey::WriteDWValue(const wchar_t* value_name, DWORD value) {
+  return ::RegSetValueEx(key_, value_name, 0, REG_DWORD,
+                         reinterpret_cast<const BYTE*>(&value), sizeof(value));
+}
+
+void RegKey::Close() {
+  if (key_ != nullptr) {
+    ::RegCloseKey(key_);
+    key_ = nullptr;
+  }
+}
+
+// static
+bool RegKey::ReadSZValue(HKEY root_key,
+                         const wchar_t* sub_key,
+                         const wchar_t* value_name,
+                         wchar_t* value,
+                         size_t size) {
+  RegKey key;
+  return (key.Open(root_key, sub_key, KEY_QUERY_VALUE) == ERROR_SUCCESS &&
+          key.ReadSZValue(value_name, value, size) == ERROR_SUCCESS);
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/installer/regkey.h b/src/cobalt/updater/win/installer/regkey.h
new file mode 100644
index 0000000..5dd7c40
--- /dev/null
+++ b/src/cobalt/updater/win/installer/regkey.h
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_REGKEY_H_
+#define CHROME_UPDATER_WIN_INSTALLER_REGKEY_H_
+
+#include <stddef.h>
+#include <windows.h>
+
+namespace updater {
+
+// A helper class used to manipulate the Windows registry.
+class RegKey {
+ public:
+  RegKey() : key_(nullptr) {}
+  ~RegKey() { Close(); }
+
+  // Opens the key named |sub_key| with given |access| rights.  Returns
+  // ERROR_SUCCESS or some other error.
+  LONG Open(HKEY key, const wchar_t* sub_key, REGSAM access);
+
+  // Returns true if a key is open.
+  bool is_valid() const { return key_ != nullptr; }
+
+  // Read a value from the registry into the memory indicated by |value|
+  // (of |value_size| wchar_t units).  Returns ERROR_SUCCESS,
+  // ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, or some other error.  |value| is
+  // guaranteed to be null-terminated on success.
+  LONG ReadSZValue(const wchar_t* value_name,
+                   wchar_t* value,
+                   size_t value_size) const;
+  LONG ReadDWValue(const wchar_t* value_name, DWORD* value) const;
+
+  // Write a value to the registry.  SZ |value| must be null-terminated.
+  // Returns ERROR_SUCCESS or an error code.
+  LONG WriteSZValue(const wchar_t* value_name, const wchar_t* value);
+  LONG WriteDWValue(const wchar_t* value_name, DWORD value);
+
+  // Closes the key if it was open.
+  void Close();
+
+  // Helper function to read a value from registry.  Returns true if value
+  // is read successfully and stored in parameter value. Returns false
+  // otherwise. |size| is measured in wchar_t units.
+  static bool ReadSZValue(HKEY root_key,
+                          const wchar_t* sub_key,
+                          const wchar_t* value_name,
+                          wchar_t* value,
+                          size_t value_size);
+
+ private:
+  RegKey(const RegKey&);
+  RegKey& operator=(const RegKey&);
+
+  HKEY key_;
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_REGKEY_H_
diff --git a/src/cobalt/updater/win/installer/run_all_unittests.cc b/src/cobalt/updater/win/installer/run_all_unittests.cc
new file mode 100644
index 0000000..875ccb9
--- /dev/null
+++ b/src/cobalt/updater/win/installer/run_all_unittests.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+
+int main(int argc, char** argv) {
+  base::TestSuite test_suite(argc, argv);
+
+  return base::LaunchUnitTestsSerially(
+      argc, argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/src/cobalt/updater/win/installer/string.cc b/src/cobalt/updater/win/installer/string.cc
new file mode 100644
index 0000000..61120a1
--- /dev/null
+++ b/src/cobalt/updater/win/installer/string.cc
@@ -0,0 +1,118 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/installer/string.h"
+
+#include <windows.h>
+
+namespace {
+
+// Returns true if the given two ASCII characters are same (ignoring case).
+bool EqualASCIICharI(wchar_t a, wchar_t b) {
+  if (a >= L'A' && a <= L'Z')
+    a += (L'a' - L'A');
+  if (b >= L'A' && b <= L'Z')
+    b += (L'a' - L'A');
+  return (a == b);
+}
+
+}  // namespace
+
+namespace updater {
+
+// Formats a sequence of |bytes| as hex.  The |str| buffer must have room for
+// at least 2*|size| + 1.
+bool HexEncode(const void* bytes, size_t size, wchar_t* str, size_t str_size) {
+  if (str_size <= (size * 2))
+    return false;
+
+  static const wchar_t kHexChars[] = L"0123456789ABCDEF";
+
+  str[size * 2] = L'\0';
+
+  for (size_t i = 0; i < size; ++i) {
+    char b = reinterpret_cast<const char*>(bytes)[i];
+    str[(i * 2)] = kHexChars[(b >> 4) & 0xf];
+    str[(i * 2) + 1] = kHexChars[b & 0xf];
+  }
+
+  return true;
+}
+
+size_t SafeStrLen(const wchar_t* str, size_t alloc_size) {
+  if (!str || !alloc_size)
+    return 0;
+  size_t len = 0;
+  while (--alloc_size && str[len] != L'\0')
+    ++len;
+  return len;
+}
+
+bool SafeStrCopy(wchar_t* dest, size_t dest_size, const wchar_t* src) {
+  if (!dest || !dest_size)
+    return false;
+
+  wchar_t* write = dest;
+  for (size_t remaining = dest_size; remaining != 0; --remaining) {
+    if ((*write++ = *src++) == L'\0')
+      return true;
+  }
+
+  // If we fail, we do not want to leave the string with partially copied
+  // contents.  The reason for this is that we use these strings mostly for
+  // named objects such as files.  If we copy a partial name, then that could
+  // match with something we do not want it to match with.
+  // Furthermore, since SafeStrCopy is called from SafeStrCat, we do not
+  // want to mutate the string in case the caller handles the error of a
+  // failed concatenation.  For example:
+  //
+  // wchar_t buf[5] = {0};
+  // if (!SafeStrCat(buf, _countof(buf), kLongName))
+  //   SafeStrCat(buf, _countof(buf), kShortName);
+  //
+  // If we were to return false in the first call to SafeStrCat but still
+  // mutate the buffer, the buffer will be in an unexpected state.
+  *dest = L'\0';
+  return false;
+}
+
+// Safer replacement for lstrcat function.
+bool SafeStrCat(wchar_t* dest, size_t dest_size, const wchar_t* src) {
+  // Use SafeStrLen instead of lstrlen just in case the |dest| buffer isn't
+  // terminated.
+  size_t str_len = SafeStrLen(dest, dest_size);
+  return SafeStrCopy(dest + str_len, dest_size - str_len, src);
+}
+
+bool StrStartsWith(const wchar_t* str, const wchar_t* start_str) {
+  if (str == nullptr || start_str == nullptr)
+    return false;
+
+  for (int i = 0; start_str[i] != L'\0'; ++i) {
+    if (!EqualASCIICharI(str[i], start_str[i]))
+      return false;
+  }
+
+  return true;
+}
+
+const wchar_t* GetNameFromPathExt(const wchar_t* path, size_t size) {
+  if (!size)
+    return path;
+
+  const wchar_t* current = &path[size - 1];
+  while (current != path && L'\\' != *current)
+    --current;
+
+  // If no path separator found, just return |path|.
+  // Otherwise, return a pointer right after the separator.
+  return ((current == path) && (L'\\' != *current)) ? current : (current + 1);
+}
+
+wchar_t* GetNameFromPathExt(wchar_t* path, size_t size) {
+  return const_cast<wchar_t*>(
+      GetNameFromPathExt(const_cast<const wchar_t*>(path), size));
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/installer/string.h b/src/cobalt/updater/win/installer/string.h
new file mode 100644
index 0000000..a04e7f7
--- /dev/null
+++ b/src/cobalt/updater/win/installer/string.h
@@ -0,0 +1,117 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_INSTALLER_STRING_H_
+#define CHROME_UPDATER_WIN_INSTALLER_STRING_H_
+
+#include <stddef.h>
+
+namespace updater {
+
+// NOTE: Do not assume that these string functions support UTF encoding.
+// This is fine for the purposes of the mini_installer, but you have
+// been warned!
+
+// Formats a sequence of |bytes| as hex.  The |str| buffer must have room for
+// at least 2*|size| + 1.
+bool HexEncode(const void* bytes, size_t size, wchar_t* str, size_t str_size);
+
+// Counts the number of characters in the string up to a maximum of
+// alloc_size.  The highest return value from this function can therefore be
+// alloc_size - 1 since |alloc_size| includes the \0 terminator.
+size_t SafeStrLen(const wchar_t* str, size_t alloc_size);
+
+// Simple replacement for CRT string copy method that does not overflow.
+// Returns true if the source was copied successfully otherwise returns false.
+// Parameter src is assumed to be nullptr terminated and the nullptr character
+// is copied over to string dest.
+bool SafeStrCopy(wchar_t* dest, size_t dest_size, const wchar_t* src);
+
+// Simple replacement for CRT string copy method that does not overflow.
+// Returns true if the source was copied successfully otherwise returns false.
+// Parameter src is assumed to be nullptr terminated and the nullptr character
+// is copied over to string dest.  If the return value is false, the |dest|
+// string should be the same as it was before.
+bool SafeStrCat(wchar_t* dest, size_t dest_size, const wchar_t* src);
+
+// Function to check if a string (specified by str) starts with another string
+// (specified by start_str). The comparison is string insensitive.
+bool StrStartsWith(const wchar_t* str, const wchar_t* start_str);
+
+// Takes the path to file and returns a pointer to the basename component.
+// Example input -> output:
+//     c:\full\path\to\file.ext -> file.ext
+//     file.ext -> file.ext
+// Note: |size| is the number of characters in |path| not including the string
+// terminator.
+const wchar_t* GetNameFromPathExt(const wchar_t* path, size_t size);
+wchar_t* GetNameFromPathExt(wchar_t* path, size_t size);
+
+// A string class that manages a fixed size buffer on the stack.
+// The methods in the class are based on the above string methods and the
+// class additionally is careful about proper buffer termination.
+template <size_t kCapacity>
+class StackString {
+ public:
+  StackString() {
+    static_assert(kCapacity != 0, "invalid buffer size");
+    buffer_[kCapacity] = L'\0';  // We always reserve 1 more than asked for.
+    clear();
+  }
+
+  // We do not expose a constructor that accepts a string pointer on purpose.
+  // We expect the caller to call assign() and handle failures.
+
+  // Returns the number of reserved characters in this buffer, _including_
+  // the reserved char for the terminator.
+  size_t capacity() const { return kCapacity; }
+
+  wchar_t* get() { return buffer_; }
+
+  bool assign(const wchar_t* str) {
+    return SafeStrCopy(buffer_, kCapacity, str);
+  }
+
+  bool append(const wchar_t* str) {
+    return SafeStrCat(buffer_, kCapacity, str);
+  }
+
+  void clear() { buffer_[0] = L'\0'; }
+
+  size_t length() const { return SafeStrLen(buffer_, kCapacity); }
+
+  // Does a case insensitive search for a substring.
+  const wchar_t* findi(const wchar_t* find) const {
+    return SearchStringI(buffer_, find);
+  }
+
+  // Case insensitive string compare.
+  int comparei(const wchar_t* str) const { return lstrcmpiW(buffer_, str); }
+
+  // Case sensitive string compare.
+  int compare(const wchar_t* str) const { return lstrcmpW(buffer_, str); }
+
+  // Terminates the string at the specified location.
+  // Note: this method has no effect if this object's length is less than
+  // |location|.
+  bool truncate_at(size_t location) {
+    if (location >= kCapacity)
+      return false;
+    buffer_[location] = L'\0';
+    return true;
+  }
+
+ protected:
+  // We reserve 1 more than what is asked for as a safeguard against
+  // off-by-one errors.
+  wchar_t buffer_[kCapacity + 1];
+
+ private:
+  StackString(const StackString&);
+  StackString& operator=(const StackString&);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_INSTALLER_STRING_H_
diff --git a/src/cobalt/updater/win/installer/string_unittest.cc b/src/cobalt/updater/win/installer/string_unittest.cc
new file mode 100644
index 0000000..d36b3a0
--- /dev/null
+++ b/src/cobalt/updater/win/installer/string_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <windows.h>
+
+#include <string>
+
+#include "chrome/updater/win/installer/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using updater::StackString;
+
+namespace {
+class InstallerStringTest : public testing::Test {
+ protected:
+  void SetUp() override {}
+  void TearDown() override {}
+};
+}  // namespace
+
+// Tests the strcat/strcpy/length support of the StackString class.
+TEST_F(InstallerStringTest, StackStringOverflow) {
+  static const wchar_t kTestString[] = L"1234567890";
+
+  StackString<MAX_PATH> str;
+  EXPECT_EQ(static_cast<size_t>(MAX_PATH), str.capacity());
+
+  std::wstring compare_str;
+
+  EXPECT_EQ(str.length(), compare_str.length());
+  EXPECT_EQ(0, compare_str.compare(str.get()));
+
+  size_t max_chars = str.capacity() - 1;
+
+  while ((str.length() + (_countof(kTestString) - 1)) <= max_chars) {
+    EXPECT_TRUE(str.append(kTestString));
+    compare_str.append(kTestString);
+    EXPECT_EQ(str.length(), compare_str.length());
+    EXPECT_EQ(0, compare_str.compare(str.get()));
+  }
+
+  EXPECT_GT(static_cast<size_t>(MAX_PATH), str.length());
+
+  // Now we've exhausted the space we allocated for the string,
+  // so append should fail.
+  EXPECT_FALSE(str.append(kTestString));
+
+  // ...and remain unchanged.
+  EXPECT_EQ(0, compare_str.compare(str.get()));
+  EXPECT_EQ(str.length(), compare_str.length());
+
+  // Last test for fun.
+  str.clear();
+  compare_str.clear();
+  EXPECT_EQ(0, compare_str.compare(str.get()));
+  EXPECT_EQ(str.length(), compare_str.length());
+}
diff --git a/src/cobalt/updater/win/installer/updater.release b/src/cobalt/updater/win/installer/updater.release
new file mode 100644
index 0000000..9d20d88
--- /dev/null
+++ b/src/cobalt/updater/win/installer/updater.release
@@ -0,0 +1,3 @@
+[GENERAL]
+updater.exe: %(UpdaterDir)s\
+gen\chrome\updater\win\uninstall.cmd: %(UpdaterDir)s\
diff --git a/src/cobalt/updater/win/main.cc b/src/cobalt/updater/win/main.cc
new file mode 100644
index 0000000..59bbc94
--- /dev/null
+++ b/src/cobalt/updater/win/main.cc
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "chrome/updater/updater.h"
+
+int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev, wchar_t*, int) {
+  return updater::UpdaterMain(0, nullptr);
+}
diff --git a/src/cobalt/updater/win/net/net_util.cc b/src/cobalt/updater/win/net/net_util.cc
new file mode 100644
index 0000000..ba26d4b
--- /dev/null
+++ b/src/cobalt/updater/win/net/net_util.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/net/net_util.h"
+
+#include <vector>
+#include "base/strings/string_piece.h"
+
+namespace updater {
+
+HRESULT QueryHeadersString(HINTERNET request_handle,
+                           uint32_t info_level,
+                           base::StringPiece16 name,
+                           base::string16* value) {
+  DWORD num_bytes = 0;
+  ::WinHttpQueryHeaders(request_handle, info_level, name.data(),
+                        WINHTTP_NO_OUTPUT_BUFFER, &num_bytes,
+                        WINHTTP_NO_HEADER_INDEX);
+  auto hr = HRESULTFromLastError();
+  if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+    return hr;
+  std::vector<base::char16> buffer(num_bytes / sizeof(base::char16));
+  if (!::WinHttpQueryHeaders(request_handle, info_level, name.data(),
+                             &buffer.front(), &num_bytes,
+                             WINHTTP_NO_HEADER_INDEX)) {
+    return HRESULTFromLastError();
+  }
+  DCHECK_EQ(0u, num_bytes % sizeof(base::char16));
+  buffer.resize(num_bytes / sizeof(base::char16));
+  value->assign(buffer.begin(), buffer.end());
+  return S_OK;
+}
+
+HRESULT QueryHeadersInt(HINTERNET request_handle,
+                        uint32_t info_level,
+                        base::StringPiece16 name,
+                        int* value) {
+  info_level |= WINHTTP_QUERY_FLAG_NUMBER;
+  DWORD num_bytes = sizeof(*value);
+  if (!::WinHttpQueryHeaders(request_handle, info_level, name.data(), value,
+                             &num_bytes, WINHTTP_NO_HEADER_INDEX)) {
+    return HRESULTFromLastError();
+  }
+
+  return S_OK;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/net/net_util.h b/src/cobalt/updater/win/net/net_util.h
new file mode 100644
index 0000000..cf0d309
--- /dev/null
+++ b/src/cobalt/updater/win/net/net_util.h
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_NET_NET_UTIL_H_
+#define CHROME_UPDATER_WIN_NET_NET_UTIL_H_
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_piece_forward.h"
+#include "chrome/updater/win/util.h"
+
+namespace updater {
+
+HRESULT QueryHeadersString(HINTERNET request_handle,
+                           uint32_t info_level,
+                           base::StringPiece16 name,
+                           base::string16* value);
+
+HRESULT QueryHeadersInt(HINTERNET request_handle,
+                        uint32_t info_level,
+                        base::StringPiece16 name,
+                        int* value);
+
+// Queries WinHTTP options for the given |handle|. Returns S_OK if the call
+// is successful.
+template <typename T>
+HRESULT QueryOption(HINTERNET handle, uint32_t option, T* value) {
+  auto num_bytes = sizeof(*value);
+  if (!::WinHttpQueryOption(handle, option, value, &num_bytes)) {
+    DCHECK_EQ(sizeof(*value), num_bytes);
+    return HRESULTFromLastError();
+  }
+  return S_OK;
+}
+
+// Sets WinHTTP options for the given |handle|. Returns S_OK if the call
+// is successful.
+template <typename T>
+HRESULT SetOption(HINTERNET handle, uint32_t option, T value) {
+  if (!::WinHttpSetOption(handle, option, &value, sizeof(value)))
+    return HRESULTFromLastError();
+  return S_OK;
+}
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_NET_NET_UTIL_H_
diff --git a/src/cobalt/updater/win/net/network.h b/src/cobalt/updater/win/net/network.h
new file mode 100644
index 0000000..6dbd601
--- /dev/null
+++ b/src/cobalt/updater/win/net/network.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_NET_NETWORK_H_
+#define CHROME_UPDATER_WIN_NET_NETWORK_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "chrome/updater/win/net/scoped_hinternet.h"
+#include "components/update_client/network.h"
+
+namespace updater {
+
+// Network fetcher factory for WinHTTP.
+class NetworkFetcherFactory : public update_client::NetworkFetcherFactory {
+ public:
+  NetworkFetcherFactory();
+
+  std::unique_ptr<update_client::NetworkFetcher> Create() const override;
+
+ protected:
+  ~NetworkFetcherFactory() override;
+
+ private:
+  static scoped_hinternet CreateSessionHandle();
+
+  THREAD_CHECKER(thread_checker_);
+  scoped_hinternet session_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcherFactory);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_NET_NETWORK_H_
diff --git a/src/cobalt/updater/win/net/network_fetcher.cc b/src/cobalt/updater/win/net/network_fetcher.cc
new file mode 100644
index 0000000..1ca27e1
--- /dev/null
+++ b/src/cobalt/updater/win/net/network_fetcher.cc
@@ -0,0 +1,98 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/net/network_fetcher.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/win/windows_version.h"
+#include "chrome/updater/win/net/network.h"
+#include "chrome/updater/win/net/network_winhttp.h"
+
+namespace updater {
+
+NetworkFetcher::NetworkFetcher(const HINTERNET& session_handle)
+    : network_fetcher_(
+          base::MakeRefCounted<NetworkFetcherWinHTTP>(session_handle)),
+      main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+
+NetworkFetcher::~NetworkFetcher() {
+  network_fetcher_->Close();
+}
+
+void NetworkFetcher::PostRequest(
+    const GURL& url,
+    const std::string& post_data,
+    const base::flat_map<std::string, std::string>& post_additional_headers,
+    ResponseStartedCallback response_started_callback,
+    ProgressCallback progress_callback,
+    PostRequestCompleteCallback post_request_complete_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  post_request_complete_callback_ = std::move(post_request_complete_callback);
+  network_fetcher_->PostRequest(
+      url, post_data, post_additional_headers,
+      std::move(response_started_callback), std::move(progress_callback),
+      base::BindOnce(&NetworkFetcher::PostRequestComplete,
+                     base::Unretained(this)));
+}
+
+void NetworkFetcher::DownloadToFile(
+    const GURL& url,
+    const base::FilePath& file_path,
+    ResponseStartedCallback response_started_callback,
+    ProgressCallback progress_callback,
+    DownloadToFileCompleteCallback download_to_file_complete_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  download_to_file_complete_callback_ =
+      std::move(download_to_file_complete_callback);
+  network_fetcher_->DownloadToFile(
+      url, file_path, std::move(response_started_callback),
+      std::move(progress_callback),
+      base::BindOnce(&NetworkFetcher::DownloadToFileComplete,
+                     base::Unretained(this)));
+}
+
+void NetworkFetcher::PostRequestComplete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  std::move(post_request_complete_callback_)
+      .Run(std::make_unique<std::string>(network_fetcher_->GetResponseBody()),
+           network_fetcher_->GetNetError(), network_fetcher_->GetHeaderETag(),
+           network_fetcher_->GetXHeaderRetryAfterSec());
+}
+
+void NetworkFetcher::DownloadToFileComplete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  std::move(download_to_file_complete_callback_)
+      .Run(network_fetcher_->GetFilePath(), network_fetcher_->GetNetError(),
+           network_fetcher_->GetContentSize());
+}
+
+NetworkFetcherFactory::NetworkFetcherFactory()
+    : session_handle_(CreateSessionHandle()) {}
+NetworkFetcherFactory::~NetworkFetcherFactory() = default;
+
+scoped_hinternet NetworkFetcherFactory::CreateSessionHandle() {
+  const auto* os_info = base::win::OSInfo::GetInstance();
+  const uint32_t access_type = os_info->version() >= base::win::Version::WIN8_1
+                                   ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
+                                   : WINHTTP_ACCESS_TYPE_NO_PROXY;
+  return scoped_hinternet(
+      ::WinHttpOpen(L"Chrome Updater", access_type, WINHTTP_NO_PROXY_NAME,
+                    WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC));
+}
+
+std::unique_ptr<update_client::NetworkFetcher> NetworkFetcherFactory::Create()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return session_handle_.get()
+             ? std::make_unique<NetworkFetcher>(session_handle_.get())
+             : nullptr;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/net/network_fetcher.h b/src/cobalt/updater/win/net/network_fetcher.h
new file mode 100644
index 0000000..ca83807
--- /dev/null
+++ b/src/cobalt/updater/win/net/network_fetcher.h
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_NET_NETWORK_FETCHER_H_
+#define CHROME_UPDATER_WIN_NET_NETWORK_FETCHER_H_
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/network.h"
+#include "url/gurl.h"
+
+namespace base {
+class FilePath;
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace updater {
+
+class NetworkFetcherWinHTTP;
+
+class NetworkFetcher : public update_client::NetworkFetcher {
+ public:
+  using ResponseStartedCallback =
+      update_client::NetworkFetcher::ResponseStartedCallback;
+  using ProgressCallback = update_client::NetworkFetcher::ProgressCallback;
+  using PostRequestCompleteCallback =
+      update_client::NetworkFetcher::PostRequestCompleteCallback;
+  using DownloadToFileCompleteCallback =
+      update_client::NetworkFetcher::DownloadToFileCompleteCallback;
+
+  explicit NetworkFetcher(const HINTERNET& session_handle_);
+  ~NetworkFetcher() override;
+
+  // NetworkFetcher overrides.
+  void PostRequest(
+      const GURL& url,
+      const std::string& post_data,
+      const base::flat_map<std::string, std::string>& post_additional_headers,
+      ResponseStartedCallback response_started_callback,
+      ProgressCallback progress_callback,
+      PostRequestCompleteCallback post_request_complete_callback) override;
+  void DownloadToFile(const GURL& url,
+                      const base::FilePath& file_path,
+                      ResponseStartedCallback response_started_callback,
+                      ProgressCallback progress_callback,
+                      DownloadToFileCompleteCallback
+                          download_to_file_complete_callback) override;
+
+ private:
+  THREAD_CHECKER(thread_checker_);
+
+  void PostRequestComplete();
+  void DownloadToFileComplete();
+
+  scoped_refptr<NetworkFetcherWinHTTP> network_fetcher_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+  DownloadToFileCompleteCallback download_to_file_complete_callback_;
+  PostRequestCompleteCallback post_request_complete_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcher);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_NET_NETWORK_FETCHER_H_
diff --git a/src/cobalt/updater/win/net/network_unittest.cc b/src/cobalt/updater/win/net/network_unittest.cc
new file mode 100644
index 0000000..b6854fc
--- /dev/null
+++ b/src/cobalt/updater/win/net/network_unittest.cc
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/net/network.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+
+TEST(UpdaterTestNetwork, NetworkFetcherWinHTTPFactory) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::UI);
+  auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create();
+  EXPECT_NE(nullptr, fetcher.get());
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/net/network_winhttp.cc b/src/cobalt/updater/win/net/network_winhttp.cc
new file mode 100644
index 0000000..c029453
--- /dev/null
+++ b/src/cobalt/updater/win/net/network_winhttp.cc
@@ -0,0 +1,553 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/net/network_winhttp.h"
+
+#include <limits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/updater/win/net/net_util.h"
+#include "chrome/updater/win/net/network.h"
+#include "chrome/updater/win/net/scoped_hinternet.h"
+#include "chrome/updater/win/util.h"
+#include "url/url_constants.h"
+
+namespace updater {
+
+namespace {
+
+void CrackUrl(const GURL& url,
+              bool* is_https,
+              std::string* host,
+              int* port,
+              std::string* path_for_request) {
+  if (is_https)
+    *is_https = url.SchemeIs(url::kHttpsScheme);
+  if (host)
+    *host = url.host();
+  if (port)
+    *port = url.EffectiveIntPort();
+  if (path_for_request)
+    *path_for_request = url.PathForRequest();
+}
+
+}  // namespace
+
+NetworkFetcherWinHTTP::NetworkFetcherWinHTTP(const HINTERNET& session_handle)
+    : main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      session_handle_(session_handle) {}
+
+NetworkFetcherWinHTTP::~NetworkFetcherWinHTTP() {}
+
+void NetworkFetcherWinHTTP::Close() {
+  request_handle_.reset();
+}
+
+std::string NetworkFetcherWinHTTP::GetResponseBody() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return post_response_body_;
+}
+
+HRESULT NetworkFetcherWinHTTP::GetNetError() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return net_error_;
+}
+
+std::string NetworkFetcherWinHTTP::GetHeaderETag() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return etag_;
+}
+
+int64_t NetworkFetcherWinHTTP::GetXHeaderRetryAfterSec() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return xheader_retry_after_sec_;
+}
+
+base::FilePath NetworkFetcherWinHTTP::GetFilePath() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return file_path_;
+}
+
+int64_t NetworkFetcherWinHTTP::GetContentSize() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return content_size_;
+}
+
+void NetworkFetcherWinHTTP::PostRequest(
+    const GURL& url,
+    const std::string& post_data,
+    const base::flat_map<std::string, std::string>& post_additional_headers,
+    FetchStartedCallback fetch_started_callback,
+    FetchProgressCallback fetch_progress_callback,
+    FetchCompleteCallback fetch_complete_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  url_ = url;
+  fetch_started_callback_ = std::move(fetch_started_callback);
+  fetch_progress_callback_ = std::move(fetch_progress_callback);
+  fetch_complete_callback_ = std::move(fetch_complete_callback);
+
+  DCHECK(url.SchemeIsHTTPOrHTTPS());
+  CrackUrl(url, &is_https_, &host_, &port_, &path_for_request_);
+
+  verb_ = L"POST";
+  content_type_ = L"Content-Type: application/json\r\n";
+  write_data_callback_ = base::BindRepeating(
+      &NetworkFetcherWinHTTP::WriteDataToMemory, base::Unretained(this));
+
+  net_error_ = BeginFetch(post_data, post_additional_headers);
+  if (FAILED(net_error_))
+    std::move(fetch_complete_callback_).Run();
+}
+
+void NetworkFetcherWinHTTP::DownloadToFile(
+    const GURL& url,
+    const base::FilePath& file_path,
+    FetchStartedCallback fetch_started_callback,
+    FetchProgressCallback fetch_progress_callback,
+    FetchCompleteCallback fetch_complete_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  url_ = url;
+  file_path_ = file_path;
+  fetch_started_callback_ = std::move(fetch_started_callback);
+  fetch_progress_callback_ = std::move(fetch_progress_callback);
+  fetch_complete_callback_ = std::move(fetch_complete_callback);
+
+  DCHECK(url.SchemeIsHTTPOrHTTPS());
+  CrackUrl(url, &is_https_, &host_, &port_, &path_for_request_);
+
+  verb_ = L"GET";
+  write_data_callback_ = base::BindRepeating(
+      &NetworkFetcherWinHTTP::WriteDataToFile, base::Unretained(this));
+
+  net_error_ = BeginFetch({}, {});
+  if (FAILED(net_error_))
+    std::move(fetch_complete_callback_).Run();
+}
+
+HRESULT NetworkFetcherWinHTTP::BeginFetch(
+    const std::string& data,
+    const base::flat_map<std::string, std::string>& additional_headers) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  connect_handle_ = Connect();
+  if (!connect_handle_.get())
+    return HRESULTFromLastError();
+
+  request_handle_ = OpenRequest();
+  if (!request_handle_.get())
+    return HRESULTFromLastError();
+
+  const auto winhttp_callback = ::WinHttpSetStatusCallback(
+      request_handle_.get(), &NetworkFetcherWinHTTP::WinHttpStatusCallback,
+      WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
+  if (winhttp_callback == WINHTTP_INVALID_STATUS_CALLBACK)
+    return HRESULTFromLastError();
+
+  auto hr =
+      SetOption(request_handle_.get(), WINHTTP_OPTION_CONTEXT_VALUE, context());
+  if (FAILED(hr))
+    return hr;
+
+  self_ = this;
+
+  // Disables both saving and sending cookies.
+  hr = SetOption(request_handle_.get(), WINHTTP_OPTION_DISABLE_FEATURE,
+                 WINHTTP_DISABLE_COOKIES);
+  if (FAILED(hr))
+    return hr;
+
+  if (!content_type_.empty()) {
+    ::WinHttpAddRequestHeaders(
+        request_handle_.get(), content_type_.data(), content_type_.size(),
+        WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE);
+  }
+  for (const auto& header : additional_headers) {
+    const auto raw_header = base::SysUTF8ToWide(
+        base::StrCat({header.first, ": ", header.second, "\r\n"}));
+    ::WinHttpAddRequestHeaders(
+        request_handle_.get(), raw_header.c_str(), raw_header.size(),
+        WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE);
+  }
+
+  hr = SendRequest(data);
+  if (FAILED(hr))
+    return hr;
+
+  return S_OK;
+}
+
+scoped_hinternet NetworkFetcherWinHTTP::Connect() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return scoped_hinternet(::WinHttpConnect(
+      session_handle_, base::SysUTF8ToWide(host_).c_str(), port_, 0));
+}
+
+scoped_hinternet NetworkFetcherWinHTTP::OpenRequest() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  uint32_t flags = WINHTTP_FLAG_REFRESH;
+  if (is_https_)
+    flags |= WINHTTP_FLAG_SECURE;
+  return scoped_hinternet(::WinHttpOpenRequest(
+      connect_handle_.get(), verb_.data(),
+      base::SysUTF8ToWide(path_for_request_).c_str(), nullptr,
+      WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags));
+}
+
+HRESULT NetworkFetcherWinHTTP::SendRequest(const std::string& data) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  const uint32_t bytes_to_send = base::saturated_cast<uint32_t>(data.size());
+  void* request_body =
+      bytes_to_send ? const_cast<char*>(data.c_str()) : WINHTTP_NO_REQUEST_DATA;
+  if (!::WinHttpSendRequest(request_handle_.get(),
+                            WINHTTP_NO_ADDITIONAL_HEADERS, 0, request_body,
+                            bytes_to_send, bytes_to_send, context())) {
+    return HRESULTFromLastError();
+  }
+
+  return S_OK;
+}
+
+void NetworkFetcherWinHTTP::SendRequestComplete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::string16 all;
+  QueryHeadersString(
+      request_handle_.get(),
+      WINHTTP_QUERY_RAW_HEADERS_CRLF | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
+      WINHTTP_HEADER_NAME_BY_INDEX, &all);
+  VLOG(2) << "request headers: " << all;
+
+  net_error_ = ReceiveResponse();
+  if (FAILED(net_error_))
+    std::move(fetch_complete_callback_).Run();
+}
+
+HRESULT NetworkFetcherWinHTTP::ReceiveResponse() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!::WinHttpReceiveResponse(request_handle_.get(), nullptr))
+    return HRESULTFromLastError();
+  return S_OK;
+}
+
+void NetworkFetcherWinHTTP::ReceiveResponseComplete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::string16 all;
+  QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                     WINHTTP_HEADER_NAME_BY_INDEX, &all);
+  VLOG(2) << "response headers: " << all;
+
+  int response_code = 0;
+  net_error_ = QueryHeadersInt(request_handle_.get(), WINHTTP_QUERY_STATUS_CODE,
+                               WINHTTP_HEADER_NAME_BY_INDEX, &response_code);
+  if (FAILED(net_error_)) {
+    std::move(fetch_complete_callback_).Run();
+    return;
+  }
+
+  int content_length = 0;
+  net_error_ =
+      QueryHeadersInt(request_handle_.get(), WINHTTP_QUERY_CONTENT_LENGTH,
+                      WINHTTP_HEADER_NAME_BY_INDEX, &content_length);
+  if (FAILED(net_error_)) {
+    std::move(fetch_complete_callback_).Run();
+    return;
+  }
+
+  base::string16 etag;
+  if (SUCCEEDED(QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_ETAG,
+                                   WINHTTP_HEADER_NAME_BY_INDEX, &etag))) {
+    etag_ = base::SysWideToUTF8(etag);
+  }
+
+  int xheader_retry_after_sec = 0;
+  if (SUCCEEDED(QueryHeadersInt(
+          request_handle_.get(), WINHTTP_QUERY_CUSTOM,
+          base::SysUTF8ToWide(
+              update_client::NetworkFetcher::kHeaderXRetryAfter),
+          &xheader_retry_after_sec))) {
+    xheader_retry_after_sec_ = xheader_retry_after_sec;
+  }
+
+  std::move(fetch_started_callback_)
+      .Run(final_url_.is_valid() ? final_url_ : url_, response_code,
+           content_length);
+
+  net_error_ = QueryDataAvailable();
+  if (FAILED(net_error_))
+    std::move(fetch_complete_callback_).Run();
+}
+
+HRESULT NetworkFetcherWinHTTP::QueryDataAvailable() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!::WinHttpQueryDataAvailable(request_handle_.get(), nullptr))
+    return HRESULTFromLastError();
+  return S_OK;
+}
+
+void NetworkFetcherWinHTTP::QueryDataAvailableComplete(
+    size_t num_bytes_available) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  net_error_ = ReadData(num_bytes_available);
+  if (FAILED(net_error_))
+    std::move(fetch_complete_callback_).Run();
+}
+
+HRESULT NetworkFetcherWinHTTP::ReadData(size_t num_bytes_available) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  const int num_bytes_to_read = base::saturated_cast<int>(num_bytes_available);
+  read_buffer_.resize(num_bytes_to_read);
+
+  if (!::WinHttpReadData(request_handle_.get(), &read_buffer_.front(),
+                         read_buffer_.size(), nullptr)) {
+    return HRESULTFromLastError();
+  }
+  return S_OK;
+}
+
+void NetworkFetcherWinHTTP::ReadDataComplete(size_t num_bytes_read) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  fetch_progress_callback_.Run(base::saturated_cast<int64_t>(num_bytes_read));
+
+  read_buffer_.resize(num_bytes_read);
+  write_data_callback_.Run();
+}
+
+void NetworkFetcherWinHTTP::RequestError(const WINHTTP_ASYNC_RESULT* result) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  net_error_ = HRESULTFromUpdaterError(result->dwError);
+  std::move(fetch_complete_callback_).Run();
+}
+
+void NetworkFetcherWinHTTP::WriteDataToFile() {
+  constexpr base::TaskTraits kTaskTraits = {
+      base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+      base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
+
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE, kTaskTraits,
+      base::BindOnce(&NetworkFetcherWinHTTP::WriteDataToFileBlocking,
+                     base::Unretained(this)),
+      base::BindOnce(&NetworkFetcherWinHTTP::WriteDataToFileComplete,
+                     base::Unretained(this)));
+}
+
+bool NetworkFetcherWinHTTP::WriteDataToFileBlocking() {
+  if (read_buffer_.empty()) {
+    file_.Close();
+    net_error_ = S_OK;
+    return true;
+  }
+
+  if (!file_.IsValid()) {
+    file_.Initialize(file_path_, base::File::Flags::FLAG_CREATE_ALWAYS |
+                                     base::File::Flags::FLAG_WRITE |
+                                     base::File::Flags::FLAG_SEQUENTIAL_SCAN);
+    if (!file_.IsValid()) {
+      net_error_ = HRESULTFromUpdaterError(file_.error_details());
+      return false;
+    }
+  }
+
+  DCHECK(file_.IsValid());
+  if (file_.WriteAtCurrentPos(&read_buffer_.front(), read_buffer_.size()) ==
+      -1) {
+    net_error_ = HRESULTFromUpdaterError(base::File::GetLastFileError());
+    file_.Close();
+    base::DeleteFile(file_path_, false);
+    return false;
+  }
+
+  content_size_ += read_buffer_.size();
+  return false;
+}
+
+void NetworkFetcherWinHTTP::WriteDataToFileComplete(bool is_eof) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (is_eof || FAILED(net_error_)) {
+    std::move(fetch_complete_callback_).Run();
+    return;
+  }
+
+  net_error_ = QueryDataAvailable();
+  if (FAILED(net_error_))
+    std::move(fetch_complete_callback_).Run();
+}
+
+void NetworkFetcherWinHTTP::WriteDataToMemory() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (read_buffer_.empty()) {
+    VLOG(2) << post_response_body_;
+    net_error_ = S_OK;
+    std::move(fetch_complete_callback_).Run();
+    return;
+  }
+
+  post_response_body_.append(read_buffer_.begin(), read_buffer_.end());
+  content_size_ += read_buffer_.size();
+
+  net_error_ = QueryDataAvailable();
+  if (FAILED(net_error_))
+    std::move(fetch_complete_callback_).Run();
+}
+
+void __stdcall NetworkFetcherWinHTTP::WinHttpStatusCallback(HINTERNET handle,
+                                                            DWORD_PTR context,
+                                                            DWORD status,
+                                                            void* info,
+                                                            DWORD info_len) {
+  DCHECK(handle);
+  DCHECK(context);
+  NetworkFetcherWinHTTP* network_fetcher =
+      reinterpret_cast<NetworkFetcherWinHTTP*>(context);
+  network_fetcher->main_thread_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&NetworkFetcherWinHTTP::StatusCallback,
+                                base::Unretained(network_fetcher), handle,
+                                status, info, info_len));
+}
+
+void NetworkFetcherWinHTTP::StatusCallback(HINTERNET handle,
+                                           uint32_t status,
+                                           void* info,
+                                           uint32_t info_len) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::StringPiece status_string;
+  base::string16 info_string;
+  switch (status) {
+    case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
+      status_string = "handle created";
+      break;
+    case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
+      status_string = "handle closing";
+      break;
+    case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
+      status_string = "resolving";
+      info_string.assign(static_cast<base::char16*>(info), info_len);  // host.
+      break;
+    case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
+      status_string = "resolved";
+      break;
+    case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
+      status_string = "connecting";
+      info_string.assign(static_cast<base::char16*>(info), info_len);  // IP.
+      break;
+    case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
+      status_string = "connected";
+      break;
+    case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
+      status_string = "sending";
+      break;
+    case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
+      status_string = "sent";
+      break;
+    case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
+      status_string = "receiving response";
+      break;
+    case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
+      status_string = "response received";
+      break;
+    case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
+      status_string = "connection closing";
+      break;
+    case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
+      status_string = "connection closed";
+      break;
+    case WINHTTP_CALLBACK_STATUS_REDIRECT:
+      status_string = "redirect";
+      info_string.assign(static_cast<base::char16*>(info), info_len);  // URL.
+      break;
+    case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
+      status_string = "data available";
+      DCHECK_EQ(info_len, sizeof(uint32_t));
+      info_string = base::StringPrintf(L"%lu", *static_cast<uint32_t*>(info));
+      break;
+    case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
+      status_string = "headers available";
+      break;
+    case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
+      status_string = "read complete";
+      info_string = base::StringPrintf(L"%lu", info_len);
+      break;
+    case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
+      status_string = "send request complete";
+      break;
+    case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
+      status_string = "write complete";
+      break;
+    case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
+      status_string = "request error";
+      break;
+    case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
+      status_string = "https failure";
+      DCHECK(info);
+      DCHECK_EQ(info_len, sizeof(uint32_t));
+      info_string = base::StringPrintf(L"%#x", *static_cast<uint32_t*>(info));
+      break;
+    default:
+      status_string = "unknown callback";
+      break;
+  }
+
+  std::string msg;
+  if (!status_string.empty())
+    base::StringAppendF(&msg, "status=%s", status_string.data());
+  else
+    base::StringAppendF(&msg, "status=%#x", status);
+  if (!info_string.empty())
+    base::StringAppendF(&msg, ", info=%s",
+                        base::SysWideToUTF8(info_string).c_str());
+
+  VLOG(2) << "WinHttp status callback:"
+          << " handle=" << handle << ", " << msg;
+
+  switch (status) {
+    case WINHTTP_CALLBACK_STATUS_REDIRECT:
+      final_url_ = GURL(static_cast<base::char16*>(info));
+      break;
+    case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
+      self_ = nullptr;
+      break;
+    case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
+      SendRequestComplete();
+      break;
+    case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
+      ReceiveResponseComplete();
+      break;
+    case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
+      DCHECK_EQ(info_len, sizeof(uint32_t));
+      QueryDataAvailableComplete(*static_cast<uint32_t*>(info));
+      break;
+    case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
+      DCHECK_EQ(info, &read_buffer_.front());
+      ReadDataComplete(info_len);
+      break;
+    case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
+      RequestError(static_cast<const WINHTTP_ASYNC_RESULT*>(info));
+      break;
+    default:
+      break;
+  }
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/net/network_winhttp.h b/src/cobalt/updater/win/net/network_winhttp.h
new file mode 100644
index 0000000..c34b602
--- /dev/null
+++ b/src/cobalt/updater/win/net/network_winhttp.h
@@ -0,0 +1,149 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_NET_NETWORK_WINHTTP_H_
+#define CHROME_UPDATER_WIN_NET_NETWORK_WINHTTP_H_
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_piece_forward.h"
+#include "base/threading/thread_checker.h"
+#include "chrome/updater/win/net/scoped_hinternet.h"
+#include "components/update_client/network.h"
+#include "url/gurl.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace updater {
+
+// Implements a network fetcher in terms of WinHTTP. The class is ref-counted
+// as it is accessed from both the main thread and the worker threads in
+// WinHTTP.
+class NetworkFetcherWinHTTP
+    : public base::RefCountedThreadSafe<NetworkFetcherWinHTTP> {
+ public:
+  using FetchCompleteCallback = base::OnceCallback<void()>;
+  using FetchStartedCallback =
+      update_client::NetworkFetcher::ResponseStartedCallback;
+  using FetchProgressCallback = update_client::NetworkFetcher::ProgressCallback;
+
+  explicit NetworkFetcherWinHTTP(const HINTERNET& session_handle_);
+
+  void Close();
+
+  void PostRequest(
+      const GURL& url,
+      const std::string& post_data,
+      const base::flat_map<std::string, std::string>& post_additional_headers,
+      FetchStartedCallback fetch_started_callback,
+      FetchProgressCallback fetch_progress_callback,
+      FetchCompleteCallback fetch_complete_callback);
+  void DownloadToFile(const GURL& url,
+                      const base::FilePath& file_path,
+                      FetchStartedCallback fetch_started_callback,
+                      FetchProgressCallback fetch_progress_callback,
+                      FetchCompleteCallback fetch_complete_callback);
+
+  std::string GetResponseBody() const;
+  HRESULT GetNetError() const;
+  std::string GetHeaderETag() const;
+  int64_t GetXHeaderRetryAfterSec() const;
+  base::FilePath GetFilePath() const;
+  int64_t GetContentSize() const;
+
+ private:
+  friend class base::RefCountedThreadSafe<NetworkFetcherWinHTTP>;
+  using WriteDataCallback = base::RepeatingCallback<void()>;
+
+  ~NetworkFetcherWinHTTP();
+
+  static void __stdcall WinHttpStatusCallback(HINTERNET handle,
+                                              DWORD_PTR context,
+                                              DWORD status,
+                                              void* info,
+                                              DWORD info_len);
+
+  DWORD_PTR context() const { return reinterpret_cast<DWORD_PTR>(this); }
+
+  void StatusCallback(HINTERNET handle,
+                      uint32_t status,
+                      void* info,
+                      uint32_t info_len);
+
+  HRESULT BeginFetch(
+      const std::string& data,
+      const base::flat_map<std::string, std::string>& additional_headers);
+  scoped_hinternet Connect();
+  scoped_hinternet OpenRequest();
+  HRESULT SendRequest(const std::string& data);
+  void SendRequestComplete();
+  HRESULT ReceiveResponse();
+  void ReceiveResponseComplete();
+  HRESULT QueryDataAvailable();
+  void QueryDataAvailableComplete(size_t num_bytes_available);
+  HRESULT ReadData(size_t num_bytes_available);
+  void ReadDataComplete(size_t num_bytes_read);
+  void RequestError(const WINHTTP_ASYNC_RESULT* result);
+
+  void WriteDataToMemory();
+  void WriteDataToFile();
+  bool WriteDataToFileBlocking();
+  void WriteDataToFileComplete(bool is_eof);
+
+  THREAD_CHECKER(thread_checker_);
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+  const HINTERNET& session_handle_;  // Owned by NetworkFetcherWinHTTPFactory.
+  scoped_hinternet connect_handle_;
+  scoped_hinternet request_handle_;
+
+  // Keeps an outstanding reference count on itself as long as there is a
+  // valid request handle and the context for the handle is set to this
+  // instance.
+  scoped_refptr<NetworkFetcherWinHTTP> self_;
+
+  GURL url_;
+  bool is_https_ = false;
+  std::string host_;
+  int port_ = 0;
+  std::string path_for_request_;
+
+  GURL final_url_;
+  base::StringPiece16 verb_;
+  base::StringPiece16 content_type_;
+  WriteDataCallback write_data_callback_;
+  HRESULT net_error_ = S_OK;
+  std::string etag_;
+  int64_t xheader_retry_after_sec_ = -1;
+  std::vector<char> read_buffer_;
+  std::string post_response_body_;
+  base::FilePath file_path_;
+  base::File file_;
+  int64_t content_size_ = 0;
+
+  FetchStartedCallback fetch_started_callback_;
+  FetchProgressCallback fetch_progress_callback_;
+  FetchCompleteCallback fetch_complete_callback_;
+
+  scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcherWinHTTP);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_NET_NETWORK_WINHTTP_H_
diff --git a/src/cobalt/updater/win/net/scoped_hinternet.h b/src/cobalt/updater/win/net/scoped_hinternet.h
new file mode 100644
index 0000000..235547e
--- /dev/null
+++ b/src/cobalt/updater/win/net/scoped_hinternet.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_NET_SCOPED_HINTERNET_H_
+#define CHROME_UPDATER_WIN_NET_SCOPED_HINTERNET_H_
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include "base/scoped_generic.h"
+
+namespace updater {
+
+namespace internal {
+
+struct ScopedHInternetTraits {
+  static HINTERNET InvalidValue() { return nullptr; }
+  static void Free(HINTERNET handle) {
+    if (handle != InvalidValue())
+      WinHttpCloseHandle(handle);
+  }
+};
+
+}  // namespace internal
+
+// Manages the lifetime of HINTERNET handles allocated by WinHTTP.
+using scoped_hinternet =
+    base::ScopedGeneric<HINTERNET, updater::internal::ScopedHInternetTraits>;
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_NET_SCOPED_HINTERNET_H_
diff --git a/src/cobalt/updater/win/setup/setup.cc b/src/cobalt/updater/win/setup/setup.cc
new file mode 100644
index 0000000..2848c5c
--- /dev/null
+++ b/src/cobalt/updater/win/setup/setup.cc
@@ -0,0 +1,118 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/setup/setup.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#include "base/win/scoped_com_initializer.h"
+#include "chrome/installer/util/copy_tree_work_item.h"
+#include "chrome/installer/util/self_cleaning_temp_dir.h"
+#include "chrome/installer/util/work_item_list.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/util.h"
+#include "chrome/updater/win/setup/setup_util.h"
+#include "chrome/updater/win/task_scheduler.h"
+
+namespace updater {
+
+namespace {
+
+const base::char16* kUpdaterFiles[] = {
+    L"updater.exe",
+    L"uninstall.cmd",
+#if defined(COMPONENT_BUILD)
+    // TODO(sorin): get the list of component dependencies from a build-time
+    // file instead of hardcoding the names of the components here.
+    L"base.dll",
+    L"boringssl.dll",
+    L"crcrypto.dll",
+    L"icuuc.dll",
+    L"libc++.dll",
+    L"prefs.dll",
+    L"protobuf_lite.dll",
+    L"url_lib.dll",
+    L"zlib.dll",
+#endif
+};
+
+}  // namespace
+
+int Setup() {
+  VLOG(1) << __func__;
+
+  auto scoped_com_initializer =
+      std::make_unique<base::win::ScopedCOMInitializer>(
+          base::win::ScopedCOMInitializer::kMTA);
+
+  if (!TaskScheduler::Initialize()) {
+    LOG(ERROR) << "Failed to initialize the scheduler.";
+    return -1;
+  }
+  base::ScopedClosureRunner task_scheduler_terminate_caller(
+      base::BindOnce([]() { TaskScheduler::Terminate(); }));
+
+  base::FilePath temp_dir;
+  if (!base::GetTempDir(&temp_dir)) {
+    LOG(ERROR) << "GetTempDir failed.";
+    return -1;
+  }
+  base::FilePath product_dir;
+  if (!GetProductDirectory(&product_dir)) {
+    LOG(ERROR) << "GetProductDirectory failed.";
+    return -1;
+  }
+  base::FilePath exe_path;
+  if (!base::PathService::Get(base::FILE_EXE, &exe_path)) {
+    LOG(ERROR) << "PathService failed.";
+    return -1;
+  }
+
+  installer::SelfCleaningTempDir backup_dir;
+  if (!backup_dir.Initialize(temp_dir, L"updater-backup")) {
+    LOG(ERROR) << "Failed to initialize the backup dir.";
+    return -1;
+  }
+
+  const base::FilePath source_dir = exe_path.DirName();
+
+  std::unique_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
+  for (const auto* file : kUpdaterFiles) {
+    const base::FilePath target_path = product_dir.Append(file);
+    const base::FilePath source_path = source_dir.Append(file);
+    install_list->AddWorkItem(
+        WorkItem::CreateCopyTreeWorkItem(source_path, target_path, temp_dir,
+                                         WorkItem::ALWAYS, base::FilePath()));
+  }
+
+  base::CommandLine run_updater_ua_command(product_dir.Append(L"updater.exe"));
+  run_updater_ua_command.AppendSwitch(kUpdateAppsSwitch);
+#if !defined(NDEBUG)
+  run_updater_ua_command.AppendSwitch(kEnableLoggingSwitch);
+  run_updater_ua_command.AppendSwitchASCII(kLoggingLevelSwitch, "1");
+  run_updater_ua_command.AppendSwitchASCII(kLoggingModuleSwitch,
+                                           "*/chrome/updater/*");
+#endif
+  if (!install_list->Do() || !RegisterUpdateAppsTask(run_updater_ua_command)) {
+    LOG(ERROR) << "Install failed, rolling back...";
+    install_list->Rollback();
+    UnregisterUpdateAppsTask();
+    LOG(ERROR) << "Rollback complete.";
+    return -1;
+  }
+
+  VLOG(1) << "Setup succeeded.";
+  return 0;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/setup/setup.h b/src/cobalt/updater/win/setup/setup.h
new file mode 100644
index 0000000..2a306ba
--- /dev/null
+++ b/src/cobalt/updater/win/setup/setup.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_SETUP_SETUP_H_
+#define CHROME_UPDATER_WIN_SETUP_SETUP_H_
+
+namespace updater {
+
+int Setup();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_SETUP_SETUP_H_
diff --git a/src/cobalt/updater/win/setup/setup_util.cc b/src/cobalt/updater/win/setup/setup_util.cc
new file mode 100644
index 0000000..fff30e8
--- /dev/null
+++ b/src/cobalt/updater/win/setup/setup_util.cc
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/setup/setup_util.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "chrome/updater/win/task_scheduler.h"
+
+namespace updater {
+
+namespace {
+
+constexpr base::char16 kTaskName[] = L"GoogleUpdaterUA";
+constexpr base::char16 kTaskDescription[] = L"Update all applications.";
+
+}  // namespace
+
+bool RegisterUpdateAppsTask(const base::CommandLine& run_command) {
+  auto task_scheduler = TaskScheduler::CreateInstance();
+  if (!task_scheduler->RegisterTask(
+          kTaskName, kTaskDescription, run_command,
+          TaskScheduler::TriggerType::TRIGGER_TYPE_HOURLY, true)) {
+    LOG(ERROR) << "RegisterUpdateAppsTask failed.";
+    return false;
+  }
+  VLOG(1) << "RegisterUpdateAppsTask succeeded.";
+  return true;
+}
+
+void UnregisterUpdateAppsTask() {
+  auto task_scheduler = TaskScheduler::CreateInstance();
+  task_scheduler->DeleteTask(kTaskName);
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/setup/setup_util.h b/src/cobalt/updater/win/setup/setup_util.h
new file mode 100644
index 0000000..4a57f32
--- /dev/null
+++ b/src/cobalt/updater/win/setup/setup_util.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_SETUP_SETUP_UTIL_H_
+#define CHROME_UPDATER_WIN_SETUP_SETUP_UTIL_H_
+
+namespace base {
+class CommandLine;
+}  // namespace base
+
+#include "base/win/windows_types.h"
+
+namespace updater {
+
+bool RegisterUpdateAppsTask(const base::CommandLine& run_command);
+void UnregisterUpdateAppsTask();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_SETUP_SETUP_UTIL_H_
diff --git a/src/cobalt/updater/win/setup/uninstall.cc b/src/cobalt/updater/win/setup/uninstall.cc
new file mode 100644
index 0000000..8767e26
--- /dev/null
+++ b/src/cobalt/updater/win/setup/uninstall.cc
@@ -0,0 +1,79 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/setup/uninstall.h"
+
+#include <windows.h>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_com_initializer.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/util.h"
+#include "chrome/updater/win/setup/setup_util.h"
+#include "chrome/updater/win/task_scheduler.h"
+
+namespace updater {
+
+// Reverses the changes made by setup. This is a best effort uninstall:
+// 1. deletes the scheduled task.
+// 2. runs the uninstall script in the install directory of the updater.
+// The execution of this function and the script race each other but the script
+// loops and waits in between iterations trying to delete the install directory.
+int Uninstall() {
+  VLOG(1) << __func__;
+
+  auto scoped_com_initializer =
+      std::make_unique<base::win::ScopedCOMInitializer>(
+          base::win::ScopedCOMInitializer::kMTA);
+
+  if (!TaskScheduler::Initialize()) {
+    LOG(ERROR) << "Failed to initialize the scheduler.";
+    return -1;
+  }
+  base::ScopedClosureRunner task_scheduler_terminate_caller(
+      base::BindOnce([]() { TaskScheduler::Terminate(); }));
+
+  updater::UnregisterUpdateAppsTask();
+
+  base::FilePath product_dir;
+  if (!GetProductDirectory(&product_dir)) {
+    LOG(ERROR) << "GetProductDirectory failed.";
+    return -1;
+  }
+
+  base::char16 cmd_path[MAX_PATH] = {0};
+  auto size = ExpandEnvironmentStrings(L"%SystemRoot%\\System32\\cmd.exe",
+                                       cmd_path, base::size(cmd_path));
+  if (!size || size >= MAX_PATH)
+    return -1;
+
+  base::FilePath script_path = product_dir.AppendASCII(kUninstallScript);
+
+  base::string16 cmdline = cmd_path;
+  base::StringAppendF(&cmdline, L" /Q /C \"%ls\"",
+                      script_path.AsUTF16Unsafe().c_str());
+  base::LaunchOptions options;
+  options.start_hidden = true;
+
+  VLOG(1) << "Running " << cmdline;
+
+  auto process = base::LaunchProcess(cmdline, options);
+  if (!process.IsValid()) {
+    LOG(ERROR) << "Failed to create process " << cmdline;
+    return -1;
+  }
+
+  return 0;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/setup/uninstall.cmd b/src/cobalt/updater/win/setup/uninstall.cmd
new file mode 100644
index 0000000..bbaf5ce
--- /dev/null
+++ b/src/cobalt/updater/win/setup/uninstall.cmd
@@ -0,0 +1,13 @@
+rem Deletes the script's parent directory if \AppData\Local\ChromeUpdater\ is

+rem anywhere in the directory path. Sleeps 3 seconds and tries 3 times to

+rem delete the directory.

+@echo off

+set Directory=%~dp0

+@echo %Directory% | FindStr /R \\AppData\\Local\\Google\\GoogleUpdater\\ > nul

+IF %ERRORLEVEL% NEQ 0 exit 1

+@echo Deleting "%Directory%"...

+for /L %%G IN (1,1,3) do (

+  rmdir "%Directory%" /s /q > nul

+  if not exist "%Directory%" exit 0

+  ping -n 3 127.0.0.1 > nul

+)

diff --git a/src/cobalt/updater/win/setup/uninstall.h b/src/cobalt/updater/win/setup/uninstall.h
new file mode 100644
index 0000000..1080629
--- /dev/null
+++ b/src/cobalt/updater/win/setup/uninstall.h
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_SETUP_UNINSTALL_H_
+#define CHROME_UPDATER_WIN_SETUP_UNINSTALL_H_
+
+namespace updater {
+
+int Uninstall();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_SETUP_UNINSTALL_H_
diff --git a/src/cobalt/updater/win/task_scheduler.cc b/src/cobalt/updater/win/task_scheduler.cc
new file mode 100644
index 0000000..bdbc206
--- /dev/null
+++ b/src/cobalt/updater/win/task_scheduler.cc
@@ -0,0 +1,961 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/task_scheduler.h"
+
+#include <mstask.h>
+#include <oleauto.h>
+#include <security.h>
+#include <taskschd.h>
+#include <wrl/client.h>
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/native_library.h"
+#include "base/path_service.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
+#include "chrome/updater/win/util.h"
+
+namespace updater {
+
+namespace {
+
+// Names of the TaskSchedulerV2 libraries so we can pin them below.
+const wchar_t kV2Library[] = L"taskschd.dll";
+
+// Text for times used in the V2 API of the Task Scheduler.
+const wchar_t kOneHourText[] = L"PT1H";
+const wchar_t kFiveHoursText[] = L"PT5H";
+const wchar_t kZeroMinuteText[] = L"PT0M";
+const wchar_t kFifteenMinutesText[] = L"PT15M";
+const wchar_t kTwentyFourHoursText[] = L"PT24H";
+
+// Most of the users with pending logs succeeds within 7 days, so no need to
+// try for longer than that, especially for those who keep crashing.
+const int kNumDaysBeforeExpiry = 7;
+const size_t kNumDeleteTaskRetry = 3;
+const size_t kDeleteRetryDelayInMs = 100;
+
+// Return |timestamp| in the following string format YYYY-MM-DDTHH:MM:SS.
+base::string16 GetTimestampString(const base::Time& timestamp) {
+  base::Time::Exploded exploded_time;
+  // The Z timezone info at the end of the string means UTC.
+  timestamp.UTCExplode(&exploded_time);
+  return base::StringPrintf(L"%04d-%02d-%02dT%02d:%02d:%02dZ",
+                            exploded_time.year, exploded_time.month,
+                            exploded_time.day_of_month, exploded_time.hour,
+                            exploded_time.minute, exploded_time.second);
+}
+
+bool LocalSystemTimeToUTCFileTime(const SYSTEMTIME& system_time_local,
+                                  FILETIME* file_time_utc) {
+  DCHECK(file_time_utc);
+  SYSTEMTIME system_time_utc = {};
+  if (!::TzSpecificLocalTimeToSystemTime(nullptr, &system_time_local,
+                                         &system_time_utc) ||
+      !::SystemTimeToFileTime(&system_time_utc, file_time_utc)) {
+    PLOG(ERROR) << "Failed to convert local system time to UTC file time.";
+    return false;
+  }
+  return true;
+}
+
+bool UTCFileTimeToLocalSystemTime(const FILETIME& file_time_utc,
+                                  SYSTEMTIME* system_time_local) {
+  DCHECK(system_time_local);
+  SYSTEMTIME system_time_utc = {};
+  if (!::FileTimeToSystemTime(&file_time_utc, &system_time_utc) ||
+      !::SystemTimeToTzSpecificLocalTime(nullptr, &system_time_utc,
+                                         system_time_local)) {
+    PLOG(ERROR) << "Failed to convert file time to UTC local system.";
+    return false;
+  }
+  return true;
+}
+
+bool GetCurrentUser(base::win::ScopedBstr* user_name) {
+  DCHECK(user_name);
+  ULONG user_name_size = 256;
+  // Paranoia... ;-)
+  DCHECK_EQ(sizeof(OLECHAR), sizeof(WCHAR));
+  if (!::GetUserNameExW(
+          NameSamCompatible,
+          user_name->AllocateBytes(user_name_size * sizeof(OLECHAR)),
+          &user_name_size)) {
+    if (::GetLastError() != ERROR_MORE_DATA) {
+      PLOG(ERROR) << "GetUserNameEx failed.";
+      return false;
+    }
+    if (!::GetUserNameExW(
+            NameSamCompatible,
+            user_name->AllocateBytes(user_name_size * sizeof(OLECHAR)),
+            &user_name_size)) {
+      DCHECK_NE(static_cast<DWORD>(ERROR_MORE_DATA), ::GetLastError());
+      PLOG(ERROR) << "GetUserNameEx failed.";
+      return false;
+    }
+  }
+  return true;
+}
+
+void PinModule(const wchar_t* module_name) {
+  // Force the DLL to stay loaded until program termination. We have seen
+  // cases where it gets unloaded even though we still have references to
+  // the objects we just CoCreated.
+  base::NativeLibrary module_handle = nullptr;
+  if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, module_name,
+                            &module_handle)) {
+    PLOG(ERROR) << "Failed to pin '" << module_name << "'.";
+  }
+}
+
+// A task scheduler class uses the V2 API of the task scheduler.
+class TaskSchedulerV2 final : public TaskScheduler {
+ public:
+  static bool Initialize() {
+    DCHECK(!task_service_);
+    DCHECK(!root_task_folder_);
+
+    HRESULT hr =
+        ::CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER,
+                           IID_PPV_ARGS(&task_service_));
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "CreateInstance failed for CLSID_TaskScheduler. "
+                  << std::hex << hr;
+      return false;
+    }
+    hr = task_service_->Connect(base::win::ScopedVariant::kEmptyVariant,
+                                base::win::ScopedVariant::kEmptyVariant,
+                                base::win::ScopedVariant::kEmptyVariant,
+                                base::win::ScopedVariant::kEmptyVariant);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Failed to connect to task service. " << std::hex << hr;
+      return false;
+    }
+    hr = task_service_->GetFolder(base::win::ScopedBstr(L"\\"),
+                                  &root_task_folder_);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Can't get task service folder. " << std::hex << hr;
+      return false;
+    }
+    PinModule(kV2Library);
+    return true;
+  }
+
+  static void Terminate() {
+    root_task_folder_.Reset();
+    task_service_.Reset();
+  }
+
+  TaskSchedulerV2() {
+    DCHECK(task_service_);
+    DCHECK(root_task_folder_);
+  }
+
+  // TaskScheduler overrides.
+  bool IsTaskRegistered(const wchar_t* task_name) override {
+    DCHECK(task_name);
+    if (!root_task_folder_)
+      return false;
+
+    return GetTask(task_name, nullptr);
+  }
+
+  bool GetNextTaskRunTime(const wchar_t* task_name,
+                          base::Time* next_run_time) override {
+    DCHECK(task_name);
+    DCHECK(next_run_time);
+    if (!root_task_folder_)
+      return false;
+
+    Microsoft::WRL::ComPtr<IRegisteredTask> registered_task;
+    if (!GetTask(task_name, &registered_task))
+      return false;
+
+    // We unfortunately can't use get_NextRunTime because of a known bug which
+    // requires hotfix: http://support.microsoft.com/kb/2495489/en-us. So fetch
+    // one of the run times in the next day.
+    // Also, although it's not obvious from MSDN, IRegisteredTask::GetRunTimes
+    // expects local time.
+    SYSTEMTIME start_system_time = {};
+    GetLocalTime(&start_system_time);
+
+    base::Time tomorrow(base::Time::NowFromSystemTime() +
+                        base::TimeDelta::FromDays(1));
+    SYSTEMTIME end_system_time = {};
+    if (!UTCFileTimeToLocalSystemTime(tomorrow.ToFileTime(), &end_system_time))
+      return false;
+
+    DWORD num_run_times = 1;
+    SYSTEMTIME* raw_run_times = nullptr;
+    HRESULT hr = registered_task->GetRunTimes(
+        &start_system_time, &end_system_time, &num_run_times, &raw_run_times);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Failed to GetRunTimes, " << std::hex << hr;
+      return false;
+    }
+
+    if (num_run_times == 0)
+      return false;
+
+    base::win::ScopedCoMem<SYSTEMTIME> run_times;
+    run_times.Reset(raw_run_times);
+    // Again, although unclear from MSDN, IRegisteredTask::GetRunTimes returns
+    // local times.
+    FILETIME file_time = {};
+    if (!LocalSystemTimeToUTCFileTime(run_times[0], &file_time))
+      return false;
+    *next_run_time = base::Time::FromFileTime(file_time);
+    return true;
+  }
+
+  bool SetTaskEnabled(const wchar_t* task_name, bool enabled) override {
+    DCHECK(task_name);
+    if (!root_task_folder_)
+      return false;
+
+    Microsoft::WRL::ComPtr<IRegisteredTask> registered_task;
+    if (!GetTask(task_name, &registered_task)) {
+      LOG(ERROR) << "Failed to find the task " << task_name
+                 << " to enable/disable";
+      return false;
+    }
+
+    HRESULT hr;
+    hr = registered_task->put_Enabled(enabled ? VARIANT_TRUE : VARIANT_FALSE);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Failed to set enabled status of task named " << task_name
+                  << ". " << std::hex << hr;
+      return false;
+    }
+    return true;
+  }
+
+  bool IsTaskEnabled(const wchar_t* task_name) override {
+    DCHECK(task_name);
+    if (!root_task_folder_)
+      return false;
+
+    Microsoft::WRL::ComPtr<IRegisteredTask> registered_task;
+    if (!GetTask(task_name, &registered_task))
+      return false;
+
+    HRESULT hr;
+    VARIANT_BOOL is_enabled;
+    hr = registered_task->get_Enabled(&is_enabled);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get enabled status for task named " << task_name
+                 << ". " << std::hex << hr << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return false;
+    }
+
+    return is_enabled == VARIANT_TRUE;
+  }
+
+  bool GetTaskNameList(std::vector<base::string16>* task_names) override {
+    DCHECK(task_names);
+    if (!root_task_folder_)
+      return false;
+
+    for (TaskIterator it(root_task_folder_.Get()); !it.done(); it.Next())
+      task_names->push_back(it.name());
+    return true;
+  }
+
+  bool GetTaskInfo(const wchar_t* task_name, TaskInfo* info) override {
+    DCHECK(task_name);
+    DCHECK(info);
+    if (!root_task_folder_)
+      return false;
+
+    Microsoft::WRL::ComPtr<IRegisteredTask> registered_task;
+    if (!GetTask(task_name, &registered_task))
+      return false;
+
+    // Collect information into internal storage to ensure that we start with
+    // a clean slate and don't return partial results on error.
+    TaskInfo info_storage;
+    HRESULT hr =
+        GetTaskDescription(registered_task.Get(), &info_storage.description);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get description for task '" << task_name << "'. "
+                 << std::hex << hr << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return false;
+    }
+
+    if (!GetTaskExecActions(registered_task.Get(),
+                            &info_storage.exec_actions)) {
+      LOG(ERROR) << "Failed to get actions for task '" << task_name << "'";
+      return false;
+    }
+
+    hr = GetTaskLogonType(registered_task.Get(), &info_storage.logon_type);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get logon type for task '" << task_name << "'. "
+                 << std::hex << hr << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return false;
+    }
+    info_storage.name = task_name;
+    std::swap(*info, info_storage);
+    return true;
+  }
+
+  bool DeleteTask(const wchar_t* task_name) override {
+    DCHECK(task_name);
+    if (!root_task_folder_)
+      return false;
+
+    VLOG(1) << "Delete Task '" << task_name << "'.";
+
+    HRESULT hr =
+        root_task_folder_->DeleteTask(base::win::ScopedBstr(task_name), 0);
+    // This can happen, e.g., while running tests, when the file system stresses
+    // quite a lot. Give it a few more chances to succeed.
+    size_t num_retries_left = kNumDeleteTaskRetry;
+
+    if (FAILED(hr)) {
+      while ((hr == HRESULT_FROM_WIN32(ERROR_TRANSACTION_NOT_ACTIVE) ||
+              hr == HRESULT_FROM_WIN32(ERROR_TRANSACTION_ALREADY_ABORTED)) &&
+             --num_retries_left && IsTaskRegistered(task_name)) {
+        LOG(WARNING) << "Retrying delete task because transaction not active, "
+                     << std::hex << hr << ".";
+        hr = root_task_folder_->DeleteTask(base::win::ScopedBstr(task_name), 0);
+        ::Sleep(kDeleteRetryDelayInMs);
+      }
+      if (!IsTaskRegistered(task_name))
+        hr = S_OK;
+    }
+
+    if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
+      PLOG(ERROR) << "Can't delete task. " << std::hex << hr;
+      return false;
+    }
+
+    DCHECK(!IsTaskRegistered(task_name));
+    return true;
+  }
+
+  bool RegisterTask(const wchar_t* task_name,
+                    const wchar_t* task_description,
+                    const base::CommandLine& run_command,
+                    TriggerType trigger_type,
+                    bool hidden) override {
+    DCHECK(task_name);
+    DCHECK(task_description);
+    if (!DeleteTask(task_name))
+      return false;
+
+    // Create the task definition object to create the task.
+    Microsoft::WRL::ComPtr<ITaskDefinition> task;
+    DCHECK(task_service_);
+    HRESULT hr = task_service_->NewTask(0, &task);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't create new task. " << std::hex << hr;
+      return false;
+    }
+
+    base::win::ScopedBstr user_name;
+    if (!GetCurrentUser(&user_name))
+      return false;
+
+    if (trigger_type != TRIGGER_TYPE_NOW) {
+      // Allow the task to run elevated on startup.
+      Microsoft::WRL::ComPtr<IPrincipal> principal;
+      hr = task->get_Principal(&principal);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't get principal. " << std::hex << hr;
+        return false;
+      }
+
+      hr = principal->put_UserId(user_name);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put user id. " << std::hex << hr;
+        return false;
+      }
+
+      hr = principal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put logon type. " << std::hex << hr;
+        return false;
+      }
+    }
+
+    Microsoft::WRL::ComPtr<IRegistrationInfo> registration_info;
+    hr = task->get_RegistrationInfo(&registration_info);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't get registration info. " << std::hex << hr;
+      return false;
+    }
+
+    hr = registration_info->put_Author(user_name);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't set registration info author. " << std::hex << hr;
+      return false;
+    }
+
+    base::win::ScopedBstr description(task_description);
+    hr = registration_info->put_Description(description);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't set description. " << std::hex << hr;
+      return false;
+    }
+
+    Microsoft::WRL::ComPtr<ITaskSettings> task_settings;
+    hr = task->get_Settings(&task_settings);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't get task settings. " << std::hex << hr;
+      return false;
+    }
+
+    hr = task_settings->put_StartWhenAvailable(VARIANT_TRUE);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't put 'StartWhenAvailable' to true. " << std::hex
+                  << hr;
+      return false;
+    }
+
+    // TODO(csharp): Find a way to only set this for log upload retry.
+    hr = task_settings->put_DeleteExpiredTaskAfter(
+        base::win::ScopedBstr(kZeroMinuteText));
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't put 'DeleteExpiredTaskAfter'. " << std::hex << hr;
+      return false;
+    }
+
+    hr = task_settings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't put 'DisallowStartIfOnBatteries' to false. "
+                  << std::hex << hr;
+      return false;
+    }
+
+    hr = task_settings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't put 'StopIfGoingOnBatteries' to false. " << std::hex
+                  << hr;
+      return false;
+    }
+
+    if (hidden) {
+      hr = task_settings->put_Hidden(VARIANT_TRUE);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put 'Hidden' to true. " << std::hex << hr;
+        return false;
+      }
+    }
+
+    Microsoft::WRL::ComPtr<ITriggerCollection> trigger_collection;
+    hr = task->get_Triggers(&trigger_collection);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't get trigger collection. " << std::hex << hr;
+      return false;
+    }
+
+    TASK_TRIGGER_TYPE2 task_trigger_type = TASK_TRIGGER_EVENT;
+    base::win::ScopedBstr repetition_interval;
+    switch (trigger_type) {
+      case TRIGGER_TYPE_POST_REBOOT:
+        task_trigger_type = TASK_TRIGGER_LOGON;
+        break;
+      case TRIGGER_TYPE_NOW:
+        task_trigger_type = TASK_TRIGGER_REGISTRATION;
+        break;
+      case TRIGGER_TYPE_HOURLY:
+      case TRIGGER_TYPE_EVERY_FIVE_HOURS:
+        task_trigger_type = TASK_TRIGGER_DAILY;
+        if (trigger_type == TRIGGER_TYPE_EVERY_FIVE_HOURS) {
+          repetition_interval.Reset(::SysAllocString(kFiveHoursText));
+        } else if (trigger_type == TRIGGER_TYPE_HOURLY) {
+          repetition_interval.Reset(::SysAllocString(kOneHourText));
+        } else {
+          NOTREACHED() << "Unknown TriggerType?";
+        }
+        break;
+      default:
+        NOTREACHED() << "Unknown TriggerType?";
+    }
+
+    Microsoft::WRL::ComPtr<ITrigger> trigger;
+    hr = trigger_collection->Create(task_trigger_type, &trigger);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't create trigger of type " << task_trigger_type
+                  << ". " << std::hex << hr;
+      return false;
+    }
+
+    if (trigger_type == TRIGGER_TYPE_HOURLY ||
+        trigger_type == TRIGGER_TYPE_EVERY_FIVE_HOURS) {
+      Microsoft::WRL::ComPtr<IDailyTrigger> daily_trigger;
+      hr = trigger.As(&daily_trigger);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't Query for registration trigger. " << std::hex
+                    << hr;
+        return false;
+      }
+
+      hr = daily_trigger->put_DaysInterval(1);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put 'DaysInterval' to 1, " << std::hex << hr;
+        return false;
+      }
+
+      Microsoft::WRL::ComPtr<IRepetitionPattern> repetition_pattern;
+      hr = trigger->get_Repetition(&repetition_pattern);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't get 'Repetition'. " << std::hex << hr;
+        return false;
+      }
+
+      // The duration is the time to keep repeating until the next daily
+      // trigger.
+      hr = repetition_pattern->put_Duration(
+          base::win::ScopedBstr(kTwentyFourHoursText));
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put 'Duration' to " << kTwentyFourHoursText
+                    << ". " << std::hex << hr;
+        return false;
+      }
+
+      hr = repetition_pattern->put_Interval(repetition_interval);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put 'Interval' to " << repetition_interval << ". "
+                    << std::hex << hr;
+        return false;
+      }
+
+      // Start now.
+      base::Time now(base::Time::NowFromSystemTime());
+      base::win::ScopedBstr start_boundary(GetTimestampString(now));
+      hr = trigger->put_StartBoundary(start_boundary);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put 'StartBoundary' to " << start_boundary << ". "
+                    << std::hex << hr;
+        return false;
+      }
+    }
+
+    if (trigger_type == TRIGGER_TYPE_POST_REBOOT) {
+      Microsoft::WRL::ComPtr<ILogonTrigger> logon_trigger;
+      hr = trigger.As(&logon_trigger);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't query trigger for 'ILogonTrigger'. " << std::hex
+                    << hr;
+        return false;
+      }
+
+      hr = logon_trigger->put_Delay(base::win::ScopedBstr(kFifteenMinutesText));
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Can't put 'Delay'. " << std::hex << hr;
+        return false;
+      }
+    }
+
+    // None of the triggers should go beyond kNumDaysBeforeExpiry.
+    base::Time expiry_date(base::Time::NowFromSystemTime() +
+                           base::TimeDelta::FromDays(kNumDaysBeforeExpiry));
+    base::win::ScopedBstr end_boundary(GetTimestampString(expiry_date));
+    hr = trigger->put_EndBoundary(end_boundary);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't put 'EndBoundary' to " << end_boundary << ". "
+                  << std::hex << hr;
+      return false;
+    }
+
+    Microsoft::WRL::ComPtr<IActionCollection> actions;
+    hr = task->get_Actions(&actions);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't get actions collection. " << std::hex << hr;
+      return false;
+    }
+
+    Microsoft::WRL::ComPtr<IAction> action;
+    hr = actions->Create(TASK_ACTION_EXEC, &action);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't create exec action. " << std::hex << hr;
+      return false;
+    }
+
+    Microsoft::WRL::ComPtr<IExecAction> exec_action;
+    hr = action.As(&exec_action);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't query for exec action. " << std::hex << hr;
+      return false;
+    }
+
+    base::win::ScopedBstr path(run_command.GetProgram().value());
+    hr = exec_action->put_Path(path);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't set path of exec action. " << std::hex << hr;
+      return false;
+    }
+
+    base::win::ScopedBstr args(run_command.GetArgumentsString());
+    hr = exec_action->put_Arguments(args);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Can't set arguments of exec action. " << std::hex << hr;
+      return false;
+    }
+
+    Microsoft::WRL::ComPtr<IRegisteredTask> registered_task;
+    base::win::ScopedVariant user(user_name);
+
+    DCHECK(root_task_folder_);
+    hr = root_task_folder_->RegisterTaskDefinition(
+        base::win::ScopedBstr(task_name), task.Get(), TASK_CREATE,
+        *user.AsInput(),  // Not really input, but API expect non-const.
+        base::win::ScopedVariant::kEmptyVariant, TASK_LOGON_NONE,
+        base::win::ScopedVariant::kEmptyVariant, &registered_task);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "RegisterTaskDefinition failed. " << std::hex << hr << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return false;
+    }
+
+    DCHECK(IsTaskRegistered(task_name));
+
+    VLOG(1) << "Successfully registered: "
+            << run_command.GetCommandLineString();
+    return true;
+  }
+
+ private:
+  // Helper class that lets us iterate over all registered tasks.
+  class TaskIterator {
+   public:
+    explicit TaskIterator(ITaskFolder* task_folder) {
+      DCHECK(task_folder);
+      HRESULT hr = task_folder->GetTasks(TASK_ENUM_HIDDEN, &tasks_);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get registered tasks from folder. "
+                    << std::hex << hr;
+        done_ = true;
+        return;
+      }
+      hr = tasks_->get_Count(&num_tasks_);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get registered tasks count. " << std::hex
+                    << hr;
+        done_ = true;
+        return;
+      }
+      Next();
+    }
+
+    // Increment to the next valid item in the task list. Skip entries for
+    // which we cannot retrieve a name.
+    void Next() {
+      DCHECK(!done_);
+      task_.Reset();
+      name_.clear();
+      if (++task_index_ >= num_tasks_) {
+        done_ = true;
+        return;
+      }
+
+      // Note: get_Item uses 1 based indices.
+      HRESULT hr =
+          tasks_->get_Item(base::win::ScopedVariant(task_index_ + 1), &task_);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get task at index: " << task_index_ << ". "
+                    << std::hex << hr;
+        Next();
+        return;
+      }
+
+      base::win::ScopedBstr task_name_bstr;
+      hr = task_->get_Name(task_name_bstr.Receive());
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get name at index: " << task_index_ << ". "
+                    << std::hex << hr;
+        Next();
+        return;
+      }
+      name_ = base::string16(task_name_bstr ? task_name_bstr : L"");
+    }
+
+    // Detach the currently active task and pass ownership to the caller.
+    // After this method has been called, the -> operator must no longer be
+    // used.
+    IRegisteredTask* Detach() { return task_.Detach(); }
+
+    // Provide access to the current task.
+    IRegisteredTask* operator->() const {
+      IRegisteredTask* result = task_.Get();
+      DCHECK(result);
+      return result;
+    }
+
+    const base::string16& name() const { return name_; }
+    bool done() const { return done_; }
+
+   private:
+    Microsoft::WRL::ComPtr<IRegisteredTaskCollection> tasks_;
+    Microsoft::WRL::ComPtr<IRegisteredTask> task_;
+    base::string16 name_;
+    long task_index_ = -1;  // NOLINT, API requires a long.
+    long num_tasks_ = 0;    // NOLINT, API requires a long.
+    bool done_ = false;
+  };
+
+  // Return the task with |task_name| and false if not found. |task| can be null
+  // when only interested in task's existence.
+  bool GetTask(const wchar_t* task_name, IRegisteredTask** task) {
+    for (TaskIterator it(root_task_folder_.Get()); !it.done(); it.Next()) {
+      if (::_wcsicmp(it.name().c_str(), task_name) == 0) {
+        if (task)
+          *task = it.Detach();
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // Return the description of the task.
+  HRESULT GetTaskDescription(IRegisteredTask* task,
+                             base::string16* description) {
+    DCHECK(task);
+    DCHECK(description);
+
+    base::win::ScopedBstr task_name_bstr;
+    HRESULT hr = task->get_Name(task_name_bstr.Receive());
+    base::string16 task_name =
+        base::string16(task_name_bstr ? task_name_bstr : L"");
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get task name";
+    }
+
+    Microsoft::WRL::ComPtr<ITaskDefinition> task_info;
+    hr = task->get_Definition(&task_info);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get definition for task, " << task_name << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return hr;
+    }
+
+    Microsoft::WRL::ComPtr<IRegistrationInfo> reg_info;
+    hr = task_info->get_RegistrationInfo(&reg_info);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get registration info, " << task_name << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return hr;
+    }
+
+    base::win::ScopedBstr raw_description;
+    hr = reg_info->get_Description(raw_description.Receive());
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get description, " << task_name << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return hr;
+    }
+    *description = base::string16(raw_description ? raw_description : L"");
+    return ERROR_SUCCESS;
+  }
+
+  // Return all executable actions associated with the given task. Non-exec
+  // actions are silently ignored.
+  bool GetTaskExecActions(IRegisteredTask* task,
+                          std::vector<TaskExecAction>* actions) {
+    DCHECK(task);
+    DCHECK(actions);
+    Microsoft::WRL::ComPtr<ITaskDefinition> task_definition;
+    HRESULT hr = task->get_Definition(&task_definition);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Failed to get definition of task, " << std::hex << hr;
+      return false;
+    }
+
+    Microsoft::WRL::ComPtr<IActionCollection> action_collection;
+    hr = task_definition->get_Actions(&action_collection);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Failed to get action collection, " << std::hex << hr;
+      return false;
+    }
+
+    long actions_count = 0;  // NOLINT, API requires a long.
+    hr = action_collection->get_Count(&actions_count);
+    if (FAILED(hr)) {
+      PLOG(ERROR) << "Failed to get number of actions, " << std::hex << hr;
+      return false;
+    }
+
+    // Find and return as many exec actions as possible in |actions| and return
+    // false if there were any errors on the way. Note that the indexing of
+    // actions is 1-based.
+    bool success = true;
+    for (long action_index = 1;  // NOLINT
+         action_index <= actions_count; ++action_index) {
+      Microsoft::WRL::ComPtr<IAction> action;
+      hr = action_collection->get_Item(action_index, &action);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get action at index " << action_index << ", "
+                    << std::hex << hr;
+        success = false;
+        continue;
+      }
+
+      ::TASK_ACTION_TYPE action_type;
+      hr = action->get_Type(&action_type);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get the type of action at index "
+                    << action_index << ", " << std::hex << hr;
+        success = false;
+        continue;
+      }
+
+      // We only care about exec actions for now. The other types are
+      // TASK_ACTION_COM_HANDLER, TASK_ACTION_SEND_EMAIL,
+      // TASK_ACTION_SHOW_MESSAGE. The latter two are marked as deprecated in
+      // the Task Scheduler's GUI.
+      if (action_type != ::TASK_ACTION_EXEC)
+        continue;
+
+      Microsoft::WRL::ComPtr<IExecAction> exec_action;
+      hr = action.As(&exec_action);
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to query from action, " << std::hex << hr;
+        success = false;
+        continue;
+      }
+
+      base::win::ScopedBstr application_path;
+      hr = exec_action->get_Path(application_path.Receive());
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get path from action, " << std::hex << hr;
+        success = false;
+        continue;
+      }
+
+      base::win::ScopedBstr working_dir;
+      hr = exec_action->get_WorkingDirectory(working_dir.Receive());
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get working directory for action, "
+                    << std::hex << hr;
+        success = false;
+        continue;
+      }
+
+      base::win::ScopedBstr parameters;
+      hr = exec_action->get_Arguments(parameters.Receive());
+      if (FAILED(hr)) {
+        PLOG(ERROR) << "Failed to get arguments from action of task, "
+                    << std::hex << hr;
+        success = false;
+        continue;
+      }
+
+      actions->push_back(
+          {base::FilePath(application_path ? application_path : L""),
+           base::FilePath(working_dir ? working_dir : L""),
+           base::string16(parameters ? parameters : L"")});
+    }
+    return success;
+  }
+
+  // Return the log-on type required for the task's actions to be run.
+  HRESULT GetTaskLogonType(IRegisteredTask* task, uint32_t* logon_type) {
+    DCHECK(task);
+    DCHECK(logon_type);
+    Microsoft::WRL::ComPtr<ITaskDefinition> task_info;
+    HRESULT hr = task->get_Definition(&task_info);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get definition, " << std::hex << hr << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return hr;
+    }
+
+    Microsoft::WRL::ComPtr<IPrincipal> principal;
+    hr = task_info->get_Principal(&principal);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get principal info, " << std::hex << hr << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return hr;
+    }
+
+    TASK_LOGON_TYPE raw_logon_type;
+    hr = principal->get_LogonType(&raw_logon_type);
+    if (FAILED(hr)) {
+      LOG(ERROR) << "Failed to get logon type info, " << std::hex << hr << ": "
+                 << logging::SystemErrorCodeToString(hr);
+      return hr;
+    }
+
+    switch (raw_logon_type) {
+      case TASK_LOGON_INTERACTIVE_TOKEN:
+        *logon_type = LOGON_INTERACTIVE;
+        break;
+      case TASK_LOGON_GROUP:     // fall-thru
+      case TASK_LOGON_PASSWORD:  // fall-thru
+      case TASK_LOGON_SERVICE_ACCOUNT:
+        *logon_type = LOGON_SERVICE;
+        break;
+      case TASK_LOGON_S4U:
+        *logon_type = LOGON_SERVICE | LOGON_S4U;
+        break;
+      case TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD:
+        *logon_type = LOGON_INTERACTIVE | LOGON_SERVICE;
+        break;
+      default:
+        *logon_type = LOGON_UNKNOWN;
+        break;
+    }
+    return ERROR_SUCCESS;
+  }
+
+  static Microsoft::WRL::ComPtr<ITaskService> task_service_;
+  static Microsoft::WRL::ComPtr<ITaskFolder> root_task_folder_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerV2);
+};
+
+Microsoft::WRL::ComPtr<ITaskService> TaskSchedulerV2::task_service_;
+Microsoft::WRL::ComPtr<ITaskFolder> TaskSchedulerV2::root_task_folder_;
+
+}  // namespace
+
+TaskScheduler::TaskInfo::TaskInfo() = default;
+
+TaskScheduler::TaskInfo::TaskInfo(const TaskScheduler::TaskInfo&) = default;
+
+TaskScheduler::TaskInfo::TaskInfo(TaskScheduler::TaskInfo&&) = default;
+
+TaskScheduler::TaskInfo& TaskScheduler::TaskInfo::operator=(
+    const TaskScheduler::TaskInfo&) = default;
+
+TaskScheduler::TaskInfo& TaskScheduler::TaskInfo::operator=(
+    TaskScheduler::TaskInfo&&) = default;
+
+TaskScheduler::TaskInfo::~TaskInfo() = default;
+
+// static.
+bool TaskScheduler::Initialize() {
+  return TaskSchedulerV2::Initialize();
+}
+
+// static.
+void TaskScheduler::Terminate() {
+  TaskSchedulerV2::Terminate();
+}
+
+// static.
+std::unique_ptr<TaskScheduler> TaskScheduler::CreateInstance() {
+  return std::make_unique<TaskSchedulerV2>();
+}
+
+TaskScheduler::TaskScheduler() = default;
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/task_scheduler.h b/src/cobalt/updater/win/task_scheduler.h
new file mode 100644
index 0000000..2708621
--- /dev/null
+++ b/src/cobalt/updater/win/task_scheduler.h
@@ -0,0 +1,141 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_TASK_SCHEDULER_H_
+#define CHROME_UPDATER_WIN_TASK_SCHEDULER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+namespace base {
+class CommandLine;
+class Time;
+}  // namespace base
+
+namespace updater {
+
+// This class wraps a scheduled task and expose an API to parametrize a task
+// before calling |Register|, or to verify its existence, or delete it.
+class TaskScheduler {
+ public:
+  // The type of trigger to register for this task.
+  enum TriggerType {
+    TRIGGER_TYPE_POST_REBOOT = 0,  // Only run once post-reboot.
+    TRIGGER_TYPE_NOW = 1,          // Run right now (mainly for tests).
+    TRIGGER_TYPE_HOURLY = 2,       // Run every hour.
+    TRIGGER_TYPE_EVERY_FIVE_HOURS = 3,
+    TRIGGER_TYPE_MAX,
+  };
+
+  // The log-on requirements for a task to be scheduled. Note that a task can
+  // have both the interactive and service bit set. In that case the
+  // interactive token will be used when available, and a stored password
+  // otherwise.
+  enum LogonType {
+    LOGON_UNKNOWN = 0,
+
+    // Run the task with the user's interactive token when logged in.
+    LOGON_INTERACTIVE = 1 << 0,
+
+    // The task will run whether the user is logged in or not using either a
+    // user/password specified at registration time, a service account or a
+    // service for user (S4U).
+    LOGON_SERVICE = 1 << 1,
+
+    // The task is run as a service for user and as such will be on an
+    // invisible desktop.
+    LOGON_S4U = 1 << 2,
+  };
+
+  // Struct representing a single scheduled task action.
+  struct TaskExecAction {
+    base::FilePath application_path;
+    base::FilePath working_dir;
+    base::string16 arguments;
+  };
+
+  // Detailed description of a scheduled task. This type is returned by the
+  // GetTaskInfo() method.
+  struct TaskInfo {
+    TaskInfo();
+    TaskInfo(const TaskInfo&);
+    TaskInfo(TaskInfo&&);
+    ~TaskInfo();
+    TaskInfo& operator=(const TaskInfo&);
+    TaskInfo& operator=(TaskInfo&&);
+
+    base::string16 name;
+
+    // Description of the task.
+    base::string16 description;
+
+    // A scheduled task can have more than one action associated with it and
+    // actions can be of types other than executables (for example, sending
+    // emails). This list however contains only the execution actions.
+    std::vector<TaskExecAction> exec_actions;
+
+    // The log-on requirements for the task's actions to be run. A bit mask with
+    // the mapping defined by LogonType.
+    uint32_t logon_type = 0;
+  };
+
+  // Control the lifespan of static data for the TaskScheduler. |Initialize|
+  // must be called before the first call to |CreateInstance|, and not other
+  // methods can be called after |Terminate| was called (unless |Initialize| is
+  // called again). |Initialize| can't be called out of balance with
+  // |Terminate|. |Terminate| can be called any number of times.
+  static bool Initialize();
+  static void Terminate();
+
+  static std::unique_ptr<TaskScheduler> CreateInstance();
+  virtual ~TaskScheduler() {}
+
+  // Identify whether the task is registered or not.
+  virtual bool IsTaskRegistered(const wchar_t* task_name) = 0;
+
+  // Return the time of the next schedule run for the given task name. Return
+  // false on failure.
+  virtual bool GetNextTaskRunTime(const wchar_t* task_name,
+                                  base::Time* next_run_time) = 0;
+
+  // Delete the task if it exists. No-op if the task doesn't exist. Return false
+  // on failure to delete an existing task.
+  virtual bool DeleteTask(const wchar_t* task_name) = 0;
+
+  // Enable or disable task based on the value of |enabled|. Return true if the
+  // task exists and the operation succeeded.
+  virtual bool SetTaskEnabled(const wchar_t* task_name, bool enabled) = 0;
+
+  // Return true if task exists and is enabled.
+  virtual bool IsTaskEnabled(const wchar_t* task_name) = 0;
+
+  // List all currently registered scheduled tasks.
+  virtual bool GetTaskNameList(std::vector<base::string16>* task_names) = 0;
+
+  // Return detailed information about a task. Return true if no errors were
+  // encountered. On error, the struct is left unmodified.
+  virtual bool GetTaskInfo(const wchar_t* task_name, TaskInfo* info) = 0;
+
+  // Register the task to run the specified application and using the given
+  // |trigger_type|.
+  virtual bool RegisterTask(const wchar_t* task_name,
+                            const wchar_t* task_description,
+                            const base::CommandLine& run_command,
+                            TriggerType trigger_type,
+                            bool hidden) = 0;
+
+ protected:
+  TaskScheduler();
+
+  DISALLOW_COPY_AND_ASSIGN(TaskScheduler);
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_TASK_SCHEDULER_H_
diff --git a/src/cobalt/updater/win/task_scheduler_unittest.cc b/src/cobalt/updater/win/task_scheduler_unittest.cc
new file mode 100644
index 0000000..3b20a78
--- /dev/null
+++ b/src/cobalt/updater/win/task_scheduler_unittest.cc
@@ -0,0 +1,319 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/task_scheduler.h"
+
+#include <taskschd.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/stl_util.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.h"
+#include "base/time/time.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
+#include "chrome/updater/win/test/test_executables.h"
+#include "chrome/updater/win/test/test_strings.h"
+#include "chrome/updater/win/util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+
+namespace {
+
+// The name of the tasks as will be visible in the scheduler so we know we can
+// safely delete them if they get stuck for whatever reason.
+const wchar_t kTaskName1[] = L"Chrome Updater Test task 1 (delete me)";
+const wchar_t kTaskName2[] = L"Chrome Updater Test task 2 (delete me)";
+// Optional descriptions for the above tasks.
+const wchar_t kTaskDescription1[] =
+    L"Task 1 used only for Chrome Updater unit testing.";
+const wchar_t kTaskDescription2[] =
+    L"Task 2 used only for Chrome Updater unit testing.";
+// A command-line switch used in testing.
+const char kTestSwitch[] = "a_switch";
+
+class TaskSchedulerTests : public testing::Test {
+ public:
+  void SetUp() override {
+    task_scheduler_ = TaskScheduler::CreateInstance();
+    // In case previous tests failed and left these tasks in the scheduler.
+    EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+    EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2));
+    ASSERT_FALSE(IsProcessRunning(kTestProcessExecutableName));
+  }
+
+  void TearDown() override {
+    // Make sure to not leave tasks behind.
+    EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+    EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2));
+    // Make sure every processes launched with scheduled task are completed.
+    ASSERT_TRUE(WaitForProcessesStopped(kTestProcessExecutableName));
+  }
+
+ protected:
+  std::unique_ptr<TaskScheduler> task_scheduler_;
+};
+
+}  // namespace
+
+TEST_F(TaskSchedulerTests, DeleteAndIsRegistered) {
+  EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  // Construct the full-path of the test executable.
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  // Validate that the task is properly seen as registered when it is.
+  EXPECT_TRUE(
+      task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line,
+                                    TaskScheduler::TRIGGER_TYPE_NOW, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  // Validate that a task with a similar name is not seen as registered.
+  EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName2));
+
+  // While the first one is still seen as registered, until it gets deleted.
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+  EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1));
+  // The other task should still not be registered.
+  EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName2));
+}
+
+TEST_F(TaskSchedulerTests, RunAProgramNow) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  // Create a unique name for a shared event to be waited for in this process
+  // and signaled in the test process to confirm it was scheduled and ran.
+  const base::string16 event_name =
+      base::StrCat({kTestProcessExecutableName, L"-",
+                    base::NumberToString16(::GetCurrentProcessId())});
+  base::WaitableEvent event(base::win::ScopedHandle(
+      ::CreateEvent(nullptr, FALSE, FALSE, event_name.c_str())));
+  ASSERT_NE(event.handle(), nullptr);
+
+  command_line.AppendSwitchNative(kTestEventToSignal, event_name);
+  EXPECT_TRUE(
+      task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line,
+                                    TaskScheduler::TRIGGER_TYPE_NOW, false));
+  EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+  base::Time next_run_time;
+  EXPECT_FALSE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time));
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+}
+
+TEST_F(TaskSchedulerTests, Hourly) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  base::Time now(base::Time::NowFromSystemTime());
+  EXPECT_TRUE(
+      task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line,
+                                    TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  base::TimeDelta one_hour(base::TimeDelta::FromHours(1));
+  base::TimeDelta one_minute(base::TimeDelta::FromMinutes(1));
+
+  base::Time next_run_time;
+  EXPECT_TRUE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time));
+  EXPECT_LT(next_run_time, now + one_hour + one_minute);
+  EXPECT_GT(next_run_time, now + one_hour - one_minute);
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+  EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1));
+  EXPECT_FALSE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time));
+}
+
+TEST_F(TaskSchedulerTests, EveryFiveHours) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  base::Time now(base::Time::NowFromSystemTime());
+  EXPECT_TRUE(task_scheduler_->RegisterTask(
+      kTaskName1, kTaskDescription1, command_line,
+      TaskScheduler::TRIGGER_TYPE_EVERY_FIVE_HOURS, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  base::TimeDelta six_hours(base::TimeDelta::FromHours(5));
+  base::TimeDelta one_minute(base::TimeDelta::FromMinutes(1));
+
+  base::Time next_run_time;
+  EXPECT_TRUE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time));
+  EXPECT_LT(next_run_time, now + six_hours + one_minute);
+  EXPECT_GT(next_run_time, now + six_hours - one_minute);
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+  EXPECT_FALSE(task_scheduler_->IsTaskRegistered(kTaskName1));
+  EXPECT_FALSE(task_scheduler_->GetNextTaskRunTime(kTaskName1, &next_run_time));
+}
+
+TEST_F(TaskSchedulerTests, SetTaskEnabled) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  EXPECT_TRUE(
+      task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line,
+                                    TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->IsTaskEnabled(kTaskName1));
+
+  EXPECT_TRUE(task_scheduler_->SetTaskEnabled(kTaskName1, true));
+  EXPECT_TRUE(task_scheduler_->IsTaskEnabled(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->SetTaskEnabled(kTaskName1, false));
+  EXPECT_FALSE(task_scheduler_->IsTaskEnabled(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->SetTaskEnabled(kTaskName1, true));
+  EXPECT_TRUE(task_scheduler_->IsTaskEnabled(kTaskName1));
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+}
+
+TEST_F(TaskSchedulerTests, GetTaskNameList) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  EXPECT_TRUE(
+      task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line,
+                                    TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+  EXPECT_TRUE(
+      task_scheduler_->RegisterTask(kTaskName2, kTaskDescription2, command_line,
+                                    TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName2));
+
+  std::vector<base::string16> task_names;
+  EXPECT_TRUE(task_scheduler_->GetTaskNameList(&task_names));
+  EXPECT_TRUE(base::Contains(task_names, kTaskName1));
+  EXPECT_TRUE(base::Contains(task_names, kTaskName2));
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2));
+}
+
+TEST_F(TaskSchedulerTests, GetTasksIncludesHidden) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  EXPECT_TRUE(
+      task_scheduler_->RegisterTask(kTaskName1, kTaskDescription1, command_line,
+                                    TaskScheduler::TRIGGER_TYPE_HOURLY, true));
+
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  std::vector<base::string16> task_names;
+  EXPECT_TRUE(task_scheduler_->GetTaskNameList(&task_names));
+  EXPECT_TRUE(base::Contains(task_names, kTaskName1));
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+}
+
+TEST_F(TaskSchedulerTests, GetTaskInfoExecActions) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line1(
+      executable_path.Append(kTestProcessExecutableName));
+
+  EXPECT_TRUE(task_scheduler_->RegisterTask(
+      kTaskName1, kTaskDescription1, command_line1,
+      TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  TaskScheduler::TaskInfo info;
+  EXPECT_FALSE(task_scheduler_->GetTaskInfo(kTaskName2, &info));
+  EXPECT_EQ(0UL, info.exec_actions.size());
+  EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName1, &info));
+  ASSERT_EQ(1UL, info.exec_actions.size());
+  EXPECT_EQ(command_line1.GetProgram(), info.exec_actions[0].application_path);
+  EXPECT_EQ(command_line1.GetArgumentsString(), info.exec_actions[0].arguments);
+
+  base::CommandLine command_line2(
+      executable_path.Append(kTestProcessExecutableName));
+  command_line2.AppendSwitch(kTestSwitch);
+  EXPECT_TRUE(task_scheduler_->RegisterTask(
+      kTaskName2, kTaskDescription2, command_line2,
+      TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName2));
+
+  // The |info| struct is re-used to ensure that new task information overwrites
+  // the previous contents of the struct.
+  EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName2, &info));
+  ASSERT_EQ(1UL, info.exec_actions.size());
+  EXPECT_EQ(command_line2.GetProgram(), info.exec_actions[0].application_path);
+  EXPECT_EQ(command_line2.GetArgumentsString(), info.exec_actions[0].arguments);
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2));
+}
+
+TEST_F(TaskSchedulerTests, GetTaskInfoNameAndDescription) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line1(
+      executable_path.Append(kTestProcessExecutableName));
+
+  EXPECT_TRUE(task_scheduler_->RegisterTask(
+      kTaskName1, kTaskDescription1, command_line1,
+      TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  TaskScheduler::TaskInfo info;
+  EXPECT_FALSE(task_scheduler_->GetTaskInfo(kTaskName2, &info));
+  EXPECT_EQ(L"", info.description);
+  EXPECT_EQ(L"", info.name);
+
+  EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName1, &info));
+  EXPECT_EQ(kTaskDescription1, info.description);
+  EXPECT_EQ(kTaskName1, info.name);
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+}
+
+TEST_F(TaskSchedulerTests, GetTaskInfoLogonType) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line1(
+      executable_path.Append(kTestProcessExecutableName));
+
+  EXPECT_TRUE(task_scheduler_->RegisterTask(
+      kTaskName1, kTaskDescription1, command_line1,
+      TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+
+  TaskScheduler::TaskInfo info;
+  EXPECT_FALSE(task_scheduler_->GetTaskInfo(kTaskName2, &info));
+  EXPECT_EQ(0U, info.logon_type);
+  EXPECT_TRUE(task_scheduler_->GetTaskInfo(kTaskName1, &info));
+  EXPECT_TRUE(info.logon_type & TaskScheduler::LOGON_INTERACTIVE);
+  EXPECT_FALSE(info.logon_type & TaskScheduler::LOGON_SERVICE);
+  EXPECT_FALSE(info.logon_type & TaskScheduler::LOGON_S4U);
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/test/BUILD.gn b/src/cobalt/updater/win/test/BUILD.gn
new file mode 100644
index 0000000..e290745
--- /dev/null
+++ b/src/cobalt/updater/win/test/BUILD.gn
@@ -0,0 +1,66 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+source_set("test_strings") {
+  testonly = true
+
+  sources = [
+    "test_strings.cc",
+    "test_strings.h",
+  ]
+}
+
+source_set("test_common") {
+  testonly = true
+
+  sources = [
+    "test_inheritable_event.cc",
+    "test_inheritable_event.h",
+    "test_initializer.cc",
+    "test_initializer.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome/updater:common",
+  ]
+}
+
+source_set("test_executables") {
+  testonly = true
+
+  sources = [
+    "test_executables.cc",
+    "test_executables.h",
+  ]
+
+  data_deps = [
+    ":updater_test_process",
+  ]
+
+  deps = [
+    ":test_common",
+    ":test_strings",
+    "//base",
+  ]
+}
+
+executable("updater_test_process") {
+  testonly = true
+
+  sources = [
+    "test_process_main.cc",
+  ]
+
+  deps = [
+    ":test_common",
+    ":test_strings",
+    "//base",
+    "//base/test:test_support",
+    "//build/win:default_exe_manifest",
+    "//chrome/updater/win:code",
+  ]
+}
diff --git a/src/cobalt/updater/win/test/test_executables.cc b/src/cobalt/updater/win/test/test_executables.cc
new file mode 100644
index 0000000..8f4e38e
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_executables.cc
@@ -0,0 +1,65 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/test/test_executables.h"
+
+#include <memory>
+
+#include "base/base_paths.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/win/win_util.h"
+#include "chrome/updater/updater_constants.h"
+#include "chrome/updater/win/test/test_inheritable_event.h"
+#include "chrome/updater/win/test/test_strings.h"
+
+namespace updater {
+
+// If you add another test executable here, also add it to the data_deps in
+// the "test_executables" target of updater/win/test/BUILD.gn.
+const base::char16 kTestProcessExecutableName[] = L"updater_test_process.exe";
+
+base::Process LongRunningProcess(base::CommandLine* cmd) {
+  base::FilePath exe_dir;
+  if (!base::PathService::Get(base::DIR_EXE, &exe_dir)) {
+    LOG(ERROR) << "Failed to get the executable path, unable to create always "
+                  "running process";
+    return base::Process();
+  }
+
+  base::FilePath exe_path = exe_dir.Append(updater::kTestProcessExecutableName);
+  base::CommandLine command_line(exe_path);
+
+  // This will ensure this new process will run for one minute before dying.
+  command_line.AppendSwitchASCII(updater::kTestSleepMinutesSwitch, "1");
+
+  auto init_done_event = updater::CreateInheritableEvent(
+      base::WaitableEvent::ResetPolicy::AUTOMATIC,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+  command_line.AppendSwitchNative(
+      updater::kInitDoneNotifierSwitch,
+      base::NumberToString16(
+          base::win::HandleToUint32(init_done_event->handle())));
+
+  if (cmd)
+    *cmd = command_line;
+
+  base::LaunchOptions launch_options;
+  launch_options.handles_to_inherit.push_back(init_done_event->handle());
+  base::Process result = base::LaunchProcess(command_line, launch_options);
+
+  if (!init_done_event->TimedWait(base::TimeDelta::FromSeconds(10))) {
+    LOG(ERROR) << "Process did not signal";
+    result.Terminate(/*exit_code=*/1, /*wait=*/false);
+    return base::Process();
+  }
+
+  return result;
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/test/test_executables.h b/src/cobalt/updater/win/test/test_executables.h
new file mode 100644
index 0000000..1ebe8b3
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_executables.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_TEST_TEST_EXECUTABLES_H_
+#define CHROME_UPDATER_WIN_TEST_TEST_EXECUTABLES_H_
+
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+
+namespace base {
+class CommandLine;
+}  // namespace base
+
+namespace updater {
+
+// The name of the service executable used for tests.
+extern const base::char16 kTestServiceExecutableName[];
+
+// The name of the executable used for tests.
+extern const base::char16 kTestProcessExecutableName[];
+
+// Creates a process that will run for a minute, which is long enough to be
+// killed by a reasonably fast unit or integration test.
+// Populates |command_line| with the used command line if it is not nullptr.
+base::Process LongRunningProcess(base::CommandLine* command_line);
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_TEST_TEST_EXECUTABLES_H_
diff --git a/src/cobalt/updater/win/test/test_inheritable_event.cc b/src/cobalt/updater/win/test/test_inheritable_event.cc
new file mode 100644
index 0000000..1866165
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_inheritable_event.cc
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/test/test_inheritable_event.h"
+
+#include <windows.h>
+
+#include <utility>
+
+#include "base/logging.h"
+
+namespace updater {
+
+std::unique_ptr<base::WaitableEvent> CreateInheritableEvent(
+    base::WaitableEvent::ResetPolicy reset_policy,
+    base::WaitableEvent::InitialState initial_state) {
+  SECURITY_ATTRIBUTES attributes = {sizeof(SECURITY_ATTRIBUTES)};
+  attributes.bInheritHandle = true;
+
+  HANDLE handle = ::CreateEvent(
+      &attributes, reset_policy == base::WaitableEvent::ResetPolicy::MANUAL,
+      initial_state == base::WaitableEvent::InitialState::SIGNALED, nullptr);
+  if (handle == nullptr || handle == INVALID_HANDLE_VALUE) {
+    PLOG(ERROR) << "Could not create inheritable event";
+    return nullptr;
+  }
+  base::win::ScopedHandle event_handle(handle);
+  return std::make_unique<base::WaitableEvent>(std::move(event_handle));
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/test/test_inheritable_event.h b/src/cobalt/updater/win/test/test_inheritable_event.h
new file mode 100644
index 0000000..3912f9d
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_inheritable_event.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_TEST_TEST_INHERITABLE_EVENT_H_
+#define CHROME_UPDATER_WIN_TEST_TEST_INHERITABLE_EVENT_H_
+
+#include <memory>
+
+#include "base/synchronization/waitable_event.h"
+
+namespace updater {
+
+std::unique_ptr<base::WaitableEvent> CreateInheritableEvent(
+    base::WaitableEvent::ResetPolicy reset_policy,
+    base::WaitableEvent::InitialState initial_state);
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_TEST_TEST_INHERITABLE_EVENT_H_
diff --git a/src/cobalt/updater/win/test/test_initializer.cc b/src/cobalt/updater/win/test/test_initializer.cc
new file mode 100644
index 0000000..e84914b
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_initializer.cc
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/test/test_initializer.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
+#include "chrome/updater/updater_constants.h"
+
+namespace updater {
+
+namespace {
+
+std::unique_ptr<base::WaitableEvent> SignalInitializationDone() {
+  base::win::ScopedHandle init_done_notifier;
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  uint32_t handle = 0;
+  if (command_line->HasSwitch(kInitDoneNotifierSwitch) &&
+      base::StringToUint(
+          command_line->GetSwitchValueNative(kInitDoneNotifierSwitch),
+          &handle)) {
+    init_done_notifier.Set(base::win::Uint32ToHandle(handle));
+  }
+
+  std::unique_ptr<base::WaitableEvent> notifier_event;
+  if (init_done_notifier.IsValid()) {
+    notifier_event =
+        std::make_unique<base::WaitableEvent>(std::move(init_done_notifier));
+    notifier_event->Signal();
+  }
+
+  return notifier_event;
+}
+
+}  // namespace
+
+void NotifyInitializationDoneForTesting() {
+  auto notifier_event = SignalInitializationDone();
+
+  // The event has ResetPolicy AUTOMATIC, so after the test is woken up it is
+  // immediately reset. Wait at most 5 seconds for the test to signal that
+  // it's ready using the same event before continuing. If the test takes
+  // longer than that stop waiting to prevent hangs.
+  if (notifier_event)
+    notifier_event->TimedWait(base::TimeDelta::FromSeconds(5));
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/test/test_initializer.h b/src/cobalt/updater/win/test/test_initializer.h
new file mode 100644
index 0000000..8c38625
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_initializer.h
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_TEST_TEST_INITIALIZER_H_
+#define CHROME_UPDATER_WIN_TEST_TEST_INITIALIZER_H_
+
+namespace updater {
+
+// Signals the event handle that was passed on the command line with
+// --init-done-notifier, if it exists. Then waits for the event to be signalled
+// again before continuing. This allows a test harness to pause the binary's
+// execution, do some extra setup, and resume it.
+// Note, this means the event must be AUTOMATIC.
+void NotifyInitializationDoneForTesting();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_TEST_TEST_INITIALIZER_H_
diff --git a/src/cobalt/updater/win/test/test_main.cc b/src/cobalt/updater/win/test/test_main.cc
new file mode 100644
index 0000000..edeb235
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_main.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "base/win/scoped_com_initializer.h"
+#include "chrome/updater/win/task_scheduler.h"
+#include "chrome/updater/win/util.h"
+
+int main(int argc, char** argv) {
+  // ScopedCOMInitializer keeps COM initialized in a specific scope. We don't
+  // want to initialize it for sandboxed processes, so manage its lifetime with
+  // a unique_ptr, which will call ScopedCOMInitializer's destructor when it
+  // goes out of scope below.
+  auto scoped_com_initializer =
+      std::make_unique<base::win::ScopedCOMInitializer>(
+          base::win::ScopedCOMInitializer::kMTA);
+  bool success = updater::InitializeCOMSecurity();
+  DCHECK(success) << "InitializeCOMSecurity() failed.";
+
+  success = updater::TaskScheduler::Initialize();
+  DCHECK(success) << "TaskScheduler::Initialize() failed.";
+
+  // Some tests will fail if two tests try to launch test_process.exe
+  // simultaneously, so run the tests serially. This will still shard them and
+  // distribute the shards to different swarming bots, but tests will run
+  // serially on each bot.
+  base::TestSuite test_suite(argc, argv);
+  const int result = base::LaunchUnitTestsWithOptions(
+      argc, argv,
+      /*parallel_jobs=*/1U,        // Like LaunchUnitTestsSerially
+      /*default_batch_limit=*/10,  // Like LaunchUnitTestsSerially
+      false,
+      base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
+
+  updater::TaskScheduler::Terminate();
+
+  return result;
+}
diff --git a/src/cobalt/updater/win/test/test_process_main.cc b/src/cobalt/updater/win/test/test_process_main.cc
new file mode 100644
index 0000000..32f34b4
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_process_main.cc
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "chrome/updater/win/test/test_initializer.h"
+#include "chrome/updater/win/test/test_strings.h"
+
+int main(int, char**) {
+  bool success = base::CommandLine::Init(0, nullptr);
+  DCHECK(success);
+
+  updater::NotifyInitializationDoneForTesting();
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(updater::kTestSleepMinutesSwitch)) {
+    std::string value =
+        command_line->GetSwitchValueASCII(updater::kTestSleepMinutesSwitch);
+    int sleep_minutes = 0;
+    if (base::StringToInt(value, &sleep_minutes) && sleep_minutes > 0) {
+      VLOG(1) << "Process is sleeping for " << sleep_minutes << " minutes";
+      ::Sleep(base::TimeDelta::FromMinutes(sleep_minutes).InMilliseconds());
+    } else {
+      LOG(ERROR) << "Invalid sleep delay value " << value;
+    }
+    NOTREACHED();
+    return 1;
+  }
+
+  if (command_line->HasSwitch(updater::kTestEventToSignal)) {
+    VLOG(1) << "Process is signaling event '" << updater::kTestEventToSignal
+            << "'";
+    base::string16 event_name =
+        command_line->GetSwitchValueNative(updater::kTestEventToSignal);
+    base::win::ScopedHandle handle(
+        ::OpenEvent(EVENT_ALL_ACCESS, TRUE, event_name.c_str()));
+    PLOG_IF(ERROR, !handle.IsValid())
+        << "Cannot create event '" << updater::kTestEventToSignal << "'";
+    base::WaitableEvent event(std::move(handle));
+    event.Signal();
+  }
+
+  VLOG(1) << "Process ended.";
+  return 0;
+}
diff --git a/src/cobalt/updater/win/test/test_strings.cc b/src/cobalt/updater/win/test/test_strings.cc
new file mode 100644
index 0000000..031003f
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_strings.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/test/test_strings.h"
+
+namespace updater {
+
+// Command line switches.
+const char kTestSleepMinutesSwitch[] = "test-sleep-minutes";
+const char kTestEventToSignal[] = "test-event-to-signal";
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/test/test_strings.h b/src/cobalt/updater/win/test/test_strings.h
new file mode 100644
index 0000000..ac95ff9
--- /dev/null
+++ b/src/cobalt/updater/win/test/test_strings.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_TEST_TEST_STRINGS_H_
+#define CHROME_UPDATER_WIN_TEST_TEST_STRINGS_H_
+
+#include <windows.h>
+
+namespace updater {
+
+// Command line switches.
+
+// The switch to activate the sleeping action for specified delay in minutes
+// before killing the process.
+extern const char kTestSleepMinutesSwitch[];
+
+// The switch to signal the event with the name given as a switch value.
+extern const char kTestEventToSignal[];
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_TEST_TEST_STRINGS_H_
diff --git a/src/cobalt/updater/win/updater.rc b/src/cobalt/updater/win/updater.rc
new file mode 100644
index 0000000..b53c04b
--- /dev/null
+++ b/src/cobalt/updater/win/updater.rc
@@ -0,0 +1,38 @@
+// Microsoft Visual C++ generated resource script.
+//
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+#include "verrsrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "#include ""winres.h""\r\n"
+    "#include ""verrsrc.h""\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (United States) resources
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/src/cobalt/updater/win/updater.ver b/src/cobalt/updater/win/updater.ver
new file mode 100644
index 0000000..bb2b1d6
--- /dev/null
+++ b/src/cobalt/updater/win/updater.ver
@@ -0,0 +1,3 @@
+INTERNAL_NAME=updater_exe

+ORIGINAL_FILENAME=updater.exe

+PRODUCT_FULLNAME=updater

diff --git a/src/cobalt/updater/win/util.cc b/src/cobalt/updater/win/util.cc
new file mode 100644
index 0000000..c7bf448
--- /dev/null
+++ b/src/cobalt/updater/win/util.cc
@@ -0,0 +1,144 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/util.h"
+
+#include <aclapi.h>
+#include <shlobj.h>
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/process/process_iterator.h"
+
+namespace updater {
+
+namespace {
+
+// The number of iterations to poll if a process is stopped correctly.
+const unsigned int kMaxProcessQueryIterations = 50;
+
+// The sleep time in ms between each poll.
+const unsigned int kProcessQueryWaitTimeMs = 100;
+
+}  // namespace
+
+HRESULT HRESULTFromLastError() {
+  const auto error_code = ::GetLastError();
+  return (error_code != NO_ERROR) ? HRESULT_FROM_WIN32(error_code) : E_FAIL;
+}
+
+bool IsProcessRunning(const wchar_t* executable) {
+  base::NamedProcessIterator iter(executable, nullptr);
+  const base::ProcessEntry* entry = iter.NextProcessEntry();
+  return entry != nullptr;
+}
+
+bool WaitForProcessesStopped(const wchar_t* executable) {
+  DCHECK(executable);
+  VLOG(1) << "Wait for processes '" << executable << "'.";
+
+  // Wait until the process is completely stopped.
+  for (unsigned int iteration = 0; iteration < kMaxProcessQueryIterations;
+       ++iteration) {
+    if (!IsProcessRunning(executable))
+      return true;
+    ::Sleep(kProcessQueryWaitTimeMs);
+  }
+
+  // The process didn't terminate.
+  LOG(ERROR) << "Cannot stop process '" << executable << "', timeout.";
+  return false;
+}
+
+// This sets up COM security to allow NetworkService, LocalService, and System
+// to call back into the process. It is largely inspired by
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa378987.aspx
+// static
+bool InitializeCOMSecurity() {
+  // Create the security descriptor explicitly as follows because
+  // CoInitializeSecurity() will not accept the relative security descriptors
+  // returned by ConvertStringSecurityDescriptorToSecurityDescriptor().
+  const size_t kSidCount = 5;
+  uint64_t* sids[kSidCount][(SECURITY_MAX_SID_SIZE + sizeof(uint64_t) - 1) /
+                            sizeof(uint64_t)] = {
+      {}, {}, {}, {}, {},
+  };
+
+  // These are ordered by most interesting ones to try first.
+  WELL_KNOWN_SID_TYPE sid_types[kSidCount] = {
+      WinBuiltinAdministratorsSid,  // administrator group security identifier
+      WinLocalServiceSid,           // local service security identifier
+      WinNetworkServiceSid,         // network service security identifier
+      WinSelfSid,                   // personal account security identifier
+      WinLocalSystemSid,            // local system security identifier
+  };
+
+  // This creates a security descriptor that is equivalent to the following
+  // security descriptor definition language (SDDL) string:
+  //   O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)
+  //   (A;;0x1;;;SY)(A;;0x1;;;BA)
+
+  // Initialize the security descriptor.
+  SECURITY_DESCRIPTOR security_desc = {};
+  if (!::InitializeSecurityDescriptor(&security_desc,
+                                      SECURITY_DESCRIPTOR_REVISION))
+    return false;
+
+  DCHECK_EQ(kSidCount, base::size(sids));
+  DCHECK_EQ(kSidCount, base::size(sid_types));
+  for (size_t i = 0; i < kSidCount; ++i) {
+    DWORD sid_bytes = sizeof(sids[i]);
+    if (!::CreateWellKnownSid(sid_types[i], nullptr, sids[i], &sid_bytes))
+      return false;
+  }
+
+  // Setup the access control entries (ACE) for COM. You may need to modify
+  // the access permissions for your application. COM_RIGHTS_EXECUTE and
+  // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required.
+  EXPLICIT_ACCESS explicit_access[kSidCount] = {};
+  DCHECK_EQ(kSidCount, base::size(sids));
+  DCHECK_EQ(kSidCount, base::size(explicit_access));
+  for (size_t i = 0; i < kSidCount; ++i) {
+    explicit_access[i].grfAccessPermissions =
+        COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
+    explicit_access[i].grfAccessMode = SET_ACCESS;
+    explicit_access[i].grfInheritance = NO_INHERITANCE;
+    explicit_access[i].Trustee.pMultipleTrustee = nullptr;
+    explicit_access[i].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+    explicit_access[i].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+    explicit_access[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+    explicit_access[i].Trustee.ptstrName = reinterpret_cast<LPTSTR>(sids[i]);
+  }
+
+  // Create an access control list (ACL) using this ACE list, if this succeeds
+  // make sure to ::LocalFree(acl).
+  ACL* acl = nullptr;
+  DWORD acl_result = ::SetEntriesInAcl(base::size(explicit_access),
+                                       explicit_access, nullptr, &acl);
+  if (acl_result != ERROR_SUCCESS || acl == nullptr)
+    return false;
+
+  HRESULT hr = E_FAIL;
+
+  // Set the security descriptor owner and group to Administrators and set the
+  // discretionary access control list (DACL) to the ACL.
+  if (::SetSecurityDescriptorOwner(&security_desc, sids[0], FALSE) &&
+      ::SetSecurityDescriptorGroup(&security_desc, sids[0], FALSE) &&
+      ::SetSecurityDescriptorDacl(&security_desc, TRUE, acl, FALSE)) {
+    // Initialize COM. You may need to modify the parameters of
+    // CoInitializeSecurity() for your application. Note that an
+    // explicit security descriptor is being passed down.
+    hr = ::CoInitializeSecurity(
+        &security_desc, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+        RPC_C_IMP_LEVEL_IDENTIFY, nullptr,
+        EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, nullptr);
+  }
+
+  ::LocalFree(acl);
+  return SUCCEEDED(hr);
+}
+
+}  // namespace updater
diff --git a/src/cobalt/updater/win/util.h b/src/cobalt/updater/win/util.h
new file mode 100644
index 0000000..257c677
--- /dev/null
+++ b/src/cobalt/updater/win/util.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UPDATER_WIN_UTIL_H_
+#define CHROME_UPDATER_WIN_UTIL_H_
+
+#include <winerror.h>
+
+#include "base/win/windows_types.h"
+
+namespace updater {
+
+// Returns the last error as an HRESULT or E_FAIL if last error is NO_ERROR.
+// This is not a drop in replacement for the HRESULT_FROM_WIN32 macro.
+// The macro maps a NO_ERROR to S_OK, whereas the HRESULTFromLastError maps a
+// NO_ERROR to E_FAIL.
+HRESULT HRESULTFromLastError();
+
+// Returns an HRESULT with a custom facility code representing an updater error.
+template <typename Error>
+HRESULT HRESULTFromUpdaterError(Error error) {
+  constexpr ULONG kCustomerBit = 0x20000000;
+  constexpr ULONG kFacilityOmaha = 67;
+  return static_cast<HRESULT>(static_cast<ULONG>(SEVERITY_ERROR) |
+                              kCustomerBit | (kFacilityOmaha << 16) |
+                              static_cast<ULONG>(error));
+}
+
+// Checks whether a process is running with the image |executable|. Returns true
+// if a process is found.
+bool IsProcessRunning(const wchar_t* executable);
+
+// Waits until every running instance of |executable| is stopped.
+// Returns true if every running processes are stopped.
+bool WaitForProcessesStopped(const wchar_t* executable);
+
+bool InitializeCOMSecurity();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_WIN_UTIL_H_
diff --git a/src/cobalt/updater/win/util_unittest.cc b/src/cobalt/updater/win/util_unittest.cc
new file mode 100644
index 0000000..e62b1b4
--- /dev/null
+++ b/src/cobalt/updater/win/util_unittest.cc
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/win/util.h"
+
+#include <windows.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+
+TEST(UpdaterTestUtil, HRESULTFromLastError) {
+  ::SetLastError(ERROR_ACCESS_DENIED);
+  EXPECT_EQ(E_ACCESSDENIED, HRESULTFromLastError());
+  ::SetLastError(ERROR_SUCCESS);
+  EXPECT_EQ(E_FAIL, HRESULTFromLastError());
+}
+
+}  // namespace updater
diff --git a/src/starboard/android/shared/gyp_configuration.py b/src/starboard/android/shared/gyp_configuration.py
index 8fc3c9b..cde48b1 100644
--- a/src/starboard/android/shared/gyp_configuration.py
+++ b/src/starboard/android/shared/gyp_configuration.py
@@ -209,8 +209,10 @@
           # Use the static LLVM libc++.
           '-static-libstdc++',
 
-          # Mimic build/cmake/android.toolchain.cmake in the Android NDK.
-          '-Wl,--build-id',
+          # Mimic build/cmake/android.toolchain.cmake in the Android NDK, but
+          # force build-id to sha1, so that older lldb versions can still find
+          # debugsymbols, see https://github.com/android-ndk/ndk/issues/885
+          '-Wl,--build-id=sha1',
           '-Wl,--warn-shared-textrel',
           '-Wl,--fatal-warnings',
           '-Wl,--gc-sections',
diff --git a/src/starboard/elf_loader/exported_symbols.cc b/src/starboard/elf_loader/exported_symbols.cc
index ea440bf..f0d9912 100644
--- a/src/starboard/elf_loader/exported_symbols.cc
+++ b/src/starboard/elf_loader/exported_symbols.cc
@@ -276,7 +276,8 @@
 
   map_["SbSocketAccept"] = reinterpret_cast<const void*>(SbSocketAccept);
   map_["SbSocketBind"] = reinterpret_cast<const void*>(SbSocketBind);
-  map_["SbSocketClearLastError"] = reinterpret_cast<const void*>(SbSocketClearLastError);
+  map_["SbSocketClearLastError"] =
+      reinterpret_cast<const void*>(SbSocketClearLastError);
   map_["SbSocketConnect"] = reinterpret_cast<const void*>(SbSocketConnect);
   map_["SbSocketCreate"] = reinterpret_cast<const void*>(SbSocketCreate);
   map_["SbSocketDestroy"] = reinterpret_cast<const void*>(SbSocketDestroy);
diff --git a/src/starboard/examples/glclear/glclear.gyp b/src/starboard/examples/glclear/glclear.gyp
index 5d68f70..db49702 100644
--- a/src/starboard/examples/glclear/glclear.gyp
+++ b/src/starboard/examples/glclear/glclear.gyp
@@ -17,12 +17,27 @@
     {
       'target_name': 'starboard_glclear_example',
       'type': '<(final_executable_type)',
+      'conditions': [
+        ['sb_evergreen == 1', {
+          'dependencies': [
+            '<(DEPTH)/starboard/client_porting/eztime/eztime.gyp:eztime',
+            '<(DEPTH)/third_party/llvm-project/compiler-rt/compiler-rt.gyp:compiler_rt',
+            '<(DEPTH)/third_party/llvm-project/libcxx/libcxx.gyp:cxx',
+            '<(DEPTH)/third_party/llvm-project/libcxxabi/libcxxabi.gyp:cxxabi',
+            '<(DEPTH)/third_party/llvm-project/libunwind/libunwind.gyp:unwind',
+            '<(DEPTH)/third_party/musl/musl.gyp:c',
+          ],
+        }, {
+          'dependencies': [
+            '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
+          ],
+        }],
+      ],
       'sources': [
         'main.cc',
       ],
       'dependencies': [
         '<(DEPTH)/starboard/starboard.gyp:starboard',
-        '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
       ],
     },
     {
diff --git a/src/starboard/raspi/2/architecture.gypi b/src/starboard/raspi/2/architecture.gypi
index ce547ff..586370c 100644
--- a/src/starboard/raspi/2/architecture.gypi
+++ b/src/starboard/raspi/2/architecture.gypi
@@ -17,15 +17,17 @@
     # RasPi 2 is ARMv7
     'arm_version': 7,
     'armv7': 1,
+    'arm_fpu': 'neon-vfpv4',
     'arm_neon': 1,
     'arm_float_abi': 'hard',
 
     'compiler_flags': [
       # Optimize for Raspberry Pi 2 chips.
       '-march=armv7-a',
-      '-mtune=cortex-a8',
       '-mfloat-abi=hard',
       '-mfpu=neon-vfpv4',
+      '-mcpu=cortex-a8',
+      '-mtune=cortex-a8',
     ],
   },
 }
diff --git a/src/starboard/shared/ffmpeg/ffmpeg.gyp b/src/starboard/shared/ffmpeg/ffmpeg.gyp
index a14ac7e..05416b2 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg.gyp
+++ b/src/starboard/shared/ffmpeg/ffmpeg.gyp
@@ -59,6 +59,14 @@
       ],
     },
     {
+      'target_name': 'ffmpeg.58.35.100',
+      'type': '<(library)',
+      'sources': [ '<@(ffmpeg_specialization_sources)' ],
+      'dependencies': [
+        '<(DEPTH)/third_party/ffmpeg_includes/ffmpeg_includes.gyp:ffmpeg.58.35.100',
+      ],
+    },
+    {
       'target_name': 'ffmpeg_dynamic_load',
       'type': '<(library)',
       'sources': [
@@ -68,6 +76,7 @@
         '<@(ffmpeg_dispatch_sources)',
       ],
       'dependencies': [
+        'ffmpeg.58.35.100',
         'ffmpeg.57.107.100',
         'libav.54.35.1',
         'libav.56.1.0',
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
index eb428da..74e4a8e 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
@@ -50,8 +50,12 @@
       audio_decoder =
           AudioDecoderImpl<571>::Create(audio_codec, audio_sample_info);
       break;
+    case 581:
+      audio_decoder =
+          AudioDecoderImpl<581>::Create(audio_codec, audio_sample_info);
+      break;
     default:
-      SB_LOG(WARNING) << "Unsupported FFMPEG specialization " << std::hex
+      SB_LOG(WARNING) << "Unsupported FFMPEG specialization "
                       << ffmpeg->specialization_version();
       break;
   }
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
index 8bb57e1..1506cd5 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
@@ -17,15 +17,15 @@
 
 #include "starboard/client_porting/poem/string_leaks_poem.h"
 
-#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
-
 #include <dlfcn.h>
+
 #include <map>
 
 #include "starboard/common/log.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/common/string.h"
 #include "starboard/once.h"
+#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
 #include "starboard/shared/starboard/lazy_initialization_internal.h"
 
 namespace starboard {
@@ -163,33 +163,29 @@
   for (auto version_iterator = versions_.rbegin();
        version_iterator != versions_.rend(); ++version_iterator) {
     LibraryMajorVersions& versions = version_iterator->second;
-    avutil_ = dlopen(
-        GetVersionedLibraryName(kAVUtilLibraryName, versions.avutil).c_str(),
-        RTLD_NOW | RTLD_GLOBAL);
+    std::string library_file =
+        GetVersionedLibraryName(kAVUtilLibraryName, versions.avutil);
+    avutil_ = dlopen(library_file.c_str(), RTLD_NOW | RTLD_GLOBAL);
     if (!avutil_) {
-      SB_DLOG(WARNING) << "Unable to open shared library "
-                       << kAVUtilLibraryName;
+      SB_DLOG(WARNING) << "Unable to open shared library " << library_file;
       continue;
     }
 
-    avcodec_ = dlopen(
-        GetVersionedLibraryName(kAVCodecLibraryName, versions.avcodec).c_str(),
-        RTLD_NOW | RTLD_GLOBAL);
+    library_file =
+        GetVersionedLibraryName(kAVCodecLibraryName, versions.avcodec);
+    avcodec_ = dlopen(library_file.c_str(), RTLD_NOW | RTLD_GLOBAL);
     if (!avcodec_) {
-      SB_DLOG(WARNING) << "Unable to open shared library "
-                       << kAVCodecLibraryName;
+      SB_DLOG(WARNING) << "Unable to open shared library " << library_file;
       dlclose(avutil_);
       avutil_ = NULL;
       continue;
     }
 
-    avformat_ =
-        dlopen(GetVersionedLibraryName(kAVFormatLibraryName, versions.avformat)
-                   .c_str(),
-               RTLD_NOW | RTLD_GLOBAL);
+    library_file =
+        GetVersionedLibraryName(kAVFormatLibraryName, versions.avformat);
+    avformat_ = dlopen(library_file.c_str(), RTLD_NOW | RTLD_GLOBAL);
     if (!avformat_) {
-      SB_DLOG(WARNING) << "Unable to open shared library "
-                       << kAVFormatLibraryName;
+      SB_DLOG(WARNING) << "Unable to open shared library " << library_file;
       dlclose(avcodec_);
       avcodec_ = NULL;
       dlclose(avutil_);
@@ -199,6 +195,36 @@
     SB_DCHECK(is_valid());
     break;
   }
+  if (!is_valid()) {
+    // Attempt to load the libraries without a version number.
+    // This allows loading of the libraries on machines where a versioned and
+    // supported library is not available. Additionally, if this results in a
+    // library version being loaded that is not supported, then the decoder
+    // instantiation can output a more informative log message.
+    avutil_ = dlopen(kAVUtilLibraryName, RTLD_NOW | RTLD_GLOBAL);
+    if (!avutil_) {
+      SB_DLOG(WARNING) << "Unable to open shared library "
+                       << kAVUtilLibraryName;
+    }
+
+    avcodec_ = dlopen(kAVCodecLibraryName, RTLD_NOW | RTLD_GLOBAL);
+    if (!avcodec_) {
+      SB_DLOG(WARNING) << "Unable to open shared library "
+                       << kAVCodecLibraryName;
+      dlclose(avutil_);
+      avutil_ = NULL;
+    }
+
+    avformat_ = dlopen(kAVFormatLibraryName, RTLD_NOW | RTLD_GLOBAL);
+    if (!avformat_) {
+      SB_DLOG(WARNING) << "Unable to open shared library "
+                       << kAVFormatLibraryName;
+      dlclose(avcodec_);
+      avcodec_ = NULL;
+      dlclose(avutil_);
+      avutil_ = NULL;
+    }
+  }
   return is_valid();
 }
 
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc
index 306786f..558d346 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc
@@ -52,9 +52,13 @@
       video_decoder = VideoDecoderImpl<571>::Create(
           video_codec, output_mode, decode_target_graphics_context_provider);
       break;
+    case 581:
+      video_decoder = VideoDecoderImpl<581>::Create(
+          video_codec, output_mode, decode_target_graphics_context_provider);
+      break;
     default:
-      SB_LOG(WARNING) << "Unsupported FFMPEG version " << std::hex
-                      << ffmpeg->avutil_version();
+      SB_LOG(WARNING) << "Unsupported FFMPEG version "
+                      << ffmpeg->specialization_version();
       break;
   }
   return video_decoder;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
index 3e9e042..26724c2 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
@@ -340,7 +340,9 @@
   codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
   codec_context_->thread_count = 2;
   codec_context_->opaque = this;
+#if defined(CODEC_FLAG_EMU_EDGE)
   codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
+#endif
 #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
   codec_context_->get_buffer2 = AllocateBufferCallback;
 #else   // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0)
diff --git a/src/starboard/starboard_all.gyp b/src/starboard/starboard_all.gyp
index 083083d..2dcf397 100644
--- a/src/starboard/starboard_all.gyp
+++ b/src/starboard/starboard_all.gyp
@@ -80,6 +80,11 @@
             'starboard_platform_tests',
           ],
         }],
+        ['sb_evergreen == 1', {
+          'dependencies': [
+            '<(DEPTH)/starboard/examples/glclear/glclear.gyp:starboard_glclear_example',
+          ],
+        }],
         ['sb_filter_based_player==1', {
           'dependencies': [
             '<(DEPTH)/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp:*',
diff --git a/src/starboard/testing/fake_graphics_context_provider.cc b/src/starboard/testing/fake_graphics_context_provider.cc
index 843a12f..bc229d8 100644
--- a/src/starboard/testing/fake_graphics_context_provider.cc
+++ b/src/starboard/testing/fake_graphics_context_provider.cc
@@ -30,25 +30,43 @@
 #include "starboard/configuration.h"
 #include "starboard/memory.h"
 
+#define EGL_CALL_PREFIX SbGetEglInterface()->
+#define GL_CALL_PREFIX SbGetGlInterface()->
+
+#define EGL_CALL(x)                                             \
+  do {                                                          \
+    EGL_CALL_PREFIX x;                                          \
+    SB_DCHECK(EGL_CALL_PREFIX eglGetError() == SB_EGL_SUCCESS); \
+  } while (false)
+
+#define GL_CALL(x)                                            \
+  do {                                                        \
+    GL_CALL_PREFIX x;                                         \
+    SB_DCHECK(GL_CALL_PREFIX glGetError() == SB_GL_NO_ERROR); \
+  } while (false)
+
+#define EGL_CALL_SIMPLE(x) (EGL_CALL_PREFIX x)
+#define GL_CALL_SIMPLE(x) (GL_CALL_PREFIX x)
+
 namespace starboard {
 namespace testing {
 
 namespace {
 
 #if SB_HAS(GLES2)
-EGLint const kAttributeList[] = {EGL_RED_SIZE,
-                                 8,
-                                 EGL_GREEN_SIZE,
-                                 8,
-                                 EGL_BLUE_SIZE,
-                                 8,
-                                 EGL_ALPHA_SIZE,
-                                 8,
-                                 EGL_SURFACE_TYPE,
-                                 EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
-                                 EGL_RENDERABLE_TYPE,
-                                 EGL_OPENGL_ES2_BIT,
-                                 EGL_NONE};
+SbEglInt32 const kAttributeList[] = {SB_EGL_RED_SIZE,
+                                     8,
+                                     SB_EGL_GREEN_SIZE,
+                                     8,
+                                     SB_EGL_BLUE_SIZE,
+                                     8,
+                                     SB_EGL_ALPHA_SIZE,
+                                     8,
+                                     SB_EGL_SURFACE_TYPE,
+                                     SB_EGL_WINDOW_BIT | SB_EGL_PBUFFER_BIT,
+                                     SB_EGL_RENDERABLE_TYPE,
+                                     SB_EGL_OPENGL_ES2_BIT,
+                                     SB_EGL_NONE};
 #endif  // SB_HAS(GLES2)
 
 }  // namespace
@@ -56,9 +74,9 @@
 FakeGraphicsContextProvider::FakeGraphicsContextProvider()
     :
 #if SB_HAS(GLES2)
-      display_(EGL_NO_DISPLAY),
-      surface_(EGL_NO_SURFACE),
-      context_(EGL_NO_CONTEXT),
+      display_(SB_EGL_NO_DISPLAY),
+      surface_(SB_EGL_NO_SURFACE),
+      context_(SB_EGL_NO_CONTEXT),
 #endif  // SB_HAS(GLES2)
       window_(kSbWindowInvalid) {
   InitializeWindow();
@@ -75,10 +93,8 @@
       std::bind(&FakeGraphicsContextProvider::DestroyContext, this));
   functor_queue_.Wake();
   SbThreadJoin(decode_target_context_thread_, NULL);
-  eglDestroySurface(display_, surface_);
-  SB_CHECK(EGL_SUCCESS == eglGetError());
-  eglTerminate(display_);
-  SB_CHECK(EGL_SUCCESS == eglGetError());
+  EGL_CALL(eglDestroySurface(display_, surface_));
+  EGL_CALL(eglTerminate(display_));
 #endif  // SB_HAS(GLES2)
   SbWindowDestroy(window_);
 }
@@ -126,18 +142,18 @@
 
 #if SB_HAS(GLES2)
 void FakeGraphicsContextProvider::InitializeEGL() {
-  display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-  SB_CHECK(EGL_SUCCESS == eglGetError());
-  SB_CHECK(EGL_NO_DISPLAY != display_);
+  display_ = EGL_CALL_SIMPLE(eglGetDisplay(SB_EGL_DEFAULT_DISPLAY));
+  SB_DCHECK(SB_EGL_SUCCESS == EGL_CALL_SIMPLE(eglGetError()));
+  SB_CHECK(SB_EGL_NO_DISPLAY != display_);
 
 #if HAS_LEAK_SANITIZER
   __lsan_disable();
 #endif  // HAS_LEAK_SANITIZER
-  eglInitialize(display_, NULL, NULL);
+  EGL_CALL_SIMPLE(eglInitialize(display_, NULL, NULL));
 #if HAS_LEAK_SANITIZER
   __lsan_enable();
 #endif  // HAS_LEAK_SANITIZER
-  SB_CHECK(EGL_SUCCESS == eglGetError());
+  SB_DCHECK(SB_EGL_SUCCESS == EGL_CALL_SIMPLE(eglGetError()));
 
   // Some EGL drivers can return a first config that doesn't allow
   // eglCreateWindowSurface(), with no differences in EGLConfig attribute values
@@ -145,50 +161,50 @@
   // eglCreateWindowSurface() until we find a config that succeeds.
 
   // First, query how many configs match the given attribute list.
-  EGLint num_configs = 0;
-  eglChooseConfig(display_, kAttributeList, NULL, 0, &num_configs);
-  SB_CHECK(EGL_SUCCESS == eglGetError());
+  SbEglInt32 num_configs = 0;
+  EGL_CALL(eglChooseConfig(display_, kAttributeList, NULL, 0, &num_configs));
   SB_CHECK(0 != num_configs);
 
   // Allocate space to receive the matching configs and retrieve them.
-  EGLConfig* configs = reinterpret_cast<EGLConfig*>(
-      SbMemoryAllocate(num_configs * sizeof(EGLConfig)));
-  eglChooseConfig(display_, kAttributeList, configs, num_configs, &num_configs);
-  SB_CHECK(EGL_SUCCESS == eglGetError());
+  SbEglConfig* configs = reinterpret_cast<SbEglConfig*>(
+      SbMemoryAllocate(num_configs * sizeof(SbEglConfig)));
+  EGL_CALL(eglChooseConfig(display_, kAttributeList, configs, num_configs,
+                           &num_configs));
 
-  EGLNativeWindowType native_window =
-      (EGLNativeWindowType)SbWindowGetPlatformHandle(window_);
-  EGLConfig config = EGLConfig();
+  SbEglNativeWindowType native_window =
+      (SbEglNativeWindowType)SbWindowGetPlatformHandle(window_);
+  SbEglConfig config = SbEglConfig();
 
   // Find the first config that successfully allow a window surface to be
   // created.
   for (int config_number = 0; config_number < num_configs; ++config_number) {
     config = configs[config_number];
-    surface_ = eglCreateWindowSurface(display_, config, native_window, NULL);
-    if (EGL_SUCCESS == eglGetError())
+    surface_ = EGL_CALL_SIMPLE(
+        eglCreateWindowSurface(display_, config, native_window, NULL));
+    if (SB_EGL_SUCCESS == EGL_CALL_SIMPLE(eglGetError()))
       break;
   }
-  SB_DCHECK(surface_ != EGL_NO_SURFACE);
+  SB_DCHECK(surface_ != SB_EGL_NO_SURFACE);
 
   SbMemoryDeallocate(configs);
 
   // Create the GLES2 or GLEX3 Context.
-  EGLint context_attrib_list[] = {
-      EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE,
+  SbEglInt32 context_attrib_list[] = {
+      SB_EGL_CONTEXT_CLIENT_VERSION, 3, SB_EGL_NONE,
   };
 #if defined(GLES3_SUPPORTED)
   // Attempt to create an OpenGL ES 3.0 context.
-  context_ =
-      eglCreateContext(display_, config, EGL_NO_CONTEXT, context_attrib_list);
+  context_ = EGL_CALL_SIMPLE(eglCreateContext(
+      display_, config, SB_EGL_NO_CONTEXT, context_attrib_list));
 #endif
-  if (context_ == EGL_NO_CONTEXT) {
+  if (context_ == SB_EGL_NO_CONTEXT) {
     // Create an OpenGL ES 2.0 context.
     context_attrib_list[1] = 2;
-    context_ =
-        eglCreateContext(display_, config, EGL_NO_CONTEXT, context_attrib_list);
+    context_ = EGL_CALL_SIMPLE(eglCreateContext(
+        display_, config, SB_EGL_NO_CONTEXT, context_attrib_list));
   }
-  SB_CHECK(EGL_SUCCESS == eglGetError());
-  SB_CHECK(context_ != EGL_NO_CONTEXT);
+  SB_CHECK(SB_EGL_SUCCESS == EGL_CALL_SIMPLE(eglGetError()));
+  SB_CHECK(context_ != SB_EGL_NO_CONTEXT);
 
   MakeContextCurrent();
 
@@ -240,22 +256,22 @@
 }
 
 void FakeGraphicsContextProvider::MakeContextCurrent() {
-  SB_CHECK(EGL_NO_DISPLAY != display_);
-  eglMakeCurrent(display_, surface_, surface_, context_);
-  EGLint error = eglGetError();
-  SB_CHECK(EGL_SUCCESS == error) << " eglGetError " << error;
+  SB_CHECK(SB_EGL_NO_DISPLAY != display_);
+  EGL_CALL_SIMPLE(eglMakeCurrent(display_, surface_, surface_, context_));
+  SbEglInt32 error = EGL_CALL_SIMPLE(eglGetError());
+  SB_CHECK(SB_EGL_SUCCESS == error) << " eglGetError " << error;
 }
 
 void FakeGraphicsContextProvider::MakeNoContextCurrent() {
-  eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-  SB_CHECK(EGL_SUCCESS == eglGetError());
+  EGL_CALL(eglMakeCurrent(display_, SB_EGL_NO_SURFACE, SB_EGL_NO_SURFACE,
+                          SB_EGL_NO_CONTEXT));
 }
 
 void FakeGraphicsContextProvider::DestroyContext() {
   MakeNoContextCurrent();
-  eglDestroyContext(display_, context_);
-  EGLint error = eglGetError();
-  SB_CHECK(EGL_SUCCESS == error) << " eglGetError " << error;
+  EGL_CALL_SIMPLE(eglDestroyContext(display_, context_));
+  SbEglInt32 error = EGL_CALL_SIMPLE(eglGetError());
+  SB_CHECK(SB_EGL_SUCCESS == error) << " eglGetError " << error;
 }
 
 // static
diff --git a/src/starboard/testing/fake_graphics_context_provider.h b/src/starboard/testing/fake_graphics_context_provider.h
index df193a1..c68f28f 100644
--- a/src/starboard/testing/fake_graphics_context_provider.h
+++ b/src/starboard/testing/fake_graphics_context_provider.h
@@ -22,15 +22,11 @@
 #include "starboard/common/queue.h"
 #include "starboard/configuration.h"
 #include "starboard/decode_target.h"
+#include "starboard/egl.h"
+#include "starboard/gles.h"
 #include "starboard/thread.h"
 #include "starboard/window.h"
 
-// SB_HAS() is available after starboard/configuration.h is included.
-#if SB_HAS(GLES2)
-#include <EGL/egl.h>
-#include <GLES2/gl2.h>
-#endif  // SB_HAS(GLES2)
-
 namespace starboard {
 namespace testing {
 
@@ -89,9 +85,9 @@
       SbDecodeTargetGlesContextRunnerTarget target_function,
       void* target_function_context);
 
-  EGLDisplay display_;
-  EGLSurface surface_;
-  EGLContext context_;
+  SbEglDisplay display_;
+  SbEglSurface surface_;
+  SbEglContext context_;
   Queue<std::function<void()>> functor_queue_;
   SbThread decode_target_context_thread_;
 #endif  // SB_HAS(GLES2)
diff --git a/src/starboard/tools/toolchain/evergreen_linker.py b/src/starboard/tools/toolchain/evergreen_linker.py
new file mode 100644
index 0000000..7cd205f
--- /dev/null
+++ b/src/starboard/tools/toolchain/evergreen_linker.py
@@ -0,0 +1,43 @@
+# 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.
+
+from starboard.tools.toolchain import abstract
+from starboard.tools.toolchain import clangxx
+
+
+class SharedLibraryLinker(clangxx.DynamicLinkerBase,
+                          abstract.SharedLibraryLinker):
+  """Links shared libraries using the LLVM Project's lld."""
+
+  def __init__(self, **kwargs):  # pylint: disable=useless-super-delegation
+    super(SharedLibraryLinker, self).__init__(**kwargs)
+
+  def GetCommand(self, path, extra_flags, flags, shell):
+    lld_path = '{0}/bin/ld.lld'.format(self.GetPath())
+
+    return shell.And('{0} '
+                     '-X '
+                     '-v '
+                     '--eh-frame-hdr '
+                     '-m armelf_linux_eabi '
+                     '-shared '
+                     '-o $out '
+                     '-L{1} '
+                     '-L/usr/lib '
+                     '-L/lib '
+                     '-soname=$soname '
+                     '-nostdlib '
+                     '--whole-archive '
+                     '--no-whole-archive '
+                     '@$rspfile'.format(lld_path, self.GetPath()))
diff --git a/src/third_party/brotli/brotli.gyp b/src/third_party/brotli/brotli.gyp
index 63f9ba9..831b5ff 100644
--- a/src/third_party/brotli/brotli.gyp
+++ b/src/third_party/brotli/brotli.gyp
@@ -3,6 +3,9 @@
 # found in the LICENSE file.
 
 {
+  'variables': {
+    'optimize_target_for_speed': 1,
+  },
   'targets': [
     {
       'target_name': 'headers',
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/COPYING.LGPLv2.1 b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/COPYING.LGPLv2.1
new file mode 100644
index 0000000..00b4fed
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/COPYING.LGPLv2.1
@@ -0,0 +1,504 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/ac3_parser.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/ac3_parser.h
new file mode 100644
index 0000000..ff8cc4c
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/ac3_parser.h
@@ -0,0 +1,36 @@
+/*
+ * AC-3 parser prototypes
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2003 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_AC3_PARSER_H
+#define AVCODEC_AC3_PARSER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * Extract the bitstream ID and the frame size from AC-3 data.
+ */
+int av_ac3_parse_header(const uint8_t *buf, size_t size,
+                        uint8_t *bitstream_id, uint16_t *frame_size);
+
+
+#endif /* AVCODEC_AC3_PARSER_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/adts_parser.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/adts_parser.h
new file mode 100644
index 0000000..f85becd
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/adts_parser.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_ADTS_PARSER_H
+#define AVCODEC_ADTS_PARSER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define AV_AAC_ADTS_HEADER_SIZE 7
+
+/**
+ * Extract the number of samples and frames from AAC data.
+ * @param[in]  buf     pointer to AAC data buffer
+ * @param[out] samples Pointer to where number of samples is written
+ * @param[out] frames  Pointer to where number of frames is written
+ * @return Returns 0 on success, error code on failure.
+ */
+int av_adts_header_parse(const uint8_t *buf, uint32_t *samples,
+                         uint8_t *frames);
+
+#endif /* AVCODEC_ADTS_PARSER_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avcodec.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avcodec.h
new file mode 100644
index 0000000..bee2234
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avcodec.h
@@ -0,0 +1,6168 @@
+/*
+ * copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_AVCODEC_H
+#define AVCODEC_AVCODEC_H
+
+/**
+ * @file
+ * @ingroup libavc
+ * Libavcodec external API header
+ */
+
+#include <errno.h>
+#include "libavutil/samplefmt.h"
+#include "libavutil/attributes.h"
+#include "libavutil/avutil.h"
+#include "libavutil/buffer.h"
+#include "libavutil/cpu.h"
+#include "libavutil/channel_layout.h"
+#include "libavutil/dict.h"
+#include "libavutil/frame.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/log.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/rational.h"
+
+#include "version.h"
+
+/**
+ * @defgroup libavc libavcodec
+ * Encoding/Decoding Library
+ *
+ * @{
+ *
+ * @defgroup lavc_decoding Decoding
+ * @{
+ * @}
+ *
+ * @defgroup lavc_encoding Encoding
+ * @{
+ * @}
+ *
+ * @defgroup lavc_codec Codecs
+ * @{
+ * @defgroup lavc_codec_native Native Codecs
+ * @{
+ * @}
+ * @defgroup lavc_codec_wrappers External library wrappers
+ * @{
+ * @}
+ * @defgroup lavc_codec_hwaccel Hardware Accelerators bridge
+ * @{
+ * @}
+ * @}
+ * @defgroup lavc_internal Internal
+ * @{
+ * @}
+ * @}
+ */
+
+/**
+ * @ingroup libavc
+ * @defgroup lavc_encdec send/receive encoding and decoding API overview
+ * @{
+ *
+ * The avcodec_send_packet()/avcodec_receive_frame()/avcodec_send_frame()/
+ * avcodec_receive_packet() functions provide an encode/decode API, which
+ * decouples input and output.
+ *
+ * The API is very similar for encoding/decoding and audio/video, and works as
+ * follows:
+ * - Set up and open the AVCodecContext as usual.
+ * - Send valid input:
+ *   - For decoding, call avcodec_send_packet() to give the decoder raw
+ *     compressed data in an AVPacket.
+ *   - For encoding, call avcodec_send_frame() to give the encoder an AVFrame
+ *     containing uncompressed audio or video.
+ *   In both cases, it is recommended that AVPackets and AVFrames are
+ *   refcounted, or libavcodec might have to copy the input data. (libavformat
+ *   always returns refcounted AVPackets, and av_frame_get_buffer() allocates
+ *   refcounted AVFrames.)
+ * - Receive output in a loop. Periodically call one of the avcodec_receive_*()
+ *   functions and process their output:
+ *   - For decoding, call avcodec_receive_frame(). On success, it will return
+ *     an AVFrame containing uncompressed audio or video data.
+ *   - For encoding, call avcodec_receive_packet(). On success, it will return
+ *     an AVPacket with a compressed frame.
+ *   Repeat this call until it returns AVERROR(EAGAIN) or an error. The
+ *   AVERROR(EAGAIN) return value means that new input data is required to
+ *   return new output. In this case, continue with sending input. For each
+ *   input frame/packet, the codec will typically return 1 output frame/packet,
+ *   but it can also be 0 or more than 1.
+ *
+ * At the beginning of decoding or encoding, the codec might accept multiple
+ * input frames/packets without returning a frame, until its internal buffers
+ * are filled. This situation is handled transparently if you follow the steps
+ * outlined above.
+ *
+ * In theory, sending input can result in EAGAIN - this should happen only if
+ * not all output was received. You can use this to structure alternative decode
+ * or encode loops other than the one suggested above. For example, you could
+ * try sending new input on each iteration, and try to receive output if that
+ * returns EAGAIN.
+ *
+ * End of stream situations. These require "flushing" (aka draining) the codec,
+ * as the codec might buffer multiple frames or packets internally for
+ * performance or out of necessity (consider B-frames).
+ * This is handled as follows:
+ * - Instead of valid input, send NULL to the avcodec_send_packet() (decoding)
+ *   or avcodec_send_frame() (encoding) functions. This will enter draining
+ *   mode.
+ * - Call avcodec_receive_frame() (decoding) or avcodec_receive_packet()
+ *   (encoding) in a loop until AVERROR_EOF is returned. The functions will
+ *   not return AVERROR(EAGAIN), unless you forgot to enter draining mode.
+ * - Before decoding can be resumed again, the codec has to be reset with
+ *   avcodec_flush_buffers().
+ *
+ * Using the API as outlined above is highly recommended. But it is also
+ * possible to call functions outside of this rigid schema. For example, you can
+ * call avcodec_send_packet() repeatedly without calling
+ * avcodec_receive_frame(). In this case, avcodec_send_packet() will succeed
+ * until the codec's internal buffer has been filled up (which is typically of
+ * size 1 per output frame, after initial input), and then reject input with
+ * AVERROR(EAGAIN). Once it starts rejecting input, you have no choice but to
+ * read at least some output.
+ *
+ * Not all codecs will follow a rigid and predictable dataflow; the only
+ * guarantee is that an AVERROR(EAGAIN) return value on a send/receive call on
+ * one end implies that a receive/send call on the other end will succeed, or
+ * at least will not fail with AVERROR(EAGAIN). In general, no codec will
+ * permit unlimited buffering of input or output.
+ *
+ * This API replaces the following legacy functions:
+ * - avcodec_decode_video2() and avcodec_decode_audio4():
+ *   Use avcodec_send_packet() to feed input to the decoder, then use
+ *   avcodec_receive_frame() to receive decoded frames after each packet.
+ *   Unlike with the old video decoding API, multiple frames might result from
+ *   a packet. For audio, splitting the input packet into frames by partially
+ *   decoding packets becomes transparent to the API user. You never need to
+ *   feed an AVPacket to the API twice (unless it is rejected with AVERROR(EAGAIN) - then
+ *   no data was read from the packet).
+ *   Additionally, sending a flush/draining packet is required only once.
+ * - avcodec_encode_video2()/avcodec_encode_audio2():
+ *   Use avcodec_send_frame() to feed input to the encoder, then use
+ *   avcodec_receive_packet() to receive encoded packets.
+ *   Providing user-allocated buffers for avcodec_receive_packet() is not
+ *   possible.
+ * - The new API does not handle subtitles yet.
+ *
+ * Mixing new and old function calls on the same AVCodecContext is not allowed,
+ * and will result in undefined behavior.
+ *
+ * Some codecs might require using the new API; using the old API will return
+ * an error when calling it. All codecs support the new API.
+ *
+ * A codec is not allowed to return AVERROR(EAGAIN) for both sending and receiving. This
+ * would be an invalid state, which could put the codec user into an endless
+ * loop. The API has no concept of time either: it cannot happen that trying to
+ * do avcodec_send_packet() results in AVERROR(EAGAIN), but a repeated call 1 second
+ * later accepts the packet (with no other receive/flush API calls involved).
+ * The API is a strict state machine, and the passage of time is not supposed
+ * to influence it. Some timing-dependent behavior might still be deemed
+ * acceptable in certain cases. But it must never result in both send/receive
+ * returning EAGAIN at the same time at any point. It must also absolutely be
+ * avoided that the current state is "unstable" and can "flip-flop" between
+ * the send/receive APIs allowing progress. For example, it's not allowed that
+ * the codec randomly decides that it actually wants to consume a packet now
+ * instead of returning a frame, after it just returned AVERROR(EAGAIN) on an
+ * avcodec_send_packet() call.
+ * @}
+ */
+
+/**
+ * @defgroup lavc_core Core functions/structures.
+ * @ingroup libavc
+ *
+ * Basic definitions, functions for querying libavcodec capabilities,
+ * allocating core structures, etc.
+ * @{
+ */
+
+
+/**
+ * Identify the syntax and semantics of the bitstream.
+ * The principle is roughly:
+ * Two decoders with the same ID can decode the same streams.
+ * Two encoders with the same ID can encode compatible streams.
+ * There may be slight deviations from the principle due to implementation
+ * details.
+ *
+ * If you add a codec ID to this list, add it so that
+ * 1. no value of an existing codec ID changes (that would break ABI),
+ * 2. it is as close as possible to similar codecs
+ *
+ * After adding new codec IDs, do not forget to add an entry to the codec
+ * descriptor list and bump libavcodec minor version.
+ */
+enum AVCodecID {
+    AV_CODEC_ID_NONE,
+
+    /* video codecs */
+    AV_CODEC_ID_MPEG1VIDEO,
+    AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding
+    AV_CODEC_ID_H261,
+    AV_CODEC_ID_H263,
+    AV_CODEC_ID_RV10,
+    AV_CODEC_ID_RV20,
+    AV_CODEC_ID_MJPEG,
+    AV_CODEC_ID_MJPEGB,
+    AV_CODEC_ID_LJPEG,
+    AV_CODEC_ID_SP5X,
+    AV_CODEC_ID_JPEGLS,
+    AV_CODEC_ID_MPEG4,
+    AV_CODEC_ID_RAWVIDEO,
+    AV_CODEC_ID_MSMPEG4V1,
+    AV_CODEC_ID_MSMPEG4V2,
+    AV_CODEC_ID_MSMPEG4V3,
+    AV_CODEC_ID_WMV1,
+    AV_CODEC_ID_WMV2,
+    AV_CODEC_ID_H263P,
+    AV_CODEC_ID_H263I,
+    AV_CODEC_ID_FLV1,
+    AV_CODEC_ID_SVQ1,
+    AV_CODEC_ID_SVQ3,
+    AV_CODEC_ID_DVVIDEO,
+    AV_CODEC_ID_HUFFYUV,
+    AV_CODEC_ID_CYUV,
+    AV_CODEC_ID_H264,
+    AV_CODEC_ID_INDEO3,
+    AV_CODEC_ID_VP3,
+    AV_CODEC_ID_THEORA,
+    AV_CODEC_ID_ASV1,
+    AV_CODEC_ID_ASV2,
+    AV_CODEC_ID_FFV1,
+    AV_CODEC_ID_4XM,
+    AV_CODEC_ID_VCR1,
+    AV_CODEC_ID_CLJR,
+    AV_CODEC_ID_MDEC,
+    AV_CODEC_ID_ROQ,
+    AV_CODEC_ID_INTERPLAY_VIDEO,
+    AV_CODEC_ID_XAN_WC3,
+    AV_CODEC_ID_XAN_WC4,
+    AV_CODEC_ID_RPZA,
+    AV_CODEC_ID_CINEPAK,
+    AV_CODEC_ID_WS_VQA,
+    AV_CODEC_ID_MSRLE,
+    AV_CODEC_ID_MSVIDEO1,
+    AV_CODEC_ID_IDCIN,
+    AV_CODEC_ID_8BPS,
+    AV_CODEC_ID_SMC,
+    AV_CODEC_ID_FLIC,
+    AV_CODEC_ID_TRUEMOTION1,
+    AV_CODEC_ID_VMDVIDEO,
+    AV_CODEC_ID_MSZH,
+    AV_CODEC_ID_ZLIB,
+    AV_CODEC_ID_QTRLE,
+    AV_CODEC_ID_TSCC,
+    AV_CODEC_ID_ULTI,
+    AV_CODEC_ID_QDRAW,
+    AV_CODEC_ID_VIXL,
+    AV_CODEC_ID_QPEG,
+    AV_CODEC_ID_PNG,
+    AV_CODEC_ID_PPM,
+    AV_CODEC_ID_PBM,
+    AV_CODEC_ID_PGM,
+    AV_CODEC_ID_PGMYUV,
+    AV_CODEC_ID_PAM,
+    AV_CODEC_ID_FFVHUFF,
+    AV_CODEC_ID_RV30,
+    AV_CODEC_ID_RV40,
+    AV_CODEC_ID_VC1,
+    AV_CODEC_ID_WMV3,
+    AV_CODEC_ID_LOCO,
+    AV_CODEC_ID_WNV1,
+    AV_CODEC_ID_AASC,
+    AV_CODEC_ID_INDEO2,
+    AV_CODEC_ID_FRAPS,
+    AV_CODEC_ID_TRUEMOTION2,
+    AV_CODEC_ID_BMP,
+    AV_CODEC_ID_CSCD,
+    AV_CODEC_ID_MMVIDEO,
+    AV_CODEC_ID_ZMBV,
+    AV_CODEC_ID_AVS,
+    AV_CODEC_ID_SMACKVIDEO,
+    AV_CODEC_ID_NUV,
+    AV_CODEC_ID_KMVC,
+    AV_CODEC_ID_FLASHSV,
+    AV_CODEC_ID_CAVS,
+    AV_CODEC_ID_JPEG2000,
+    AV_CODEC_ID_VMNC,
+    AV_CODEC_ID_VP5,
+    AV_CODEC_ID_VP6,
+    AV_CODEC_ID_VP6F,
+    AV_CODEC_ID_TARGA,
+    AV_CODEC_ID_DSICINVIDEO,
+    AV_CODEC_ID_TIERTEXSEQVIDEO,
+    AV_CODEC_ID_TIFF,
+    AV_CODEC_ID_GIF,
+    AV_CODEC_ID_DXA,
+    AV_CODEC_ID_DNXHD,
+    AV_CODEC_ID_THP,
+    AV_CODEC_ID_SGI,
+    AV_CODEC_ID_C93,
+    AV_CODEC_ID_BETHSOFTVID,
+    AV_CODEC_ID_PTX,
+    AV_CODEC_ID_TXD,
+    AV_CODEC_ID_VP6A,
+    AV_CODEC_ID_AMV,
+    AV_CODEC_ID_VB,
+    AV_CODEC_ID_PCX,
+    AV_CODEC_ID_SUNRAST,
+    AV_CODEC_ID_INDEO4,
+    AV_CODEC_ID_INDEO5,
+    AV_CODEC_ID_MIMIC,
+    AV_CODEC_ID_RL2,
+    AV_CODEC_ID_ESCAPE124,
+    AV_CODEC_ID_DIRAC,
+    AV_CODEC_ID_BFI,
+    AV_CODEC_ID_CMV,
+    AV_CODEC_ID_MOTIONPIXELS,
+    AV_CODEC_ID_TGV,
+    AV_CODEC_ID_TGQ,
+    AV_CODEC_ID_TQI,
+    AV_CODEC_ID_AURA,
+    AV_CODEC_ID_AURA2,
+    AV_CODEC_ID_V210X,
+    AV_CODEC_ID_TMV,
+    AV_CODEC_ID_V210,
+    AV_CODEC_ID_DPX,
+    AV_CODEC_ID_MAD,
+    AV_CODEC_ID_FRWU,
+    AV_CODEC_ID_FLASHSV2,
+    AV_CODEC_ID_CDGRAPHICS,
+    AV_CODEC_ID_R210,
+    AV_CODEC_ID_ANM,
+    AV_CODEC_ID_BINKVIDEO,
+    AV_CODEC_ID_IFF_ILBM,
+#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM
+    AV_CODEC_ID_KGV1,
+    AV_CODEC_ID_YOP,
+    AV_CODEC_ID_VP8,
+    AV_CODEC_ID_PICTOR,
+    AV_CODEC_ID_ANSI,
+    AV_CODEC_ID_A64_MULTI,
+    AV_CODEC_ID_A64_MULTI5,
+    AV_CODEC_ID_R10K,
+    AV_CODEC_ID_MXPEG,
+    AV_CODEC_ID_LAGARITH,
+    AV_CODEC_ID_PRORES,
+    AV_CODEC_ID_JV,
+    AV_CODEC_ID_DFA,
+    AV_CODEC_ID_WMV3IMAGE,
+    AV_CODEC_ID_VC1IMAGE,
+    AV_CODEC_ID_UTVIDEO,
+    AV_CODEC_ID_BMV_VIDEO,
+    AV_CODEC_ID_VBLE,
+    AV_CODEC_ID_DXTORY,
+    AV_CODEC_ID_V410,
+    AV_CODEC_ID_XWD,
+    AV_CODEC_ID_CDXL,
+    AV_CODEC_ID_XBM,
+    AV_CODEC_ID_ZEROCODEC,
+    AV_CODEC_ID_MSS1,
+    AV_CODEC_ID_MSA1,
+    AV_CODEC_ID_TSCC2,
+    AV_CODEC_ID_MTS2,
+    AV_CODEC_ID_CLLC,
+    AV_CODEC_ID_MSS2,
+    AV_CODEC_ID_VP9,
+    AV_CODEC_ID_AIC,
+    AV_CODEC_ID_ESCAPE130,
+    AV_CODEC_ID_G2M,
+    AV_CODEC_ID_WEBP,
+    AV_CODEC_ID_HNM4_VIDEO,
+    AV_CODEC_ID_HEVC,
+#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC
+    AV_CODEC_ID_FIC,
+    AV_CODEC_ID_ALIAS_PIX,
+    AV_CODEC_ID_BRENDER_PIX,
+    AV_CODEC_ID_PAF_VIDEO,
+    AV_CODEC_ID_EXR,
+    AV_CODEC_ID_VP7,
+    AV_CODEC_ID_SANM,
+    AV_CODEC_ID_SGIRLE,
+    AV_CODEC_ID_MVC1,
+    AV_CODEC_ID_MVC2,
+    AV_CODEC_ID_HQX,
+    AV_CODEC_ID_TDSC,
+    AV_CODEC_ID_HQ_HQA,
+    AV_CODEC_ID_HAP,
+    AV_CODEC_ID_DDS,
+    AV_CODEC_ID_DXV,
+    AV_CODEC_ID_SCREENPRESSO,
+    AV_CODEC_ID_RSCC,
+    AV_CODEC_ID_AVS2,
+
+    AV_CODEC_ID_Y41P = 0x8000,
+    AV_CODEC_ID_AVRP,
+    AV_CODEC_ID_012V,
+    AV_CODEC_ID_AVUI,
+    AV_CODEC_ID_AYUV,
+    AV_CODEC_ID_TARGA_Y216,
+    AV_CODEC_ID_V308,
+    AV_CODEC_ID_V408,
+    AV_CODEC_ID_YUV4,
+    AV_CODEC_ID_AVRN,
+    AV_CODEC_ID_CPIA,
+    AV_CODEC_ID_XFACE,
+    AV_CODEC_ID_SNOW,
+    AV_CODEC_ID_SMVJPEG,
+    AV_CODEC_ID_APNG,
+    AV_CODEC_ID_DAALA,
+    AV_CODEC_ID_CFHD,
+    AV_CODEC_ID_TRUEMOTION2RT,
+    AV_CODEC_ID_M101,
+    AV_CODEC_ID_MAGICYUV,
+    AV_CODEC_ID_SHEERVIDEO,
+    AV_CODEC_ID_YLC,
+    AV_CODEC_ID_PSD,
+    AV_CODEC_ID_PIXLET,
+    AV_CODEC_ID_SPEEDHQ,
+    AV_CODEC_ID_FMVC,
+    AV_CODEC_ID_SCPR,
+    AV_CODEC_ID_CLEARVIDEO,
+    AV_CODEC_ID_XPM,
+    AV_CODEC_ID_AV1,
+    AV_CODEC_ID_BITPACKED,
+    AV_CODEC_ID_MSCC,
+    AV_CODEC_ID_SRGC,
+    AV_CODEC_ID_SVG,
+    AV_CODEC_ID_GDV,
+    AV_CODEC_ID_FITS,
+    AV_CODEC_ID_IMM4,
+    AV_CODEC_ID_PROSUMER,
+    AV_CODEC_ID_MWSC,
+    AV_CODEC_ID_WCMV,
+    AV_CODEC_ID_RASC,
+
+    /* various PCM "codecs" */
+    AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
+    AV_CODEC_ID_PCM_S16LE = 0x10000,
+    AV_CODEC_ID_PCM_S16BE,
+    AV_CODEC_ID_PCM_U16LE,
+    AV_CODEC_ID_PCM_U16BE,
+    AV_CODEC_ID_PCM_S8,
+    AV_CODEC_ID_PCM_U8,
+    AV_CODEC_ID_PCM_MULAW,
+    AV_CODEC_ID_PCM_ALAW,
+    AV_CODEC_ID_PCM_S32LE,
+    AV_CODEC_ID_PCM_S32BE,
+    AV_CODEC_ID_PCM_U32LE,
+    AV_CODEC_ID_PCM_U32BE,
+    AV_CODEC_ID_PCM_S24LE,
+    AV_CODEC_ID_PCM_S24BE,
+    AV_CODEC_ID_PCM_U24LE,
+    AV_CODEC_ID_PCM_U24BE,
+    AV_CODEC_ID_PCM_S24DAUD,
+    AV_CODEC_ID_PCM_ZORK,
+    AV_CODEC_ID_PCM_S16LE_PLANAR,
+    AV_CODEC_ID_PCM_DVD,
+    AV_CODEC_ID_PCM_F32BE,
+    AV_CODEC_ID_PCM_F32LE,
+    AV_CODEC_ID_PCM_F64BE,
+    AV_CODEC_ID_PCM_F64LE,
+    AV_CODEC_ID_PCM_BLURAY,
+    AV_CODEC_ID_PCM_LXF,
+    AV_CODEC_ID_S302M,
+    AV_CODEC_ID_PCM_S8_PLANAR,
+    AV_CODEC_ID_PCM_S24LE_PLANAR,
+    AV_CODEC_ID_PCM_S32LE_PLANAR,
+    AV_CODEC_ID_PCM_S16BE_PLANAR,
+
+    AV_CODEC_ID_PCM_S64LE = 0x10800,
+    AV_CODEC_ID_PCM_S64BE,
+    AV_CODEC_ID_PCM_F16LE,
+    AV_CODEC_ID_PCM_F24LE,
+    AV_CODEC_ID_PCM_VIDC,
+
+    /* various ADPCM codecs */
+    AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
+    AV_CODEC_ID_ADPCM_IMA_WAV,
+    AV_CODEC_ID_ADPCM_IMA_DK3,
+    AV_CODEC_ID_ADPCM_IMA_DK4,
+    AV_CODEC_ID_ADPCM_IMA_WS,
+    AV_CODEC_ID_ADPCM_IMA_SMJPEG,
+    AV_CODEC_ID_ADPCM_MS,
+    AV_CODEC_ID_ADPCM_4XM,
+    AV_CODEC_ID_ADPCM_XA,
+    AV_CODEC_ID_ADPCM_ADX,
+    AV_CODEC_ID_ADPCM_EA,
+    AV_CODEC_ID_ADPCM_G726,
+    AV_CODEC_ID_ADPCM_CT,
+    AV_CODEC_ID_ADPCM_SWF,
+    AV_CODEC_ID_ADPCM_YAMAHA,
+    AV_CODEC_ID_ADPCM_SBPRO_4,
+    AV_CODEC_ID_ADPCM_SBPRO_3,
+    AV_CODEC_ID_ADPCM_SBPRO_2,
+    AV_CODEC_ID_ADPCM_THP,
+    AV_CODEC_ID_ADPCM_IMA_AMV,
+    AV_CODEC_ID_ADPCM_EA_R1,
+    AV_CODEC_ID_ADPCM_EA_R3,
+    AV_CODEC_ID_ADPCM_EA_R2,
+    AV_CODEC_ID_ADPCM_IMA_EA_SEAD,
+    AV_CODEC_ID_ADPCM_IMA_EA_EACS,
+    AV_CODEC_ID_ADPCM_EA_XAS,
+    AV_CODEC_ID_ADPCM_EA_MAXIS_XA,
+    AV_CODEC_ID_ADPCM_IMA_ISS,
+    AV_CODEC_ID_ADPCM_G722,
+    AV_CODEC_ID_ADPCM_IMA_APC,
+    AV_CODEC_ID_ADPCM_VIMA,
+
+    AV_CODEC_ID_ADPCM_AFC = 0x11800,
+    AV_CODEC_ID_ADPCM_IMA_OKI,
+    AV_CODEC_ID_ADPCM_DTK,
+    AV_CODEC_ID_ADPCM_IMA_RAD,
+    AV_CODEC_ID_ADPCM_G726LE,
+    AV_CODEC_ID_ADPCM_THP_LE,
+    AV_CODEC_ID_ADPCM_PSX,
+    AV_CODEC_ID_ADPCM_AICA,
+    AV_CODEC_ID_ADPCM_IMA_DAT4,
+    AV_CODEC_ID_ADPCM_MTAF,
+
+    /* AMR */
+    AV_CODEC_ID_AMR_NB = 0x12000,
+    AV_CODEC_ID_AMR_WB,
+
+    /* RealAudio codecs*/
+    AV_CODEC_ID_RA_144 = 0x13000,
+    AV_CODEC_ID_RA_288,
+
+    /* various DPCM codecs */
+    AV_CODEC_ID_ROQ_DPCM = 0x14000,
+    AV_CODEC_ID_INTERPLAY_DPCM,
+    AV_CODEC_ID_XAN_DPCM,
+    AV_CODEC_ID_SOL_DPCM,
+
+    AV_CODEC_ID_SDX2_DPCM = 0x14800,
+    AV_CODEC_ID_GREMLIN_DPCM,
+
+    /* audio codecs */
+    AV_CODEC_ID_MP2 = 0x15000,
+    AV_CODEC_ID_MP3, ///< preferred ID for decoding MPEG audio layer 1, 2 or 3
+    AV_CODEC_ID_AAC,
+    AV_CODEC_ID_AC3,
+    AV_CODEC_ID_DTS,
+    AV_CODEC_ID_VORBIS,
+    AV_CODEC_ID_DVAUDIO,
+    AV_CODEC_ID_WMAV1,
+    AV_CODEC_ID_WMAV2,
+    AV_CODEC_ID_MACE3,
+    AV_CODEC_ID_MACE6,
+    AV_CODEC_ID_VMDAUDIO,
+    AV_CODEC_ID_FLAC,
+    AV_CODEC_ID_MP3ADU,
+    AV_CODEC_ID_MP3ON4,
+    AV_CODEC_ID_SHORTEN,
+    AV_CODEC_ID_ALAC,
+    AV_CODEC_ID_WESTWOOD_SND1,
+    AV_CODEC_ID_GSM, ///< as in Berlin toast format
+    AV_CODEC_ID_QDM2,
+    AV_CODEC_ID_COOK,
+    AV_CODEC_ID_TRUESPEECH,
+    AV_CODEC_ID_TTA,
+    AV_CODEC_ID_SMACKAUDIO,
+    AV_CODEC_ID_QCELP,
+    AV_CODEC_ID_WAVPACK,
+    AV_CODEC_ID_DSICINAUDIO,
+    AV_CODEC_ID_IMC,
+    AV_CODEC_ID_MUSEPACK7,
+    AV_CODEC_ID_MLP,
+    AV_CODEC_ID_GSM_MS, /* as found in WAV */
+    AV_CODEC_ID_ATRAC3,
+    AV_CODEC_ID_APE,
+    AV_CODEC_ID_NELLYMOSER,
+    AV_CODEC_ID_MUSEPACK8,
+    AV_CODEC_ID_SPEEX,
+    AV_CODEC_ID_WMAVOICE,
+    AV_CODEC_ID_WMAPRO,
+    AV_CODEC_ID_WMALOSSLESS,
+    AV_CODEC_ID_ATRAC3P,
+    AV_CODEC_ID_EAC3,
+    AV_CODEC_ID_SIPR,
+    AV_CODEC_ID_MP1,
+    AV_CODEC_ID_TWINVQ,
+    AV_CODEC_ID_TRUEHD,
+    AV_CODEC_ID_MP4ALS,
+    AV_CODEC_ID_ATRAC1,
+    AV_CODEC_ID_BINKAUDIO_RDFT,
+    AV_CODEC_ID_BINKAUDIO_DCT,
+    AV_CODEC_ID_AAC_LATM,
+    AV_CODEC_ID_QDMC,
+    AV_CODEC_ID_CELT,
+    AV_CODEC_ID_G723_1,
+    AV_CODEC_ID_G729,
+    AV_CODEC_ID_8SVX_EXP,
+    AV_CODEC_ID_8SVX_FIB,
+    AV_CODEC_ID_BMV_AUDIO,
+    AV_CODEC_ID_RALF,
+    AV_CODEC_ID_IAC,
+    AV_CODEC_ID_ILBC,
+    AV_CODEC_ID_OPUS,
+    AV_CODEC_ID_COMFORT_NOISE,
+    AV_CODEC_ID_TAK,
+    AV_CODEC_ID_METASOUND,
+    AV_CODEC_ID_PAF_AUDIO,
+    AV_CODEC_ID_ON2AVC,
+    AV_CODEC_ID_DSS_SP,
+    AV_CODEC_ID_CODEC2,
+
+    AV_CODEC_ID_FFWAVESYNTH = 0x15800,
+    AV_CODEC_ID_SONIC,
+    AV_CODEC_ID_SONIC_LS,
+    AV_CODEC_ID_EVRC,
+    AV_CODEC_ID_SMV,
+    AV_CODEC_ID_DSD_LSBF,
+    AV_CODEC_ID_DSD_MSBF,
+    AV_CODEC_ID_DSD_LSBF_PLANAR,
+    AV_CODEC_ID_DSD_MSBF_PLANAR,
+    AV_CODEC_ID_4GV,
+    AV_CODEC_ID_INTERPLAY_ACM,
+    AV_CODEC_ID_XMA1,
+    AV_CODEC_ID_XMA2,
+    AV_CODEC_ID_DST,
+    AV_CODEC_ID_ATRAC3AL,
+    AV_CODEC_ID_ATRAC3PAL,
+    AV_CODEC_ID_DOLBY_E,
+    AV_CODEC_ID_APTX,
+    AV_CODEC_ID_APTX_HD,
+    AV_CODEC_ID_SBC,
+    AV_CODEC_ID_ATRAC9,
+
+    /* subtitle codecs */
+    AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing at the start of subtitle codecs.
+    AV_CODEC_ID_DVD_SUBTITLE = 0x17000,
+    AV_CODEC_ID_DVB_SUBTITLE,
+    AV_CODEC_ID_TEXT,  ///< raw UTF-8 text
+    AV_CODEC_ID_XSUB,
+    AV_CODEC_ID_SSA,
+    AV_CODEC_ID_MOV_TEXT,
+    AV_CODEC_ID_HDMV_PGS_SUBTITLE,
+    AV_CODEC_ID_DVB_TELETEXT,
+    AV_CODEC_ID_SRT,
+
+    AV_CODEC_ID_MICRODVD   = 0x17800,
+    AV_CODEC_ID_EIA_608,
+    AV_CODEC_ID_JACOSUB,
+    AV_CODEC_ID_SAMI,
+    AV_CODEC_ID_REALTEXT,
+    AV_CODEC_ID_STL,
+    AV_CODEC_ID_SUBVIEWER1,
+    AV_CODEC_ID_SUBVIEWER,
+    AV_CODEC_ID_SUBRIP,
+    AV_CODEC_ID_WEBVTT,
+    AV_CODEC_ID_MPL2,
+    AV_CODEC_ID_VPLAYER,
+    AV_CODEC_ID_PJS,
+    AV_CODEC_ID_ASS,
+    AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
+    AV_CODEC_ID_TTML,
+
+    /* other specific kind of codecs (generally used for attachments) */
+    AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,           ///< A dummy ID pointing at the start of various fake codecs.
+    AV_CODEC_ID_TTF = 0x18000,
+
+    AV_CODEC_ID_SCTE_35, ///< Contain timestamp estimated through PCR of program stream.
+    AV_CODEC_ID_BINTEXT    = 0x18800,
+    AV_CODEC_ID_XBIN,
+    AV_CODEC_ID_IDF,
+    AV_CODEC_ID_OTF,
+    AV_CODEC_ID_SMPTE_KLV,
+    AV_CODEC_ID_DVD_NAV,
+    AV_CODEC_ID_TIMED_ID3,
+    AV_CODEC_ID_BIN_DATA,
+
+
+    AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it
+
+    AV_CODEC_ID_MPEG2TS = 0x20000, /**< _FAKE_ codec to indicate a raw MPEG-2 TS
+                                * stream (only used by libavformat) */
+    AV_CODEC_ID_MPEG4SYSTEMS = 0x20001, /**< _FAKE_ codec to indicate a MPEG-4 Systems
+                                * stream (only used by libavformat) */
+    AV_CODEC_ID_FFMETADATA = 0x21000,   ///< Dummy codec for streams containing only metadata information.
+    AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket
+};
+
+/**
+ * This struct describes the properties of a single codec described by an
+ * AVCodecID.
+ * @see avcodec_descriptor_get()
+ */
+typedef struct AVCodecDescriptor {
+    enum AVCodecID     id;
+    enum AVMediaType type;
+    /**
+     * Name of the codec described by this descriptor. It is non-empty and
+     * unique for each codec descriptor. It should contain alphanumeric
+     * characters and '_' only.
+     */
+    const char      *name;
+    /**
+     * A more descriptive name for this codec. May be NULL.
+     */
+    const char *long_name;
+    /**
+     * Codec properties, a combination of AV_CODEC_PROP_* flags.
+     */
+    int             props;
+    /**
+     * MIME type(s) associated with the codec.
+     * May be NULL; if not, a NULL-terminated array of MIME types.
+     * The first item is always non-NULL and is the preferred MIME type.
+     */
+    const char *const *mime_types;
+    /**
+     * If non-NULL, an array of profiles recognized for this codec.
+     * Terminated with FF_PROFILE_UNKNOWN.
+     */
+    const struct AVProfile *profiles;
+} AVCodecDescriptor;
+
+/**
+ * Codec uses only intra compression.
+ * Video and audio codecs only.
+ */
+#define AV_CODEC_PROP_INTRA_ONLY    (1 << 0)
+/**
+ * Codec supports lossy compression. Audio and video codecs only.
+ * @note a codec may support both lossy and lossless
+ * compression modes
+ */
+#define AV_CODEC_PROP_LOSSY         (1 << 1)
+/**
+ * Codec supports lossless compression. Audio and video codecs only.
+ */
+#define AV_CODEC_PROP_LOSSLESS      (1 << 2)
+/**
+ * Codec supports frame reordering. That is, the coded order (the order in which
+ * the encoded packets are output by the encoders / stored / input to the
+ * decoders) may be different from the presentation order of the corresponding
+ * frames.
+ *
+ * For codecs that do not have this property set, PTS and DTS should always be
+ * equal.
+ */
+#define AV_CODEC_PROP_REORDER       (1 << 3)
+/**
+ * Subtitle codec is bitmap based
+ * Decoded AVSubtitle data can be read from the AVSubtitleRect->pict field.
+ */
+#define AV_CODEC_PROP_BITMAP_SUB    (1 << 16)
+/**
+ * Subtitle codec is text based.
+ * Decoded AVSubtitle data can be read from the AVSubtitleRect->ass field.
+ */
+#define AV_CODEC_PROP_TEXT_SUB      (1 << 17)
+
+/**
+ * @ingroup lavc_decoding
+ * Required number of additionally allocated bytes at the end of the input bitstream for decoding.
+ * This is mainly needed because some optimized bitstream readers read
+ * 32 or 64 bit at once and could read over the end.<br>
+ * Note: If the first 23 bits of the additional bytes are not 0, then damaged
+ * MPEG bitstreams could cause overread and segfault.
+ */
+#define AV_INPUT_BUFFER_PADDING_SIZE 64
+
+/**
+ * @ingroup lavc_encoding
+ * minimum encoding buffer size
+ * Used to avoid some checks during header writing.
+ */
+#define AV_INPUT_BUFFER_MIN_SIZE 16384
+
+/**
+ * @ingroup lavc_decoding
+ */
+enum AVDiscard{
+    /* We leave some space between them for extensions (drop some
+     * keyframes for intra-only or drop just some bidir frames). */
+    AVDISCARD_NONE    =-16, ///< discard nothing
+    AVDISCARD_DEFAULT =  0, ///< discard useless packets like 0 size packets in avi
+    AVDISCARD_NONREF  =  8, ///< discard all non reference
+    AVDISCARD_BIDIR   = 16, ///< discard all bidirectional frames
+    AVDISCARD_NONINTRA= 24, ///< discard all non intra frames
+    AVDISCARD_NONKEY  = 32, ///< discard all frames except keyframes
+    AVDISCARD_ALL     = 48, ///< discard all
+};
+
+enum AVAudioServiceType {
+    AV_AUDIO_SERVICE_TYPE_MAIN              = 0,
+    AV_AUDIO_SERVICE_TYPE_EFFECTS           = 1,
+    AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED = 2,
+    AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED  = 3,
+    AV_AUDIO_SERVICE_TYPE_DIALOGUE          = 4,
+    AV_AUDIO_SERVICE_TYPE_COMMENTARY        = 5,
+    AV_AUDIO_SERVICE_TYPE_EMERGENCY         = 6,
+    AV_AUDIO_SERVICE_TYPE_VOICE_OVER        = 7,
+    AV_AUDIO_SERVICE_TYPE_KARAOKE           = 8,
+    AV_AUDIO_SERVICE_TYPE_NB                   , ///< Not part of ABI
+};
+
+/**
+ * @ingroup lavc_encoding
+ */
+typedef struct RcOverride{
+    int start_frame;
+    int end_frame;
+    int qscale; // If this is 0 then quality_factor will be used instead.
+    float quality_factor;
+} RcOverride;
+
+/* encoding support
+   These flags can be passed in AVCodecContext.flags before initialization.
+   Note: Not everything is supported yet.
+*/
+
+/**
+ * Allow decoders to produce frames with data planes that are not aligned
+ * to CPU requirements (e.g. due to cropping).
+ */
+#define AV_CODEC_FLAG_UNALIGNED       (1 <<  0)
+/**
+ * Use fixed qscale.
+ */
+#define AV_CODEC_FLAG_QSCALE          (1 <<  1)
+/**
+ * 4 MV per MB allowed / advanced prediction for H.263.
+ */
+#define AV_CODEC_FLAG_4MV             (1 <<  2)
+/**
+ * Output even those frames that might be corrupted.
+ */
+#define AV_CODEC_FLAG_OUTPUT_CORRUPT  (1 <<  3)
+/**
+ * Use qpel MC.
+ */
+#define AV_CODEC_FLAG_QPEL            (1 <<  4)
+/**
+ * Use internal 2pass ratecontrol in first pass mode.
+ */
+#define AV_CODEC_FLAG_PASS1           (1 <<  9)
+/**
+ * Use internal 2pass ratecontrol in second pass mode.
+ */
+#define AV_CODEC_FLAG_PASS2           (1 << 10)
+/**
+ * loop filter.
+ */
+#define AV_CODEC_FLAG_LOOP_FILTER     (1 << 11)
+/**
+ * Only decode/encode grayscale.
+ */
+#define AV_CODEC_FLAG_GRAY            (1 << 13)
+/**
+ * error[?] variables will be set during encoding.
+ */
+#define AV_CODEC_FLAG_PSNR            (1 << 15)
+/**
+ * Input bitstream might be truncated at a random location
+ * instead of only at frame boundaries.
+ */
+#define AV_CODEC_FLAG_TRUNCATED       (1 << 16)
+/**
+ * Use interlaced DCT.
+ */
+#define AV_CODEC_FLAG_INTERLACED_DCT  (1 << 18)
+/**
+ * Force low delay.
+ */
+#define AV_CODEC_FLAG_LOW_DELAY       (1 << 19)
+/**
+ * Place global headers in extradata instead of every keyframe.
+ */
+#define AV_CODEC_FLAG_GLOBAL_HEADER   (1 << 22)
+/**
+ * Use only bitexact stuff (except (I)DCT).
+ */
+#define AV_CODEC_FLAG_BITEXACT        (1 << 23)
+/* Fx : Flag for H.263+ extra options */
+/**
+ * H.263 advanced intra coding / MPEG-4 AC prediction
+ */
+#define AV_CODEC_FLAG_AC_PRED         (1 << 24)
+/**
+ * interlaced motion estimation
+ */
+#define AV_CODEC_FLAG_INTERLACED_ME   (1 << 29)
+#define AV_CODEC_FLAG_CLOSED_GOP      (1U << 31)
+
+/**
+ * Allow non spec compliant speedup tricks.
+ */
+#define AV_CODEC_FLAG2_FAST           (1 <<  0)
+/**
+ * Skip bitstream encoding.
+ */
+#define AV_CODEC_FLAG2_NO_OUTPUT      (1 <<  2)
+/**
+ * Place global headers at every keyframe instead of in extradata.
+ */
+#define AV_CODEC_FLAG2_LOCAL_HEADER   (1 <<  3)
+
+/**
+ * timecode is in drop frame format. DEPRECATED!!!!
+ */
+#define AV_CODEC_FLAG2_DROP_FRAME_TIMECODE (1 << 13)
+
+/**
+ * Input bitstream might be truncated at a packet boundaries
+ * instead of only at frame boundaries.
+ */
+#define AV_CODEC_FLAG2_CHUNKS         (1 << 15)
+/**
+ * Discard cropping information from SPS.
+ */
+#define AV_CODEC_FLAG2_IGNORE_CROP    (1 << 16)
+
+/**
+ * Show all frames before the first keyframe
+ */
+#define AV_CODEC_FLAG2_SHOW_ALL       (1 << 22)
+/**
+ * Export motion vectors through frame side data
+ */
+#define AV_CODEC_FLAG2_EXPORT_MVS     (1 << 28)
+/**
+ * Do not skip samples and export skip information as frame side data
+ */
+#define AV_CODEC_FLAG2_SKIP_MANUAL    (1 << 29)
+/**
+ * Do not reset ASS ReadOrder field on flush (subtitles decoding)
+ */
+#define AV_CODEC_FLAG2_RO_FLUSH_NOOP  (1 << 30)
+
+/* Unsupported options :
+ *              Syntax Arithmetic coding (SAC)
+ *              Reference Picture Selection
+ *              Independent Segment Decoding */
+/* /Fx */
+/* codec capabilities */
+
+/**
+ * Decoder can use draw_horiz_band callback.
+ */
+#define AV_CODEC_CAP_DRAW_HORIZ_BAND     (1 <<  0)
+/**
+ * Codec uses get_buffer() for allocating buffers and supports custom allocators.
+ * If not set, it might not use get_buffer() at all or use operations that
+ * assume the buffer was allocated by avcodec_default_get_buffer.
+ */
+#define AV_CODEC_CAP_DR1                 (1 <<  1)
+#define AV_CODEC_CAP_TRUNCATED           (1 <<  3)
+/**
+ * Encoder or decoder requires flushing with NULL input at the end in order to
+ * give the complete and correct output.
+ *
+ * NOTE: If this flag is not set, the codec is guaranteed to never be fed with
+ *       with NULL data. The user can still send NULL data to the public encode
+ *       or decode function, but libavcodec will not pass it along to the codec
+ *       unless this flag is set.
+ *
+ * Decoders:
+ * The decoder has a non-zero delay and needs to be fed with avpkt->data=NULL,
+ * avpkt->size=0 at the end to get the delayed data until the decoder no longer
+ * returns frames.
+ *
+ * Encoders:
+ * The encoder needs to be fed with NULL data at the end of encoding until the
+ * encoder no longer returns data.
+ *
+ * NOTE: For encoders implementing the AVCodec.encode2() function, setting this
+ *       flag also means that the encoder must set the pts and duration for
+ *       each output packet. If this flag is not set, the pts and duration will
+ *       be determined by libavcodec from the input frame.
+ */
+#define AV_CODEC_CAP_DELAY               (1 <<  5)
+/**
+ * Codec can be fed a final frame with a smaller size.
+ * This can be used to prevent truncation of the last audio samples.
+ */
+#define AV_CODEC_CAP_SMALL_LAST_FRAME    (1 <<  6)
+
+/**
+ * Codec can output multiple frames per AVPacket
+ * Normally demuxers return one frame at a time, demuxers which do not do
+ * are connected to a parser to split what they return into proper frames.
+ * This flag is reserved to the very rare category of codecs which have a
+ * bitstream that cannot be split into frames without timeconsuming
+ * operations like full decoding. Demuxers carrying such bitstreams thus
+ * may return multiple frames in a packet. This has many disadvantages like
+ * prohibiting stream copy in many cases thus it should only be considered
+ * as a last resort.
+ */
+#define AV_CODEC_CAP_SUBFRAMES           (1 <<  8)
+/**
+ * Codec is experimental and is thus avoided in favor of non experimental
+ * encoders
+ */
+#define AV_CODEC_CAP_EXPERIMENTAL        (1 <<  9)
+/**
+ * Codec should fill in channel configuration and samplerate instead of container
+ */
+#define AV_CODEC_CAP_CHANNEL_CONF        (1 << 10)
+/**
+ * Codec supports frame-level multithreading.
+ */
+#define AV_CODEC_CAP_FRAME_THREADS       (1 << 12)
+/**
+ * Codec supports slice-based (or partition-based) multithreading.
+ */
+#define AV_CODEC_CAP_SLICE_THREADS       (1 << 13)
+/**
+ * Codec supports changed parameters at any point.
+ */
+#define AV_CODEC_CAP_PARAM_CHANGE        (1 << 14)
+/**
+ * Codec supports avctx->thread_count == 0 (auto).
+ */
+#define AV_CODEC_CAP_AUTO_THREADS        (1 << 15)
+/**
+ * Audio encoder supports receiving a different number of samples in each call.
+ */
+#define AV_CODEC_CAP_VARIABLE_FRAME_SIZE (1 << 16)
+/**
+ * Decoder is not a preferred choice for probing.
+ * This indicates that the decoder is not a good choice for probing.
+ * It could for example be an expensive to spin up hardware decoder,
+ * or it could simply not provide a lot of useful information about
+ * the stream.
+ * A decoder marked with this flag should only be used as last resort
+ * choice for probing.
+ */
+#define AV_CODEC_CAP_AVOID_PROBING       (1 << 17)
+/**
+ * Codec is intra only.
+ */
+#define AV_CODEC_CAP_INTRA_ONLY       0x40000000
+/**
+ * Codec is lossless.
+ */
+#define AV_CODEC_CAP_LOSSLESS         0x80000000
+
+/**
+ * Codec is backed by a hardware implementation. Typically used to
+ * identify a non-hwaccel hardware decoder. For information about hwaccels, use
+ * avcodec_get_hw_config() instead.
+ */
+#define AV_CODEC_CAP_HARDWARE            (1 << 18)
+
+/**
+ * Codec is potentially backed by a hardware implementation, but not
+ * necessarily. This is used instead of AV_CODEC_CAP_HARDWARE, if the
+ * implementation provides some sort of internal fallback.
+ */
+#define AV_CODEC_CAP_HYBRID              (1 << 19)
+
+/**
+ * Pan Scan area.
+ * This specifies the area which should be displayed.
+ * Note there may be multiple such areas for one frame.
+ */
+typedef struct AVPanScan {
+    /**
+     * id
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+    int id;
+
+    /**
+     * width and height in 1/16 pel
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+    int width;
+    int height;
+
+    /**
+     * position of the top left corner in 1/16 pel for up to 3 fields/frames
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+    int16_t position[3][2];
+} AVPanScan;
+
+/**
+ * This structure describes the bitrate properties of an encoded bitstream. It
+ * roughly corresponds to a subset the VBV parameters for MPEG-2 or HRD
+ * parameters for H.264/HEVC.
+ */
+typedef struct AVCPBProperties {
+    /**
+     * Maximum bitrate of the stream, in bits per second.
+     * Zero if unknown or unspecified.
+     */
+    int max_bitrate;
+    /**
+     * Minimum bitrate of the stream, in bits per second.
+     * Zero if unknown or unspecified.
+     */
+    int min_bitrate;
+    /**
+     * Average bitrate of the stream, in bits per second.
+     * Zero if unknown or unspecified.
+     */
+    int avg_bitrate;
+
+    /**
+     * The size of the buffer to which the ratecontrol is applied, in bits.
+     * Zero if unknown or unspecified.
+     */
+    int buffer_size;
+
+    /**
+     * The delay between the time the packet this structure is associated with
+     * is received and the time when it should be decoded, in periods of a 27MHz
+     * clock.
+     *
+     * UINT64_MAX when unknown or unspecified.
+     */
+    uint64_t vbv_delay;
+} AVCPBProperties;
+
+/**
+ * The decoder will keep a reference to the frame and may reuse it later.
+ */
+#define AV_GET_BUFFER_FLAG_REF (1 << 0)
+
+/**
+ * @defgroup lavc_packet AVPacket
+ *
+ * Types and functions for working with AVPacket.
+ * @{
+ */
+enum AVPacketSideDataType {
+    /**
+     * An AV_PKT_DATA_PALETTE side data packet contains exactly AVPALETTE_SIZE
+     * bytes worth of palette. This side data signals that a new palette is
+     * present.
+     */
+    AV_PKT_DATA_PALETTE,
+
+    /**
+     * The AV_PKT_DATA_NEW_EXTRADATA is used to notify the codec or the format
+     * that the extradata buffer was changed and the receiving side should
+     * act upon it appropriately. The new extradata is embedded in the side
+     * data buffer and should be immediately used for processing the current
+     * frame or packet.
+     */
+    AV_PKT_DATA_NEW_EXTRADATA,
+
+    /**
+     * An AV_PKT_DATA_PARAM_CHANGE side data packet is laid out as follows:
+     * @code
+     * u32le param_flags
+     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT)
+     *     s32le channel_count
+     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT)
+     *     u64le channel_layout
+     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE)
+     *     s32le sample_rate
+     * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS)
+     *     s32le width
+     *     s32le height
+     * @endcode
+     */
+    AV_PKT_DATA_PARAM_CHANGE,
+
+    /**
+     * An AV_PKT_DATA_H263_MB_INFO side data packet contains a number of
+     * structures with info about macroblocks relevant to splitting the
+     * packet into smaller packets on macroblock edges (e.g. as for RFC 2190).
+     * That is, it does not necessarily contain info about all macroblocks,
+     * as long as the distance between macroblocks in the info is smaller
+     * than the target payload size.
+     * Each MB info structure is 12 bytes, and is laid out as follows:
+     * @code
+     * u32le bit offset from the start of the packet
+     * u8    current quantizer at the start of the macroblock
+     * u8    GOB number
+     * u16le macroblock address within the GOB
+     * u8    horizontal MV predictor
+     * u8    vertical MV predictor
+     * u8    horizontal MV predictor for block number 3
+     * u8    vertical MV predictor for block number 3
+     * @endcode
+     */
+    AV_PKT_DATA_H263_MB_INFO,
+
+    /**
+     * This side data should be associated with an audio stream and contains
+     * ReplayGain information in form of the AVReplayGain struct.
+     */
+    AV_PKT_DATA_REPLAYGAIN,
+
+    /**
+     * This side data contains a 3x3 transformation matrix describing an affine
+     * transformation that needs to be applied to the decoded video frames for
+     * correct presentation.
+     *
+     * See libavutil/display.h for a detailed description of the data.
+     */
+    AV_PKT_DATA_DISPLAYMATRIX,
+
+    /**
+     * This side data should be associated with a video stream and contains
+     * Stereoscopic 3D information in form of the AVStereo3D struct.
+     */
+    AV_PKT_DATA_STEREO3D,
+
+    /**
+     * This side data should be associated with an audio stream and corresponds
+     * to enum AVAudioServiceType.
+     */
+    AV_PKT_DATA_AUDIO_SERVICE_TYPE,
+
+    /**
+     * This side data contains quality related information from the encoder.
+     * @code
+     * u32le quality factor of the compressed frame. Allowed range is between 1 (good) and FF_LAMBDA_MAX (bad).
+     * u8    picture type
+     * u8    error count
+     * u16   reserved
+     * u64le[error count] sum of squared differences between encoder in and output
+     * @endcode
+     */
+    AV_PKT_DATA_QUALITY_STATS,
+
+    /**
+     * This side data contains an integer value representing the stream index
+     * of a "fallback" track.  A fallback track indicates an alternate
+     * track to use when the current track can not be decoded for some reason.
+     * e.g. no decoder available for codec.
+     */
+    AV_PKT_DATA_FALLBACK_TRACK,
+
+    /**
+     * This side data corresponds to the AVCPBProperties struct.
+     */
+    AV_PKT_DATA_CPB_PROPERTIES,
+
+    /**
+     * Recommmends skipping the specified number of samples
+     * @code
+     * u32le number of samples to skip from start of this packet
+     * u32le number of samples to skip from end of this packet
+     * u8    reason for start skip
+     * u8    reason for end   skip (0=padding silence, 1=convergence)
+     * @endcode
+     */
+    AV_PKT_DATA_SKIP_SAMPLES,
+
+    /**
+     * An AV_PKT_DATA_JP_DUALMONO side data packet indicates that
+     * the packet may contain "dual mono" audio specific to Japanese DTV
+     * and if it is true, recommends only the selected channel to be used.
+     * @code
+     * u8    selected channels (0=mail/left, 1=sub/right, 2=both)
+     * @endcode
+     */
+    AV_PKT_DATA_JP_DUALMONO,
+
+    /**
+     * A list of zero terminated key/value strings. There is no end marker for
+     * the list, so it is required to rely on the side data size to stop.
+     */
+    AV_PKT_DATA_STRINGS_METADATA,
+
+    /**
+     * Subtitle event position
+     * @code
+     * u32le x1
+     * u32le y1
+     * u32le x2
+     * u32le y2
+     * @endcode
+     */
+    AV_PKT_DATA_SUBTITLE_POSITION,
+
+    /**
+     * Data found in BlockAdditional element of matroska container. There is
+     * no end marker for the data, so it is required to rely on the side data
+     * size to recognize the end. 8 byte id (as found in BlockAddId) followed
+     * by data.
+     */
+    AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
+
+    /**
+     * The optional first identifier line of a WebVTT cue.
+     */
+    AV_PKT_DATA_WEBVTT_IDENTIFIER,
+
+    /**
+     * The optional settings (rendering instructions) that immediately
+     * follow the timestamp specifier of a WebVTT cue.
+     */
+    AV_PKT_DATA_WEBVTT_SETTINGS,
+
+    /**
+     * A list of zero terminated key/value strings. There is no end marker for
+     * the list, so it is required to rely on the side data size to stop. This
+     * side data includes updated metadata which appeared in the stream.
+     */
+    AV_PKT_DATA_METADATA_UPDATE,
+
+    /**
+     * MPEGTS stream ID as uint8_t, this is required to pass the stream ID
+     * information from the demuxer to the corresponding muxer.
+     */
+    AV_PKT_DATA_MPEGTS_STREAM_ID,
+
+    /**
+     * Mastering display metadata (based on SMPTE-2086:2014). This metadata
+     * should be associated with a video stream and contains data in the form
+     * of the AVMasteringDisplayMetadata struct.
+     */
+    AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+
+    /**
+     * This side data should be associated with a video stream and corresponds
+     * to the AVSphericalMapping structure.
+     */
+    AV_PKT_DATA_SPHERICAL,
+
+    /**
+     * Content light level (based on CTA-861.3). This metadata should be
+     * associated with a video stream and contains data in the form of the
+     * AVContentLightMetadata struct.
+     */
+    AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+
+    /**
+     * ATSC A53 Part 4 Closed Captions. This metadata should be associated with
+     * a video stream. A53 CC bitstream is stored as uint8_t in AVPacketSideData.data.
+     * The number of bytes of CC data is AVPacketSideData.size.
+     */
+    AV_PKT_DATA_A53_CC,
+
+    /**
+     * This side data is encryption initialization data.
+     * The format is not part of ABI, use av_encryption_init_info_* methods to
+     * access.
+     */
+    AV_PKT_DATA_ENCRYPTION_INIT_INFO,
+
+    /**
+     * This side data contains encryption info for how to decrypt the packet.
+     * The format is not part of ABI, use av_encryption_info_* methods to access.
+     */
+    AV_PKT_DATA_ENCRYPTION_INFO,
+
+    /**
+     * Active Format Description data consisting of a single byte as specified
+     * in ETSI TS 101 154 using AVActiveFormatDescription enum.
+     */
+    AV_PKT_DATA_AFD,
+
+    /**
+     * The number of side data types.
+     * This is not part of the public API/ABI in the sense that it may
+     * change when new side data types are added.
+     * This must stay the last enum value.
+     * If its value becomes huge, some code using it
+     * needs to be updated as it assumes it to be smaller than other limits.
+     */
+    AV_PKT_DATA_NB
+};
+
+#define AV_PKT_DATA_QUALITY_FACTOR AV_PKT_DATA_QUALITY_STATS //DEPRECATED
+
+typedef struct AVPacketSideData {
+    uint8_t *data;
+    int      size;
+    enum AVPacketSideDataType type;
+} AVPacketSideData;
+
+/**
+ * This structure stores compressed data. It is typically exported by demuxers
+ * and then passed as input to decoders, or received as output from encoders and
+ * then passed to muxers.
+ *
+ * For video, it should typically contain one compressed frame. For audio it may
+ * contain several compressed frames. Encoders are allowed to output empty
+ * packets, with no compressed data, containing only side data
+ * (e.g. to update some stream parameters at the end of encoding).
+ *
+ * AVPacket is one of the few structs in FFmpeg, whose size is a part of public
+ * ABI. Thus it may be allocated on stack and no new fields can be added to it
+ * without libavcodec and libavformat major bump.
+ *
+ * The semantics of data ownership depends on the buf field.
+ * If it is set, the packet data is dynamically allocated and is
+ * valid indefinitely until a call to av_packet_unref() reduces the
+ * reference count to 0.
+ *
+ * If the buf field is not set av_packet_ref() would make a copy instead
+ * of increasing the reference count.
+ *
+ * The side data is always allocated with av_malloc(), copied by
+ * av_packet_ref() and freed by av_packet_unref().
+ *
+ * @see av_packet_ref
+ * @see av_packet_unref
+ */
+typedef struct AVPacket {
+    /**
+     * A reference to the reference-counted buffer where the packet data is
+     * stored.
+     * May be NULL, then the packet data is not reference-counted.
+     */
+    AVBufferRef *buf;
+    /**
+     * Presentation timestamp in AVStream->time_base units; the time at which
+     * the decompressed packet will be presented to the user.
+     * Can be AV_NOPTS_VALUE if it is not stored in the file.
+     * pts MUST be larger or equal to dts as presentation cannot happen before
+     * decompression, unless one wants to view hex dumps. Some formats misuse
+     * the terms dts and pts/cts to mean something different. Such timestamps
+     * must be converted to true pts/dts before they are stored in AVPacket.
+     */
+    int64_t pts;
+    /**
+     * Decompression timestamp in AVStream->time_base units; the time at which
+     * the packet is decompressed.
+     * Can be AV_NOPTS_VALUE if it is not stored in the file.
+     */
+    int64_t dts;
+    uint8_t *data;
+    int   size;
+    int   stream_index;
+    /**
+     * A combination of AV_PKT_FLAG values
+     */
+    int   flags;
+    /**
+     * Additional packet data that can be provided by the container.
+     * Packet can contain several types of side information.
+     */
+    AVPacketSideData *side_data;
+    int side_data_elems;
+
+    /**
+     * Duration of this packet in AVStream->time_base units, 0 if unknown.
+     * Equals next_pts - this_pts in presentation order.
+     */
+    int64_t duration;
+
+    int64_t pos;                            ///< byte position in stream, -1 if unknown
+
+#if FF_API_CONVERGENCE_DURATION
+    /**
+     * @deprecated Same as the duration field, but as int64_t. This was required
+     * for Matroska subtitles, whose duration values could overflow when the
+     * duration field was still an int.
+     */
+    attribute_deprecated
+    int64_t convergence_duration;
+#endif
+} AVPacket;
+#define AV_PKT_FLAG_KEY     0x0001 ///< The packet contains a keyframe
+#define AV_PKT_FLAG_CORRUPT 0x0002 ///< The packet content is corrupted
+/**
+ * Flag is used to discard packets which are required to maintain valid
+ * decoder state but are not required for output and should be dropped
+ * after decoding.
+ **/
+#define AV_PKT_FLAG_DISCARD   0x0004
+/**
+ * The packet comes from a trusted source.
+ *
+ * Otherwise-unsafe constructs such as arbitrary pointers to data
+ * outside the packet may be followed.
+ */
+#define AV_PKT_FLAG_TRUSTED   0x0008
+/**
+ * Flag is used to indicate packets that contain frames that can
+ * be discarded by the decoder.  I.e. Non-reference frames.
+ */
+#define AV_PKT_FLAG_DISPOSABLE 0x0010
+
+
+enum AVSideDataParamChangeFlags {
+    AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT  = 0x0001,
+    AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT = 0x0002,
+    AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE    = 0x0004,
+    AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS     = 0x0008,
+};
+/**
+ * @}
+ */
+
+struct AVCodecInternal;
+
+enum AVFieldOrder {
+    AV_FIELD_UNKNOWN,
+    AV_FIELD_PROGRESSIVE,
+    AV_FIELD_TT,          //< Top coded_first, top displayed first
+    AV_FIELD_BB,          //< Bottom coded first, bottom displayed first
+    AV_FIELD_TB,          //< Top coded first, bottom displayed first
+    AV_FIELD_BT,          //< Bottom coded first, top displayed first
+};
+
+/**
+ * main external API structure.
+ * New fields can be added to the end with minor version bumps.
+ * Removal, reordering and changes to existing fields require a major
+ * version bump.
+ * You can use AVOptions (av_opt* / av_set/get*()) to access these fields from user
+ * applications.
+ * The name string for AVOptions options matches the associated command line
+ * parameter name and can be found in libavcodec/options_table.h
+ * The AVOption/command line parameter names differ in some cases from the C
+ * structure field names for historic reasons or brevity.
+ * sizeof(AVCodecContext) must not be used outside libav*.
+ */
+typedef struct AVCodecContext {
+    /**
+     * information on struct for av_log
+     * - set by avcodec_alloc_context3
+     */
+    const AVClass *av_class;
+    int log_level_offset;
+
+    enum AVMediaType codec_type; /* see AVMEDIA_TYPE_xxx */
+    const struct AVCodec  *codec;
+    enum AVCodecID     codec_id; /* see AV_CODEC_ID_xxx */
+
+    /**
+     * fourcc (LSB first, so "ABCD" -> ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A').
+     * This is used to work around some encoder bugs.
+     * A demuxer should set this to what is stored in the field used to identify the codec.
+     * If there are multiple such fields in a container then the demuxer should choose the one
+     * which maximizes the information about the used codec.
+     * If the codec tag field in a container is larger than 32 bits then the demuxer should
+     * remap the longer ID to 32 bits with a table or other structure. Alternatively a new
+     * extra_codec_tag + size could be added but for this a clear advantage must be demonstrated
+     * first.
+     * - encoding: Set by user, if not then the default based on codec_id will be used.
+     * - decoding: Set by user, will be converted to uppercase by libavcodec during init.
+     */
+    unsigned int codec_tag;
+
+    void *priv_data;
+
+    /**
+     * Private context used for internal data.
+     *
+     * Unlike priv_data, this is not codec-specific. It is used in general
+     * libavcodec functions.
+     */
+    struct AVCodecInternal *internal;
+
+    /**
+     * Private data of the user, can be used to carry app specific stuff.
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    void *opaque;
+
+    /**
+     * the average bitrate
+     * - encoding: Set by user; unused for constant quantizer encoding.
+     * - decoding: Set by user, may be overwritten by libavcodec
+     *             if this info is available in the stream
+     */
+    int64_t bit_rate;
+
+    /**
+     * number of bits the bitstream is allowed to diverge from the reference.
+     *           the reference can be CBR (for CBR pass1) or VBR (for pass2)
+     * - encoding: Set by user; unused for constant quantizer encoding.
+     * - decoding: unused
+     */
+    int bit_rate_tolerance;
+
+    /**
+     * Global quality for codecs which cannot change it per frame.
+     * This should be proportional to MPEG-1/2/4 qscale.
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int global_quality;
+
+    /**
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int compression_level;
+#define FF_COMPRESSION_DEFAULT -1
+
+    /**
+     * AV_CODEC_FLAG_*.
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    int flags;
+
+    /**
+     * AV_CODEC_FLAG2_*
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    int flags2;
+
+    /**
+     * some codecs need / can use extradata like Huffman tables.
+     * MJPEG: Huffman tables
+     * rv10: additional flags
+     * MPEG-4: global headers (they can be in the bitstream or here)
+     * The allocated memory should be AV_INPUT_BUFFER_PADDING_SIZE bytes larger
+     * than extradata_size to avoid problems if it is read with the bitstream reader.
+     * The bytewise contents of extradata must not depend on the architecture or CPU endianness.
+     * Must be allocated with the av_malloc() family of functions.
+     * - encoding: Set/allocated/freed by libavcodec.
+     * - decoding: Set/allocated/freed by user.
+     */
+    uint8_t *extradata;
+    int extradata_size;
+
+    /**
+     * This is the fundamental unit of time (in seconds) in terms
+     * of which frame timestamps are represented. For fixed-fps content,
+     * timebase should be 1/framerate and timestamp increments should be
+     * identically 1.
+     * This often, but not always is the inverse of the frame rate or field rate
+     * for video. 1/time_base is not the average frame rate if the frame rate is not
+     * constant.
+     *
+     * Like containers, elementary streams also can store timestamps, 1/time_base
+     * is the unit in which these timestamps are specified.
+     * As example of such codec time base see ISO/IEC 14496-2:2001(E)
+     * vop_time_increment_resolution and fixed_vop_rate
+     * (fixed_vop_rate == 0 implies that it is different from the framerate)
+     *
+     * - encoding: MUST be set by user.
+     * - decoding: the use of this field for decoding is deprecated.
+     *             Use framerate instead.
+     */
+    AVRational time_base;
+
+    /**
+     * For some codecs, the time base is closer to the field rate than the frame rate.
+     * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration
+     * if no telecine is used ...
+     *
+     * Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2.
+     */
+    int ticks_per_frame;
+
+    /**
+     * Codec delay.
+     *
+     * Encoding: Number of frames delay there will be from the encoder input to
+     *           the decoder output. (we assume the decoder matches the spec)
+     * Decoding: Number of frames delay in addition to what a standard decoder
+     *           as specified in the spec would produce.
+     *
+     * Video:
+     *   Number of frames the decoded output will be delayed relative to the
+     *   encoded input.
+     *
+     * Audio:
+     *   For encoding, this field is unused (see initial_padding).
+     *
+     *   For decoding, this is the number of samples the decoder needs to
+     *   output before the decoder's output is valid. When seeking, you should
+     *   start decoding this many samples prior to your desired seek point.
+     *
+     * - encoding: Set by libavcodec.
+     * - decoding: Set by libavcodec.
+     */
+    int delay;
+
+
+    /* video only */
+    /**
+     * picture width / height.
+     *
+     * @note Those fields may not match the values of the last
+     * AVFrame output by avcodec_decode_video2 due frame
+     * reordering.
+     *
+     * - encoding: MUST be set by user.
+     * - decoding: May be set by the user before opening the decoder if known e.g.
+     *             from the container. Some decoders will require the dimensions
+     *             to be set by the caller. During decoding, the decoder may
+     *             overwrite those values as required while parsing the data.
+     */
+    int width, height;
+
+    /**
+     * Bitstream width / height, may be different from width/height e.g. when
+     * the decoded frame is cropped before being output or lowres is enabled.
+     *
+     * @note Those field may not match the value of the last
+     * AVFrame output by avcodec_receive_frame() due frame
+     * reordering.
+     *
+     * - encoding: unused
+     * - decoding: May be set by the user before opening the decoder if known
+     *             e.g. from the container. During decoding, the decoder may
+     *             overwrite those values as required while parsing the data.
+     */
+    int coded_width, coded_height;
+
+    /**
+     * the number of pictures in a group of pictures, or 0 for intra_only
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int gop_size;
+
+    /**
+     * Pixel format, see AV_PIX_FMT_xxx.
+     * May be set by the demuxer if known from headers.
+     * May be overridden by the decoder if it knows better.
+     *
+     * @note This field may not match the value of the last
+     * AVFrame output by avcodec_receive_frame() due frame
+     * reordering.
+     *
+     * - encoding: Set by user.
+     * - decoding: Set by user if known, overridden by libavcodec while
+     *             parsing the data.
+     */
+    enum AVPixelFormat pix_fmt;
+
+    /**
+     * If non NULL, 'draw_horiz_band' is called by the libavcodec
+     * decoder to draw a horizontal band. It improves cache usage. Not
+     * all codecs can do that. You must check the codec capabilities
+     * beforehand.
+     * When multithreading is used, it may be called from multiple threads
+     * at the same time; threads might draw different parts of the same AVFrame,
+     * or multiple AVFrames, and there is no guarantee that slices will be drawn
+     * in order.
+     * The function is also used by hardware acceleration APIs.
+     * It is called at least once during frame decoding to pass
+     * the data needed for hardware render.
+     * In that mode instead of pixel data, AVFrame points to
+     * a structure specific to the acceleration API. The application
+     * reads the structure and can change some fields to indicate progress
+     * or mark state.
+     * - encoding: unused
+     * - decoding: Set by user.
+     * @param height the height of the slice
+     * @param y the y position of the slice
+     * @param type 1->top field, 2->bottom field, 3->frame
+     * @param offset offset into the AVFrame.data from which the slice should be read
+     */
+    void (*draw_horiz_band)(struct AVCodecContext *s,
+                            const AVFrame *src, int offset[AV_NUM_DATA_POINTERS],
+                            int y, int type, int height);
+
+    /**
+     * callback to negotiate the pixelFormat
+     * @param fmt is the list of formats which are supported by the codec,
+     * it is terminated by -1 as 0 is a valid format, the formats are ordered by quality.
+     * The first is always the native one.
+     * @note The callback may be called again immediately if initialization for
+     * the selected (hardware-accelerated) pixel format failed.
+     * @warning Behavior is undefined if the callback returns a value not
+     * in the fmt list of formats.
+     * @return the chosen format
+     * - encoding: unused
+     * - decoding: Set by user, if not set the native format will be chosen.
+     */
+    enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
+
+    /**
+     * maximum number of B-frames between non-B-frames
+     * Note: The output will be delayed by max_b_frames+1 relative to the input.
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int max_b_frames;
+
+    /**
+     * qscale factor between IP and B-frames
+     * If > 0 then the last P-frame quantizer will be used (q= lastp_q*factor+offset).
+     * If < 0 then normal ratecontrol will be done (q= -normal_q*factor+offset).
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float b_quant_factor;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int b_frame_strategy;
+#endif
+
+    /**
+     * qscale offset between IP and B-frames
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float b_quant_offset;
+
+    /**
+     * Size of the frame reordering buffer in the decoder.
+     * For MPEG-2 it is 1 IPB or 0 low delay IP.
+     * - encoding: Set by libavcodec.
+     * - decoding: Set by libavcodec.
+     */
+    int has_b_frames;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int mpeg_quant;
+#endif
+
+    /**
+     * qscale factor between P- and I-frames
+     * If > 0 then the last P-frame quantizer will be used (q = lastp_q * factor + offset).
+     * If < 0 then normal ratecontrol will be done (q= -normal_q*factor+offset).
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float i_quant_factor;
+
+    /**
+     * qscale offset between P and I-frames
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float i_quant_offset;
+
+    /**
+     * luminance masking (0-> disabled)
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float lumi_masking;
+
+    /**
+     * temporary complexity masking (0-> disabled)
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float temporal_cplx_masking;
+
+    /**
+     * spatial complexity masking (0-> disabled)
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float spatial_cplx_masking;
+
+    /**
+     * p block masking (0-> disabled)
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float p_masking;
+
+    /**
+     * darkness masking (0-> disabled)
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    float dark_masking;
+
+    /**
+     * slice count
+     * - encoding: Set by libavcodec.
+     * - decoding: Set by user (or 0).
+     */
+    int slice_count;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+     int prediction_method;
+#define FF_PRED_LEFT   0
+#define FF_PRED_PLANE  1
+#define FF_PRED_MEDIAN 2
+#endif
+
+    /**
+     * slice offsets in the frame in bytes
+     * - encoding: Set/allocated by libavcodec.
+     * - decoding: Set/allocated by user (or NULL).
+     */
+    int *slice_offset;
+
+    /**
+     * sample aspect ratio (0 if unknown)
+     * That is the width of a pixel divided by the height of the pixel.
+     * Numerator and denominator must be relatively prime and smaller than 256 for some video standards.
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+    AVRational sample_aspect_ratio;
+
+    /**
+     * motion estimation comparison function
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int me_cmp;
+    /**
+     * subpixel motion estimation comparison function
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int me_sub_cmp;
+    /**
+     * macroblock comparison function (not supported yet)
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int mb_cmp;
+    /**
+     * interlaced DCT comparison function
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int ildct_cmp;
+#define FF_CMP_SAD          0
+#define FF_CMP_SSE          1
+#define FF_CMP_SATD         2
+#define FF_CMP_DCT          3
+#define FF_CMP_PSNR         4
+#define FF_CMP_BIT          5
+#define FF_CMP_RD           6
+#define FF_CMP_ZERO         7
+#define FF_CMP_VSAD         8
+#define FF_CMP_VSSE         9
+#define FF_CMP_NSSE         10
+#define FF_CMP_W53          11
+#define FF_CMP_W97          12
+#define FF_CMP_DCTMAX       13
+#define FF_CMP_DCT264       14
+#define FF_CMP_MEDIAN_SAD   15
+#define FF_CMP_CHROMA       256
+
+    /**
+     * ME diamond size & shape
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int dia_size;
+
+    /**
+     * amount of previous MV predictors (2a+1 x 2a+1 square)
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int last_predictor_count;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int pre_me;
+#endif
+
+    /**
+     * motion estimation prepass comparison function
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int me_pre_cmp;
+
+    /**
+     * ME prepass diamond size & shape
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int pre_dia_size;
+
+    /**
+     * subpel ME quality
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int me_subpel_quality;
+
+    /**
+     * maximum motion estimation search range in subpel units
+     * If 0 then no limit.
+     *
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int me_range;
+
+    /**
+     * slice flags
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    int slice_flags;
+#define SLICE_FLAG_CODED_ORDER    0x0001 ///< draw_horiz_band() is called in coded order instead of display
+#define SLICE_FLAG_ALLOW_FIELD    0x0002 ///< allow draw_horiz_band() with field slices (MPEG-2 field pics)
+#define SLICE_FLAG_ALLOW_PLANE    0x0004 ///< allow draw_horiz_band() with 1 component at a time (SVQ1)
+
+    /**
+     * macroblock decision mode
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int mb_decision;
+#define FF_MB_DECISION_SIMPLE 0        ///< uses mb_cmp
+#define FF_MB_DECISION_BITS   1        ///< chooses the one which needs the fewest bits
+#define FF_MB_DECISION_RD     2        ///< rate distortion
+
+    /**
+     * custom intra quantization matrix
+     * - encoding: Set by user, can be NULL.
+     * - decoding: Set by libavcodec.
+     */
+    uint16_t *intra_matrix;
+
+    /**
+     * custom inter quantization matrix
+     * - encoding: Set by user, can be NULL.
+     * - decoding: Set by libavcodec.
+     */
+    uint16_t *inter_matrix;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int scenechange_threshold;
+
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int noise_reduction;
+#endif
+
+    /**
+     * precision of the intra DC coefficient - 8
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec
+     */
+    int intra_dc_precision;
+
+    /**
+     * Number of macroblock rows at the top which are skipped.
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    int skip_top;
+
+    /**
+     * Number of macroblock rows at the bottom which are skipped.
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    int skip_bottom;
+
+    /**
+     * minimum MB Lagrange multiplier
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int mb_lmin;
+
+    /**
+     * maximum MB Lagrange multiplier
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int mb_lmax;
+
+#if FF_API_PRIVATE_OPT
+    /**
+     * @deprecated use encoder private options instead
+     */
+    attribute_deprecated
+    int me_penalty_compensation;
+#endif
+
+    /**
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int bidir_refine;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int brd_scale;
+#endif
+
+    /**
+     * minimum GOP size
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int keyint_min;
+
+    /**
+     * number of reference frames
+     * - encoding: Set by user.
+     * - decoding: Set by lavc.
+     */
+    int refs;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int chromaoffset;
+#endif
+
+    /**
+     * Note: Value depends upon the compare function used for fullpel ME.
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int mv0_threshold;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int b_sensitivity;
+#endif
+
+    /**
+     * Chromaticity coordinates of the source primaries.
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
+     */
+    enum AVColorPrimaries color_primaries;
+
+    /**
+     * Color Transfer Characteristic.
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
+     */
+    enum AVColorTransferCharacteristic color_trc;
+
+    /**
+     * YUV colorspace type.
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
+     */
+    enum AVColorSpace colorspace;
+
+    /**
+     * MPEG vs JPEG YUV range.
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
+     */
+    enum AVColorRange color_range;
+
+    /**
+     * This defines the location of chroma samples.
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
+     */
+    enum AVChromaLocation chroma_sample_location;
+
+    /**
+     * Number of slices.
+     * Indicates number of picture subdivisions. Used for parallelized
+     * decoding.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    int slices;
+
+    /** Field order
+     * - encoding: set by libavcodec
+     * - decoding: Set by user.
+     */
+    enum AVFieldOrder field_order;
+
+    /* audio only */
+    int sample_rate; ///< samples per second
+    int channels;    ///< number of audio channels
+
+    /**
+     * audio sample format
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+    enum AVSampleFormat sample_fmt;  ///< sample format
+
+    /* The following data should not be initialized. */
+    /**
+     * Number of samples per channel in an audio frame.
+     *
+     * - encoding: set by libavcodec in avcodec_open2(). Each submitted frame
+     *   except the last must contain exactly frame_size samples per channel.
+     *   May be 0 when the codec has AV_CODEC_CAP_VARIABLE_FRAME_SIZE set, then the
+     *   frame size is not restricted.
+     * - decoding: may be set by some decoders to indicate constant frame size
+     */
+    int frame_size;
+
+    /**
+     * Frame counter, set by libavcodec.
+     *
+     * - decoding: total number of frames returned from the decoder so far.
+     * - encoding: total number of frames passed to the encoder so far.
+     *
+     *   @note the counter is not incremented if encoding/decoding resulted in
+     *   an error.
+     */
+    int frame_number;
+
+    /**
+     * number of bytes per packet if constant and known or 0
+     * Used by some WAV based audio codecs.
+     */
+    int block_align;
+
+    /**
+     * Audio cutoff bandwidth (0 means "automatic")
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int cutoff;
+
+    /**
+     * Audio channel layout.
+     * - encoding: set by user.
+     * - decoding: set by user, may be overwritten by libavcodec.
+     */
+    uint64_t channel_layout;
+
+    /**
+     * Request decoder to use this channel layout if it can (0 for default)
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    uint64_t request_channel_layout;
+
+    /**
+     * Type of service that the audio stream conveys.
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+    enum AVAudioServiceType audio_service_type;
+
+    /**
+     * desired sample format
+     * - encoding: Not used.
+     * - decoding: Set by user.
+     * Decoder will decode to this format if it can.
+     */
+    enum AVSampleFormat request_sample_fmt;
+
+    /**
+     * This callback is called at the beginning of each frame to get data
+     * buffer(s) for it. There may be one contiguous buffer for all the data or
+     * there may be a buffer per each data plane or anything in between. What
+     * this means is, you may set however many entries in buf[] you feel necessary.
+     * Each buffer must be reference-counted using the AVBuffer API (see description
+     * of buf[] below).
+     *
+     * The following fields will be set in the frame before this callback is
+     * called:
+     * - format
+     * - width, height (video only)
+     * - sample_rate, channel_layout, nb_samples (audio only)
+     * Their values may differ from the corresponding values in
+     * AVCodecContext. This callback must use the frame values, not the codec
+     * context values, to calculate the required buffer size.
+     *
+     * This callback must fill the following fields in the frame:
+     * - data[]
+     * - linesize[]
+     * - extended_data:
+     *   * if the data is planar audio with more than 8 channels, then this
+     *     callback must allocate and fill extended_data to contain all pointers
+     *     to all data planes. data[] must hold as many pointers as it can.
+     *     extended_data must be allocated with av_malloc() and will be freed in
+     *     av_frame_unref().
+     *   * otherwise extended_data must point to data
+     * - buf[] must contain one or more pointers to AVBufferRef structures. Each of
+     *   the frame's data and extended_data pointers must be contained in these. That
+     *   is, one AVBufferRef for each allocated chunk of memory, not necessarily one
+     *   AVBufferRef per data[] entry. See: av_buffer_create(), av_buffer_alloc(),
+     *   and av_buffer_ref().
+     * - extended_buf and nb_extended_buf must be allocated with av_malloc() by
+     *   this callback and filled with the extra buffers if there are more
+     *   buffers than buf[] can hold. extended_buf will be freed in
+     *   av_frame_unref().
+     *
+     * If AV_CODEC_CAP_DR1 is not set then get_buffer2() must call
+     * avcodec_default_get_buffer2() instead of providing buffers allocated by
+     * some other means.
+     *
+     * Each data plane must be aligned to the maximum required by the target
+     * CPU.
+     *
+     * @see avcodec_default_get_buffer2()
+     *
+     * Video:
+     *
+     * If AV_GET_BUFFER_FLAG_REF is set in flags then the frame may be reused
+     * (read and/or written to if it is writable) later by libavcodec.
+     *
+     * avcodec_align_dimensions2() should be used to find the required width and
+     * height, as they normally need to be rounded up to the next multiple of 16.
+     *
+     * Some decoders do not support linesizes changing between frames.
+     *
+     * If frame multithreading is used and thread_safe_callbacks is set,
+     * this callback may be called from a different thread, but not from more
+     * than one at once. Does not need to be reentrant.
+     *
+     * @see avcodec_align_dimensions2()
+     *
+     * Audio:
+     *
+     * Decoders request a buffer of a particular size by setting
+     * AVFrame.nb_samples prior to calling get_buffer2(). The decoder may,
+     * however, utilize only part of the buffer by setting AVFrame.nb_samples
+     * to a smaller value in the output frame.
+     *
+     * As a convenience, av_samples_get_buffer_size() and
+     * av_samples_fill_arrays() in libavutil may be used by custom get_buffer2()
+     * functions to find the required data size and to fill data pointers and
+     * linesize. In AVFrame.linesize, only linesize[0] may be set for audio
+     * since all planes must be the same size.
+     *
+     * @see av_samples_get_buffer_size(), av_samples_fill_arrays()
+     *
+     * - encoding: unused
+     * - decoding: Set by libavcodec, user can override.
+     */
+    int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags);
+
+    /**
+     * If non-zero, the decoded audio and video frames returned from
+     * avcodec_decode_video2() and avcodec_decode_audio4() are reference-counted
+     * and are valid indefinitely. The caller must free them with
+     * av_frame_unref() when they are not needed anymore.
+     * Otherwise, the decoded frames must not be freed by the caller and are
+     * only valid until the next decode call.
+     *
+     * This is always automatically enabled if avcodec_receive_frame() is used.
+     *
+     * - encoding: unused
+     * - decoding: set by the caller before avcodec_open2().
+     */
+    attribute_deprecated
+    int refcounted_frames;
+
+    /* - encoding parameters */
+    float qcompress;  ///< amount of qscale change between easy & hard scenes (0.0-1.0)
+    float qblur;      ///< amount of qscale smoothing over time (0.0-1.0)
+
+    /**
+     * minimum quantizer
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int qmin;
+
+    /**
+     * maximum quantizer
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int qmax;
+
+    /**
+     * maximum quantizer difference between frames
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int max_qdiff;
+
+    /**
+     * decoder bitstream buffer size
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int rc_buffer_size;
+
+    /**
+     * ratecontrol override, see RcOverride
+     * - encoding: Allocated/set/freed by user.
+     * - decoding: unused
+     */
+    int rc_override_count;
+    RcOverride *rc_override;
+
+    /**
+     * maximum bitrate
+     * - encoding: Set by user.
+     * - decoding: Set by user, may be overwritten by libavcodec.
+     */
+    int64_t rc_max_rate;
+
+    /**
+     * minimum bitrate
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int64_t rc_min_rate;
+
+    /**
+     * Ratecontrol attempt to use, at maximum, <value> of what can be used without an underflow.
+     * - encoding: Set by user.
+     * - decoding: unused.
+     */
+    float rc_max_available_vbv_use;
+
+    /**
+     * Ratecontrol attempt to use, at least, <value> times the amount needed to prevent a vbv overflow.
+     * - encoding: Set by user.
+     * - decoding: unused.
+     */
+    float rc_min_vbv_overflow_use;
+
+    /**
+     * Number of bits which should be loaded into the rc buffer before decoding starts.
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int rc_initial_buffer_occupancy;
+
+#if FF_API_CODER_TYPE
+#define FF_CODER_TYPE_VLC       0
+#define FF_CODER_TYPE_AC        1
+#define FF_CODER_TYPE_RAW       2
+#define FF_CODER_TYPE_RLE       3
+    /**
+     * @deprecated use encoder private options instead
+     */
+    attribute_deprecated
+    int coder_type;
+#endif /* FF_API_CODER_TYPE */
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int context_model;
+#endif
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int frame_skip_threshold;
+
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int frame_skip_factor;
+
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int frame_skip_exp;
+
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int frame_skip_cmp;
+#endif /* FF_API_PRIVATE_OPT */
+
+    /**
+     * trellis RD quantization
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int trellis;
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int min_prediction_order;
+
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int max_prediction_order;
+
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int64_t timecode_frame_start;
+#endif
+
+#if FF_API_RTP_CALLBACK
+    /**
+     * @deprecated unused
+     */
+    /* The RTP callback: This function is called    */
+    /* every time the encoder has a packet to send. */
+    /* It depends on the encoder if the data starts */
+    /* with a Start Code (it should). H.263 does.   */
+    /* mb_nb contains the number of macroblocks     */
+    /* encoded in the RTP payload.                  */
+    attribute_deprecated
+    void (*rtp_callback)(struct AVCodecContext *avctx, void *data, int size, int mb_nb);
+#endif
+
+#if FF_API_PRIVATE_OPT
+    /** @deprecated use encoder private options instead */
+    attribute_deprecated
+    int rtp_payload_size;   /* The size of the RTP payload: the coder will  */
+                            /* do its best to deliver a chunk with size     */
+                            /* below rtp_payload_size, the chunk will start */
+                            /* with a start code on some codecs like H.263. */
+                            /* This doesn't take account of any particular  */
+                            /* headers inside the transmitted RTP payload.  */
+#endif
+
+#if FF_API_STAT_BITS
+    /* statistics, used for 2-pass encoding */
+    attribute_deprecated
+    int mv_bits;
+    attribute_deprecated
+    int header_bits;
+    attribute_deprecated
+    int i_tex_bits;
+    attribute_deprecated
+    int p_tex_bits;
+    attribute_deprecated
+    int i_count;
+    attribute_deprecated
+    int p_count;
+    attribute_deprecated
+    int skip_count;
+    attribute_deprecated
+    int misc_bits;
+
+    /** @deprecated this field is unused */
+    attribute_deprecated
+    int frame_bits;
+#endif
+
+    /**
+     * pass1 encoding statistics output buffer
+     * - encoding: Set by libavcodec.
+     * - decoding: unused
+     */
+    char *stats_out;
+
+    /**
+     * pass2 encoding statistics input buffer
+     * Concatenated stuff from stats_out of pass1 should be placed here.
+     * - encoding: Allocated/set/freed by user.
+     * - decoding: unused
+     */
+    char *stats_in;
+
+    /**
+     * Work around bugs in encoders which sometimes cannot be detected automatically.
+     * - encoding: Set by user
+     * - decoding: Set by user
+     */
+    int workaround_bugs;
+#define FF_BUG_AUTODETECT       1  ///< autodetection
+#define FF_BUG_XVID_ILACE       4
+#define FF_BUG_UMP4             8
+#define FF_BUG_NO_PADDING       16
+#define FF_BUG_AMV              32
+#define FF_BUG_QPEL_CHROMA      64
+#define FF_BUG_STD_QPEL         128
+#define FF_BUG_QPEL_CHROMA2     256
+#define FF_BUG_DIRECT_BLOCKSIZE 512
+#define FF_BUG_EDGE             1024
+#define FF_BUG_HPEL_CHROMA      2048
+#define FF_BUG_DC_CLIP          4096
+#define FF_BUG_MS               8192 ///< Work around various bugs in Microsoft's broken decoders.
+#define FF_BUG_TRUNCATED       16384
+#define FF_BUG_IEDGE           32768
+
+    /**
+     * strictly follow the standard (MPEG-4, ...).
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     * Setting this to STRICT or higher means the encoder and decoder will
+     * generally do stupid things, whereas setting it to unofficial or lower
+     * will mean the encoder might produce output that is not supported by all
+     * spec-compliant decoders. Decoders don't differentiate between normal,
+     * unofficial and experimental (that is, they always try to decode things
+     * when they can) unless they are explicitly asked to behave stupidly
+     * (=strictly conform to the specs)
+     */
+    int strict_std_compliance;
+#define FF_COMPLIANCE_VERY_STRICT   2 ///< Strictly conform to an older more strict version of the spec or reference software.
+#define FF_COMPLIANCE_STRICT        1 ///< Strictly conform to all the things in the spec no matter what consequences.
+#define FF_COMPLIANCE_NORMAL        0
+#define FF_COMPLIANCE_UNOFFICIAL   -1 ///< Allow unofficial extensions
+#define FF_COMPLIANCE_EXPERIMENTAL -2 ///< Allow nonstandardized experimental things.
+
+    /**
+     * error concealment flags
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    int error_concealment;
+#define FF_EC_GUESS_MVS   1
+#define FF_EC_DEBLOCK     2
+#define FF_EC_FAVOR_INTER 256
+
+    /**
+     * debug
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    int debug;
+#define FF_DEBUG_PICT_INFO   1
+#define FF_DEBUG_RC          2
+#define FF_DEBUG_BITSTREAM   4
+#define FF_DEBUG_MB_TYPE     8
+#define FF_DEBUG_QP          16
+#if FF_API_DEBUG_MV
+/**
+ * @deprecated this option does nothing
+ */
+#define FF_DEBUG_MV          32
+#endif
+#define FF_DEBUG_DCT_COEFF   0x00000040
+#define FF_DEBUG_SKIP        0x00000080
+#define FF_DEBUG_STARTCODE   0x00000100
+#define FF_DEBUG_ER          0x00000400
+#define FF_DEBUG_MMCO        0x00000800
+#define FF_DEBUG_BUGS        0x00001000
+#if FF_API_DEBUG_MV
+#define FF_DEBUG_VIS_QP      0x00002000
+#define FF_DEBUG_VIS_MB_TYPE 0x00004000
+#endif
+#define FF_DEBUG_BUFFERS     0x00008000
+#define FF_DEBUG_THREADS     0x00010000
+#define FF_DEBUG_GREEN_MD    0x00800000
+#define FF_DEBUG_NOMC        0x01000000
+
+#if FF_API_DEBUG_MV
+    /**
+     * debug
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    int debug_mv;
+#define FF_DEBUG_VIS_MV_P_FOR  0x00000001 // visualize forward predicted MVs of P-frames
+#define FF_DEBUG_VIS_MV_B_FOR  0x00000002 // visualize forward predicted MVs of B-frames
+#define FF_DEBUG_VIS_MV_B_BACK 0x00000004 // visualize backward predicted MVs of B-frames
+#endif
+
+    /**
+     * Error recognition; may misdetect some more or less valid parts as errors.
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    int err_recognition;
+
+/**
+ * Verify checksums embedded in the bitstream (could be of either encoded or
+ * decoded data, depending on the codec) and print an error message on mismatch.
+ * If AV_EF_EXPLODE is also set, a mismatching checksum will result in the
+ * decoder returning an error.
+ */
+#define AV_EF_CRCCHECK  (1<<0)
+#define AV_EF_BITSTREAM (1<<1)          ///< detect bitstream specification deviations
+#define AV_EF_BUFFER    (1<<2)          ///< detect improper bitstream length
+#define AV_EF_EXPLODE   (1<<3)          ///< abort decoding on minor error detection
+
+#define AV_EF_IGNORE_ERR (1<<15)        ///< ignore errors and continue
+#define AV_EF_CAREFUL    (1<<16)        ///< consider things that violate the spec, are fast to calculate and have not been seen in the wild as errors
+#define AV_EF_COMPLIANT  (1<<17)        ///< consider all spec non compliances as errors
+#define AV_EF_AGGRESSIVE (1<<18)        ///< consider things that a sane encoder should not do as an error
+
+
+    /**
+     * opaque 64-bit number (generally a PTS) that will be reordered and
+     * output in AVFrame.reordered_opaque
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    int64_t reordered_opaque;
+
+    /**
+     * Hardware accelerator in use
+     * - encoding: unused.
+     * - decoding: Set by libavcodec
+     */
+    const struct AVHWAccel *hwaccel;
+
+    /**
+     * Hardware accelerator context.
+     * For some hardware accelerators, a global context needs to be
+     * provided by the user. In that case, this holds display-dependent
+     * data FFmpeg cannot instantiate itself. Please refer to the
+     * FFmpeg HW accelerator documentation to know how to fill this
+     * is. e.g. for VA API, this is a struct vaapi_context.
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    void *hwaccel_context;
+
+    /**
+     * error
+     * - encoding: Set by libavcodec if flags & AV_CODEC_FLAG_PSNR.
+     * - decoding: unused
+     */
+    uint64_t error[AV_NUM_DATA_POINTERS];
+
+    /**
+     * DCT algorithm, see FF_DCT_* below
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+    int dct_algo;
+#define FF_DCT_AUTO    0
+#define FF_DCT_FASTINT 1
+#define FF_DCT_INT     2
+#define FF_DCT_MMX     3
+#define FF_DCT_ALTIVEC 5
+#define FF_DCT_FAAN    6
+
+    /**
+     * IDCT algorithm, see FF_IDCT_* below.
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    int idct_algo;
+#define FF_IDCT_AUTO          0
+#define FF_IDCT_INT           1
+#define FF_IDCT_SIMPLE        2
+#define FF_IDCT_SIMPLEMMX     3
+#define FF_IDCT_ARM           7
+#define FF_IDCT_ALTIVEC       8
+#define FF_IDCT_SIMPLEARM     10
+#define FF_IDCT_XVID          14
+#define FF_IDCT_SIMPLEARMV5TE 16
+#define FF_IDCT_SIMPLEARMV6   17
+#define FF_IDCT_FAAN          20
+#define FF_IDCT_SIMPLENEON    22
+#define FF_IDCT_NONE          24 /* Used by XvMC to extract IDCT coefficients with FF_IDCT_PERM_NONE */
+#define FF_IDCT_SIMPLEAUTO    128
+
+    /**
+     * bits per sample/pixel from the demuxer (needed for huffyuv).
+     * - encoding: Set by libavcodec.
+     * - decoding: Set by user.
+     */
+     int bits_per_coded_sample;
+
+    /**
+     * Bits per sample/pixel of internal libavcodec pixel/sample format.
+     * - encoding: set by user.
+     * - decoding: set by libavcodec.
+     */
+    int bits_per_raw_sample;
+
+#if FF_API_LOWRES
+    /**
+     * low resolution decoding, 1-> 1/2 size, 2->1/4 size
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+     int lowres;
+#endif
+
+#if FF_API_CODED_FRAME
+    /**
+     * the picture in the bitstream
+     * - encoding: Set by libavcodec.
+     * - decoding: unused
+     *
+     * @deprecated use the quality factor packet side data instead
+     */
+    attribute_deprecated AVFrame *coded_frame;
+#endif
+
+    /**
+     * thread count
+     * is used to decide how many independent tasks should be passed to execute()
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    int thread_count;
+
+    /**
+     * Which multithreading methods to use.
+     * Use of FF_THREAD_FRAME will increase decoding delay by one frame per thread,
+     * so clients which cannot provide future frames should not use it.
+     *
+     * - encoding: Set by user, otherwise the default is used.
+     * - decoding: Set by user, otherwise the default is used.
+     */
+    int thread_type;
+#define FF_THREAD_FRAME   1 ///< Decode more than one frame at once
+#define FF_THREAD_SLICE   2 ///< Decode more than one part of a single frame at once
+
+    /**
+     * Which multithreading methods are in use by the codec.
+     * - encoding: Set by libavcodec.
+     * - decoding: Set by libavcodec.
+     */
+    int active_thread_type;
+
+    /**
+     * Set by the client if its custom get_buffer() callback can be called
+     * synchronously from another thread, which allows faster multithreaded decoding.
+     * draw_horiz_band() will be called from other threads regardless of this setting.
+     * Ignored if the default get_buffer() is used.
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    int thread_safe_callbacks;
+
+    /**
+     * The codec may call this to execute several independent things.
+     * It will return only after finishing all tasks.
+     * The user may replace this with some multithreaded implementation,
+     * the default implementation will execute the parts serially.
+     * @param count the number of things to execute
+     * - encoding: Set by libavcodec, user can override.
+     * - decoding: Set by libavcodec, user can override.
+     */
+    int (*execute)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg), void *arg2, int *ret, int count, int size);
+
+    /**
+     * The codec may call this to execute several independent things.
+     * It will return only after finishing all tasks.
+     * The user may replace this with some multithreaded implementation,
+     * the default implementation will execute the parts serially.
+     * Also see avcodec_thread_init and e.g. the --enable-pthread configure option.
+     * @param c context passed also to func
+     * @param count the number of things to execute
+     * @param arg2 argument passed unchanged to func
+     * @param ret return values of executed functions, must have space for "count" values. May be NULL.
+     * @param func function that will be called count times, with jobnr from 0 to count-1.
+     *             threadnr will be in the range 0 to c->thread_count-1 < MAX_THREADS and so that no
+     *             two instances of func executing at the same time will have the same threadnr.
+     * @return always 0 currently, but code should handle a future improvement where when any call to func
+     *         returns < 0 no further calls to func may be done and < 0 is returned.
+     * - encoding: Set by libavcodec, user can override.
+     * - decoding: Set by libavcodec, user can override.
+     */
+    int (*execute2)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg, int jobnr, int threadnr), void *arg2, int *ret, int count);
+
+    /**
+     * noise vs. sse weight for the nsse comparison function
+     * - encoding: Set by user.
+     * - decoding: unused
+     */
+     int nsse_weight;
+
+    /**
+     * profile
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+     int profile;
+#define FF_PROFILE_UNKNOWN -99
+#define FF_PROFILE_RESERVED -100
+
+#define FF_PROFILE_AAC_MAIN 0
+#define FF_PROFILE_AAC_LOW  1
+#define FF_PROFILE_AAC_SSR  2
+#define FF_PROFILE_AAC_LTP  3
+#define FF_PROFILE_AAC_HE   4
+#define FF_PROFILE_AAC_HE_V2 28
+#define FF_PROFILE_AAC_LD   22
+#define FF_PROFILE_AAC_ELD  38
+#define FF_PROFILE_MPEG2_AAC_LOW 128
+#define FF_PROFILE_MPEG2_AAC_HE  131
+
+#define FF_PROFILE_DNXHD         0
+#define FF_PROFILE_DNXHR_LB      1
+#define FF_PROFILE_DNXHR_SQ      2
+#define FF_PROFILE_DNXHR_HQ      3
+#define FF_PROFILE_DNXHR_HQX     4
+#define FF_PROFILE_DNXHR_444     5
+
+#define FF_PROFILE_DTS         20
+#define FF_PROFILE_DTS_ES      30
+#define FF_PROFILE_DTS_96_24   40
+#define FF_PROFILE_DTS_HD_HRA  50
+#define FF_PROFILE_DTS_HD_MA   60
+#define FF_PROFILE_DTS_EXPRESS 70
+
+#define FF_PROFILE_MPEG2_422    0
+#define FF_PROFILE_MPEG2_HIGH   1
+#define FF_PROFILE_MPEG2_SS     2
+#define FF_PROFILE_MPEG2_SNR_SCALABLE  3
+#define FF_PROFILE_MPEG2_MAIN   4
+#define FF_PROFILE_MPEG2_SIMPLE 5
+
+#define FF_PROFILE_H264_CONSTRAINED  (1<<9)  // 8+1; constraint_set1_flag
+#define FF_PROFILE_H264_INTRA        (1<<11) // 8+3; constraint_set3_flag
+
+#define FF_PROFILE_H264_BASELINE             66
+#define FF_PROFILE_H264_CONSTRAINED_BASELINE (66|FF_PROFILE_H264_CONSTRAINED)
+#define FF_PROFILE_H264_MAIN                 77
+#define FF_PROFILE_H264_EXTENDED             88
+#define FF_PROFILE_H264_HIGH                 100
+#define FF_PROFILE_H264_HIGH_10              110
+#define FF_PROFILE_H264_HIGH_10_INTRA        (110|FF_PROFILE_H264_INTRA)
+#define FF_PROFILE_H264_MULTIVIEW_HIGH       118
+#define FF_PROFILE_H264_HIGH_422             122
+#define FF_PROFILE_H264_HIGH_422_INTRA       (122|FF_PROFILE_H264_INTRA)
+#define FF_PROFILE_H264_STEREO_HIGH          128
+#define FF_PROFILE_H264_HIGH_444             144
+#define FF_PROFILE_H264_HIGH_444_PREDICTIVE  244
+#define FF_PROFILE_H264_HIGH_444_INTRA       (244|FF_PROFILE_H264_INTRA)
+#define FF_PROFILE_H264_CAVLC_444            44
+
+#define FF_PROFILE_VC1_SIMPLE   0
+#define FF_PROFILE_VC1_MAIN     1
+#define FF_PROFILE_VC1_COMPLEX  2
+#define FF_PROFILE_VC1_ADVANCED 3
+
+#define FF_PROFILE_MPEG4_SIMPLE                     0
+#define FF_PROFILE_MPEG4_SIMPLE_SCALABLE            1
+#define FF_PROFILE_MPEG4_CORE                       2
+#define FF_PROFILE_MPEG4_MAIN                       3
+#define FF_PROFILE_MPEG4_N_BIT                      4
+#define FF_PROFILE_MPEG4_SCALABLE_TEXTURE           5
+#define FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION      6
+#define FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE     7
+#define FF_PROFILE_MPEG4_HYBRID                     8
+#define FF_PROFILE_MPEG4_ADVANCED_REAL_TIME         9
+#define FF_PROFILE_MPEG4_CORE_SCALABLE             10
+#define FF_PROFILE_MPEG4_ADVANCED_CODING           11
+#define FF_PROFILE_MPEG4_ADVANCED_CORE             12
+#define FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13
+#define FF_PROFILE_MPEG4_SIMPLE_STUDIO             14
+#define FF_PROFILE_MPEG4_ADVANCED_SIMPLE           15
+
+#define FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0   1
+#define FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1   2
+#define FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION  32768
+#define FF_PROFILE_JPEG2000_DCINEMA_2K              3
+#define FF_PROFILE_JPEG2000_DCINEMA_4K              4
+
+#define FF_PROFILE_VP9_0                            0
+#define FF_PROFILE_VP9_1                            1
+#define FF_PROFILE_VP9_2                            2
+#define FF_PROFILE_VP9_3                            3
+
+#define FF_PROFILE_HEVC_MAIN                        1
+#define FF_PROFILE_HEVC_MAIN_10                     2
+#define FF_PROFILE_HEVC_MAIN_STILL_PICTURE          3
+#define FF_PROFILE_HEVC_REXT                        4
+
+#define FF_PROFILE_AV1_MAIN                         0
+#define FF_PROFILE_AV1_HIGH                         1
+#define FF_PROFILE_AV1_PROFESSIONAL                 2
+
+#define FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT            0xc0
+#define FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT 0xc1
+#define FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT         0xc2
+#define FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS                0xc3
+#define FF_PROFILE_MJPEG_JPEG_LS                         0xf7
+
+#define FF_PROFILE_SBC_MSBC                         1
+
+    /**
+     * level
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+     int level;
+#define FF_LEVEL_UNKNOWN -99
+
+    /**
+     * Skip loop filtering for selected frames.
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    enum AVDiscard skip_loop_filter;
+
+    /**
+     * Skip IDCT/dequantization for selected frames.
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    enum AVDiscard skip_idct;
+
+    /**
+     * Skip decoding for selected frames.
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+    enum AVDiscard skip_frame;
+
+    /**
+     * Header containing style information for text subtitles.
+     * For SUBTITLE_ASS subtitle type, it should contain the whole ASS
+     * [Script Info] and [V4+ Styles] section, plus the [Events] line and
+     * the Format line following. It shouldn't include any Dialogue line.
+     * - encoding: Set/allocated/freed by user (before avcodec_open2())
+     * - decoding: Set/allocated/freed by libavcodec (by avcodec_open2())
+     */
+    uint8_t *subtitle_header;
+    int subtitle_header_size;
+
+#if FF_API_VBV_DELAY
+    /**
+     * VBV delay coded in the last frame (in periods of a 27 MHz clock).
+     * Used for compliant TS muxing.
+     * - encoding: Set by libavcodec.
+     * - decoding: unused.
+     * @deprecated this value is now exported as a part of
+     * AV_PKT_DATA_CPB_PROPERTIES packet side data
+     */
+    attribute_deprecated
+    uint64_t vbv_delay;
+#endif
+
+#if FF_API_SIDEDATA_ONLY_PKT
+    /**
+     * Encoding only and set by default. Allow encoders to output packets
+     * that do not contain any encoded data, only side data.
+     *
+     * Some encoders need to output such packets, e.g. to update some stream
+     * parameters at the end of encoding.
+     *
+     * @deprecated this field disables the default behaviour and
+     *             it is kept only for compatibility.
+     */
+    attribute_deprecated
+    int side_data_only_packets;
+#endif
+
+    /**
+     * Audio only. The number of "priming" samples (padding) inserted by the
+     * encoder at the beginning of the audio. I.e. this number of leading
+     * decoded samples must be discarded by the caller to get the original audio
+     * without leading padding.
+     *
+     * - decoding: unused
+     * - encoding: Set by libavcodec. The timestamps on the output packets are
+     *             adjusted by the encoder so that they always refer to the
+     *             first sample of the data actually contained in the packet,
+     *             including any added padding.  E.g. if the timebase is
+     *             1/samplerate and the timestamp of the first input sample is
+     *             0, the timestamp of the first output packet will be
+     *             -initial_padding.
+     */
+    int initial_padding;
+
+    /**
+     * - decoding: For codecs that store a framerate value in the compressed
+     *             bitstream, the decoder may export it here. { 0, 1} when
+     *             unknown.
+     * - encoding: May be used to signal the framerate of CFR content to an
+     *             encoder.
+     */
+    AVRational framerate;
+
+    /**
+     * Nominal unaccelerated pixel format, see AV_PIX_FMT_xxx.
+     * - encoding: unused.
+     * - decoding: Set by libavcodec before calling get_format()
+     */
+    enum AVPixelFormat sw_pix_fmt;
+
+    /**
+     * Timebase in which pkt_dts/pts and AVPacket.dts/pts are.
+     * - encoding unused.
+     * - decoding set by user.
+     */
+    AVRational pkt_timebase;
+
+    /**
+     * AVCodecDescriptor
+     * - encoding: unused.
+     * - decoding: set by libavcodec.
+     */
+    const AVCodecDescriptor *codec_descriptor;
+
+#if !FF_API_LOWRES
+    /**
+     * low resolution decoding, 1-> 1/2 size, 2->1/4 size
+     * - encoding: unused
+     * - decoding: Set by user.
+     */
+     int lowres;
+#endif
+
+    /**
+     * Current statistics for PTS correction.
+     * - decoding: maintained and used by libavcodec, not intended to be used by user apps
+     * - encoding: unused
+     */
+    int64_t pts_correction_num_faulty_pts; /// Number of incorrect PTS values so far
+    int64_t pts_correction_num_faulty_dts; /// Number of incorrect DTS values so far
+    int64_t pts_correction_last_pts;       /// PTS of the last frame
+    int64_t pts_correction_last_dts;       /// DTS of the last frame
+
+    /**
+     * Character encoding of the input subtitles file.
+     * - decoding: set by user
+     * - encoding: unused
+     */
+    char *sub_charenc;
+
+    /**
+     * Subtitles character encoding mode. Formats or codecs might be adjusting
+     * this setting (if they are doing the conversion themselves for instance).
+     * - decoding: set by libavcodec
+     * - encoding: unused
+     */
+    int sub_charenc_mode;
+#define FF_SUB_CHARENC_MODE_DO_NOTHING  -1  ///< do nothing (demuxer outputs a stream supposed to be already in UTF-8, or the codec is bitmap for instance)
+#define FF_SUB_CHARENC_MODE_AUTOMATIC    0  ///< libavcodec will select the mode itself
+#define FF_SUB_CHARENC_MODE_PRE_DECODER  1  ///< the AVPacket data needs to be recoded to UTF-8 before being fed to the decoder, requires iconv
+#define FF_SUB_CHARENC_MODE_IGNORE       2  ///< neither convert the subtitles, nor check them for valid UTF-8
+
+    /**
+     * Skip processing alpha if supported by codec.
+     * Note that if the format uses pre-multiplied alpha (common with VP6,
+     * and recommended due to better video quality/compression)
+     * the image will look as if alpha-blended onto a black background.
+     * However for formats that do not use pre-multiplied alpha
+     * there might be serious artefacts (though e.g. libswscale currently
+     * assumes pre-multiplied alpha anyway).
+     *
+     * - decoding: set by user
+     * - encoding: unused
+     */
+    int skip_alpha;
+
+    /**
+     * Number of samples to skip after a discontinuity
+     * - decoding: unused
+     * - encoding: set by libavcodec
+     */