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
+     */
+    int seek_preroll;
+
+#if !FF_API_DEBUG_MV
+    /**
+     * debug motion vectors
+     * - 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
+
+    /**
+     * custom intra quantization matrix
+     * - encoding: Set by user, can be NULL.
+     * - decoding: unused.
+     */
+    uint16_t *chroma_intra_matrix;
+
+    /**
+     * dump format separator.
+     * can be ", " or "\n      " or anything else
+     * - encoding: Set by user.
+     * - decoding: Set by user.
+     */
+    uint8_t *dump_separator;
+
+    /**
+     * ',' separated list of allowed decoders.
+     * If NULL then all are allowed
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    char *codec_whitelist;
+
+    /**
+     * Properties of the stream that gets decoded
+     * - encoding: unused
+     * - decoding: set by libavcodec
+     */
+    unsigned properties;
+#define FF_CODEC_PROPERTY_LOSSLESS        0x00000001
+#define FF_CODEC_PROPERTY_CLOSED_CAPTIONS 0x00000002
+
+    /**
+     * Additional data associated with the entire coded stream.
+     *
+     * - decoding: unused
+     * - encoding: may be set by libavcodec after avcodec_open2().
+     */
+    AVPacketSideData *coded_side_data;
+    int            nb_coded_side_data;
+
+    /**
+     * A reference to the AVHWFramesContext describing the input (for encoding)
+     * or output (decoding) frames. The reference is set by the caller and
+     * afterwards owned (and freed) by libavcodec - it should never be read by
+     * the caller after being set.
+     *
+     * - decoding: This field should be set by the caller from the get_format()
+     *             callback. The previous reference (if any) will always be
+     *             unreffed by libavcodec before the get_format() call.
+     *
+     *             If the default get_buffer2() is used with a hwaccel pixel
+     *             format, then this AVHWFramesContext will be used for
+     *             allocating the frame buffers.
+     *
+     * - encoding: For hardware encoders configured to use a hwaccel pixel
+     *             format, this field should be set by the caller to a reference
+     *             to the AVHWFramesContext describing input frames.
+     *             AVHWFramesContext.format must be equal to
+     *             AVCodecContext.pix_fmt.
+     *
+     *             This field should be set before avcodec_open2() is called.
+     */
+    AVBufferRef *hw_frames_ctx;
+
+    /**
+     * Control the form of AVSubtitle.rects[N]->ass
+     * - decoding: set by user
+     * - encoding: unused
+     */
+    int sub_text_format;
+#define FF_SUB_TEXT_FMT_ASS              0
+#if FF_API_ASS_TIMING
+#define FF_SUB_TEXT_FMT_ASS_WITH_TIMINGS 1
+#endif
+
+    /**
+     * Audio only. The amount of padding (in samples) appended by the encoder to
+     * the end of the audio. I.e. this number of decoded samples must be
+     * discarded by the caller from the end of the stream to get the original
+     * audio without any trailing padding.
+     *
+     * - decoding: unused
+     * - encoding: unused
+     */
+    int trailing_padding;
+
+    /**
+     * The number of pixels per image to maximally accept.
+     *
+     * - decoding: set by user
+     * - encoding: set by user
+     */
+    int64_t max_pixels;
+
+    /**
+     * A reference to the AVHWDeviceContext describing the device which will
+     * be used by a hardware encoder/decoder.  The reference is set by the
+     * caller and afterwards owned (and freed) by libavcodec.
+     *
+     * This should be used if either the codec device does not require
+     * hardware frames or any that are used are to be allocated internally by
+     * libavcodec.  If the user wishes to supply any of the frames used as
+     * encoder input or decoder output then hw_frames_ctx should be used
+     * instead.  When hw_frames_ctx is set in get_format() for a decoder, this
+     * field will be ignored while decoding the associated stream segment, but
+     * may again be used on a following one after another get_format() call.
+     *
+     * For both encoders and decoders this field should be set before
+     * avcodec_open2() is called and must not be written to thereafter.
+     *
+     * Note that some decoders may require this field to be set initially in
+     * order to support hw_frames_ctx at all - in that case, all frames
+     * contexts used must be created on the same device.
+     */
+    AVBufferRef *hw_device_ctx;
+
+    /**
+     * Bit set of AV_HWACCEL_FLAG_* flags, which affect hardware accelerated
+     * decoding (if active).
+     * - encoding: unused
+     * - decoding: Set by user (either before avcodec_open2(), or in the
+     *             AVCodecContext.get_format callback)
+     */
+    int hwaccel_flags;
+
+    /**
+     * Video decoding only. Certain video codecs support cropping, meaning that
+     * only a sub-rectangle of the decoded frame is intended for display.  This
+     * option controls how cropping is handled by libavcodec.
+     *
+     * When set to 1 (the default), libavcodec will apply cropping internally.
+     * I.e. it will modify the output frame width/height fields and offset the
+     * data pointers (only by as much as possible while preserving alignment, or
+     * by the full amount if the AV_CODEC_FLAG_UNALIGNED flag is set) so that
+     * the frames output by the decoder refer only to the cropped area. The
+     * crop_* fields of the output frames will be zero.
+     *
+     * When set to 0, the width/height fields of the output frames will be set
+     * to the coded dimensions and the crop_* fields will describe the cropping
+     * rectangle. Applying the cropping is left to the caller.
+     *
+     * @warning When hardware acceleration with opaque output frames is used,
+     * libavcodec is unable to apply cropping from the top/left border.
+     *
+     * @note when this option is set to zero, the width/height fields of the
+     * AVCodecContext and output AVFrames have different meanings. The codec
+     * context fields store display dimensions (with the coded dimensions in
+     * coded_width/height), while the frame fields store the coded dimensions
+     * (with the display dimensions being determined by the crop_* fields).
+     */
+    int apply_cropping;
+
+    /*
+     * Video decoding only.  Sets the number of extra hardware frames which
+     * the decoder will allocate for use by the caller.  This must be set
+     * before avcodec_open2() is called.
+     *
+     * Some hardware decoders require all frames that they will use for
+     * output to be defined in advance before decoding starts.  For such
+     * decoders, the hardware frame pool must therefore be of a fixed size.
+     * The extra frames set here are on top of any number that the decoder
+     * needs internally in order to operate normally (for example, frames
+     * used as reference pictures).
+     */
+    int extra_hw_frames;
+} AVCodecContext;
+
+#if FF_API_CODEC_GET_SET
+/**
+ * Accessors for some AVCodecContext fields. These used to be provided for ABI
+ * compatibility, and do not need to be used anymore.
+ */
+attribute_deprecated
+AVRational av_codec_get_pkt_timebase         (const AVCodecContext *avctx);
+attribute_deprecated
+void       av_codec_set_pkt_timebase         (AVCodecContext *avctx, AVRational val);
+
+attribute_deprecated
+const AVCodecDescriptor *av_codec_get_codec_descriptor(const AVCodecContext *avctx);
+attribute_deprecated
+void                     av_codec_set_codec_descriptor(AVCodecContext *avctx, const AVCodecDescriptor *desc);
+
+attribute_deprecated
+unsigned av_codec_get_codec_properties(const AVCodecContext *avctx);
+
+#if FF_API_LOWRES
+attribute_deprecated
+int  av_codec_get_lowres(const AVCodecContext *avctx);
+attribute_deprecated
+void av_codec_set_lowres(AVCodecContext *avctx, int val);
+#endif
+
+attribute_deprecated
+int  av_codec_get_seek_preroll(const AVCodecContext *avctx);
+attribute_deprecated
+void av_codec_set_seek_preroll(AVCodecContext *avctx, int val);
+
+attribute_deprecated
+uint16_t *av_codec_get_chroma_intra_matrix(const AVCodecContext *avctx);
+attribute_deprecated
+void av_codec_set_chroma_intra_matrix(AVCodecContext *avctx, uint16_t *val);
+#endif
+
+/**
+ * AVProfile.
+ */
+typedef struct AVProfile {
+    int profile;
+    const char *name; ///< short name for the profile
+} AVProfile;
+
+enum {
+    /**
+     * The codec supports this format via the hw_device_ctx interface.
+     *
+     * When selecting this format, AVCodecContext.hw_device_ctx should
+     * have been set to a device of the specified type before calling
+     * avcodec_open2().
+     */
+    AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX = 0x01,
+    /**
+     * The codec supports this format via the hw_frames_ctx interface.
+     *
+     * When selecting this format for a decoder,
+     * AVCodecContext.hw_frames_ctx should be set to a suitable frames
+     * context inside the get_format() callback.  The frames context
+     * must have been created on a device of the specified type.
+     */
+    AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX = 0x02,
+    /**
+     * The codec supports this format by some internal method.
+     *
+     * This format can be selected without any additional configuration -
+     * no device or frames context is required.
+     */
+    AV_CODEC_HW_CONFIG_METHOD_INTERNAL      = 0x04,
+    /**
+     * The codec supports this format by some ad-hoc method.
+     *
+     * Additional settings and/or function calls are required.  See the
+     * codec-specific documentation for details.  (Methods requiring
+     * this sort of configuration are deprecated and others should be
+     * used in preference.)
+     */
+    AV_CODEC_HW_CONFIG_METHOD_AD_HOC        = 0x08,
+};
+
+typedef struct AVCodecHWConfig {
+    /**
+     * A hardware pixel format which the codec can use.
+     */
+    enum AVPixelFormat pix_fmt;
+    /**
+     * Bit set of AV_CODEC_HW_CONFIG_METHOD_* flags, describing the possible
+     * setup methods which can be used with this configuration.
+     */
+    int methods;
+    /**
+     * The device type associated with the configuration.
+     *
+     * Must be set for AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX and
+     * AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, otherwise unused.
+     */
+    enum AVHWDeviceType device_type;
+} AVCodecHWConfig;
+
+typedef struct AVCodecDefault AVCodecDefault;
+
+struct AVSubtitle;
+
+/**
+ * AVCodec.
+ */
+typedef struct AVCodec {
+    /**
+     * Name of the codec implementation.
+     * The name is globally unique among encoders and among decoders (but an
+     * encoder and a decoder can share the same name).
+     * This is the primary way to find a codec from the user perspective.
+     */
+    const char *name;
+    /**
+     * Descriptive name for the codec, meant to be more human readable than name.
+     * You should use the NULL_IF_CONFIG_SMALL() macro to define it.
+     */
+    const char *long_name;
+    enum AVMediaType type;
+    enum AVCodecID id;
+    /**
+     * Codec capabilities.
+     * see AV_CODEC_CAP_*
+     */
+    int capabilities;
+    const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
+    const enum AVPixelFormat *pix_fmts;     ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
+    const int *supported_samplerates;       ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
+    const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
+    const uint64_t *channel_layouts;         ///< array of support channel layouts, or NULL if unknown. array is terminated by 0
+    uint8_t max_lowres;                     ///< maximum value for lowres supported by the decoder
+    const AVClass *priv_class;              ///< AVClass for the private context
+    const AVProfile *profiles;              ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
+
+    /**
+     * Group name of the codec implementation.
+     * This is a short symbolic name of the wrapper backing this codec. A
+     * wrapper uses some kind of external implementation for the codec, such
+     * as an external library, or a codec implementation provided by the OS or
+     * the hardware.
+     * If this field is NULL, this is a builtin, libavcodec native codec.
+     * If non-NULL, this will be the suffix in AVCodec.name in most cases
+     * (usually AVCodec.name will be of the form "<codec_name>_<wrapper_name>").
+     */
+    const char *wrapper_name;
+
+    /*****************************************************************
+     * No fields below this line are part of the public API. They
+     * may not be used outside of libavcodec and can be changed and
+     * removed at will.
+     * New public fields should be added right above.
+     *****************************************************************
+     */
+    int priv_data_size;
+    struct AVCodec *next;
+    /**
+     * @name Frame-level threading support functions
+     * @{
+     */
+    /**
+     * If defined, called on thread contexts when they are created.
+     * If the codec allocates writable tables in init(), re-allocate them here.
+     * priv_data will be set to a copy of the original.
+     */
+    int (*init_thread_copy)(AVCodecContext *);
+    /**
+     * Copy necessary context variables from a previous thread context to the current one.
+     * If not defined, the next thread will start automatically; otherwise, the codec
+     * must call ff_thread_finish_setup().
+     *
+     * dst and src will (rarely) point to the same context, in which case memcpy should be skipped.
+     */
+    int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src);
+    /** @} */
+
+    /**
+     * Private codec-specific defaults.
+     */
+    const AVCodecDefault *defaults;
+
+    /**
+     * Initialize codec static data, called from avcodec_register().
+     *
+     * This is not intended for time consuming operations as it is
+     * run for every codec regardless of that codec being used.
+     */
+    void (*init_static_data)(struct AVCodec *codec);
+
+    int (*init)(AVCodecContext *);
+    int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size,
+                      const struct AVSubtitle *sub);
+    /**
+     * Encode data to an AVPacket.
+     *
+     * @param      avctx          codec context
+     * @param      avpkt          output AVPacket (may contain a user-provided buffer)
+     * @param[in]  frame          AVFrame containing the raw data to be encoded
+     * @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a
+     *                            non-empty packet was returned in avpkt.
+     * @return 0 on success, negative error code on failure
+     */
+    int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame,
+                   int *got_packet_ptr);
+    int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
+    int (*close)(AVCodecContext *);
+    /**
+     * Encode API with decoupled packet/frame dataflow. The API is the
+     * same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except
+     * that:
+     * - never called if the codec is closed or the wrong type,
+     * - if AV_CODEC_CAP_DELAY is not set, drain frames are never sent,
+     * - only one drain frame is ever passed down,
+     */
+    int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);
+    int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);
+
+    /**
+     * Decode API with decoupled packet/frame dataflow. This function is called
+     * to get one output frame. It should call ff_decode_get_packet() to obtain
+     * input data.
+     */
+    int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
+    /**
+     * Flush buffers.
+     * Will be called when seeking
+     */
+    void (*flush)(AVCodecContext *);
+    /**
+     * Internal codec capabilities.
+     * See FF_CODEC_CAP_* in internal.h
+     */
+    int caps_internal;
+
+    /**
+     * Decoding only, a comma-separated list of bitstream filters to apply to
+     * packets before decoding.
+     */
+    const char *bsfs;
+
+    /**
+     * Array of pointers to hardware configurations supported by the codec,
+     * or NULL if no hardware supported.  The array is terminated by a NULL
+     * pointer.
+     *
+     * The user can only access this field via avcodec_get_hw_config().
+     */
+    const struct AVCodecHWConfigInternal **hw_configs;
+} AVCodec;
+
+#if FF_API_CODEC_GET_SET
+attribute_deprecated
+int av_codec_get_max_lowres(const AVCodec *codec);
+#endif
+
+struct MpegEncContext;
+
+/**
+ * Retrieve supported hardware configurations for a codec.
+ *
+ * Values of index from zero to some maximum return the indexed configuration
+ * descriptor; all other values return NULL.  If the codec does not support
+ * any hardware configurations then it will always return NULL.
+ */
+const AVCodecHWConfig *avcodec_get_hw_config(const AVCodec *codec, int index);
+
+/**
+ * @defgroup lavc_hwaccel AVHWAccel
+ *
+ * @note  Nothing in this structure should be accessed by the user.  At some
+ *        point in future it will not be externally visible at all.
+ *
+ * @{
+ */
+typedef struct AVHWAccel {
+    /**
+     * Name of the hardware accelerated codec.
+     * The name is globally unique among encoders and among decoders (but an
+     * encoder and a decoder can share the same name).
+     */
+    const char *name;
+
+    /**
+     * Type of codec implemented by the hardware accelerator.
+     *
+     * See AVMEDIA_TYPE_xxx
+     */
+    enum AVMediaType type;
+
+    /**
+     * Codec implemented by the hardware accelerator.
+     *
+     * See AV_CODEC_ID_xxx
+     */
+    enum AVCodecID id;
+
+    /**
+     * Supported pixel format.
+     *
+     * Only hardware accelerated formats are supported here.
+     */
+    enum AVPixelFormat pix_fmt;
+
+    /**
+     * Hardware accelerated codec capabilities.
+     * see AV_HWACCEL_CODEC_CAP_*
+     */
+    int capabilities;
+
+    /*****************************************************************
+     * No fields below this line are part of the public API. They
+     * may not be used outside of libavcodec and can be changed and
+     * removed at will.
+     * New public fields should be added right above.
+     *****************************************************************
+     */
+
+    /**
+     * Allocate a custom buffer
+     */
+    int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame);
+
+    /**
+     * Called at the beginning of each frame or field picture.
+     *
+     * Meaningful frame information (codec specific) is guaranteed to
+     * be parsed at this point. This function is mandatory.
+     *
+     * Note that buf can be NULL along with buf_size set to 0.
+     * Otherwise, this means the whole frame is available at this point.
+     *
+     * @param avctx the codec context
+     * @param buf the frame data buffer base
+     * @param buf_size the size of the frame in bytes
+     * @return zero if successful, a negative value otherwise
+     */
+    int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size);
+
+    /**
+     * Callback for parameter data (SPS/PPS/VPS etc).
+     *
+     * Useful for hardware decoders which keep persistent state about the
+     * video parameters, and need to receive any changes to update that state.
+     *
+     * @param avctx the codec context
+     * @param type the nal unit type
+     * @param buf the nal unit data buffer
+     * @param buf_size the size of the nal unit in bytes
+     * @return zero if successful, a negative value otherwise
+     */
+    int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size);
+
+    /**
+     * Callback for each slice.
+     *
+     * Meaningful slice information (codec specific) is guaranteed to
+     * be parsed at this point. This function is mandatory.
+     * The only exception is XvMC, that works on MB level.
+     *
+     * @param avctx the codec context
+     * @param buf the slice data buffer base
+     * @param buf_size the size of the slice in bytes
+     * @return zero if successful, a negative value otherwise
+     */
+    int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size);
+
+    /**
+     * Called at the end of each frame or field picture.
+     *
+     * The whole picture is parsed at this point and can now be sent
+     * to the hardware accelerator. This function is mandatory.
+     *
+     * @param avctx the codec context
+     * @return zero if successful, a negative value otherwise
+     */
+    int (*end_frame)(AVCodecContext *avctx);
+
+    /**
+     * Size of per-frame hardware accelerator private data.
+     *
+     * Private data is allocated with av_mallocz() before
+     * AVCodecContext.get_buffer() and deallocated after
+     * AVCodecContext.release_buffer().
+     */
+    int frame_priv_data_size;
+
+    /**
+     * Called for every Macroblock in a slice.
+     *
+     * XvMC uses it to replace the ff_mpv_reconstruct_mb().
+     * Instead of decoding to raw picture, MB parameters are
+     * stored in an array provided by the video driver.
+     *
+     * @param s the mpeg context
+     */
+    void (*decode_mb)(struct MpegEncContext *s);
+
+    /**
+     * Initialize the hwaccel private data.
+     *
+     * This will be called from ff_get_format(), after hwaccel and
+     * hwaccel_context are set and the hwaccel private data in AVCodecInternal
+     * is allocated.
+     */
+    int (*init)(AVCodecContext *avctx);
+
+    /**
+     * Uninitialize the hwaccel private data.
+     *
+     * This will be called from get_format() or avcodec_close(), after hwaccel
+     * and hwaccel_context are already uninitialized.
+     */
+    int (*uninit)(AVCodecContext *avctx);
+
+    /**
+     * Size of the private data to allocate in
+     * AVCodecInternal.hwaccel_priv_data.
+     */
+    int priv_data_size;
+
+    /**
+     * Internal hwaccel capabilities.
+     */
+    int caps_internal;
+
+    /**
+     * Fill the given hw_frames context with current codec parameters. Called
+     * from get_format. Refer to avcodec_get_hw_frames_parameters() for
+     * details.
+     *
+     * This CAN be called before AVHWAccel.init is called, and you must assume
+     * that avctx->hwaccel_priv_data is invalid.
+     */
+    int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx);
+} AVHWAccel;
+
+/**
+ * HWAccel is experimental and is thus avoided in favor of non experimental
+ * codecs
+ */
+#define AV_HWACCEL_CODEC_CAP_EXPERIMENTAL 0x0200
+
+/**
+ * Hardware acceleration should be used for decoding even if the codec level
+ * used is unknown or higher than the maximum supported level reported by the
+ * hardware driver.
+ *
+ * It's generally a good idea to pass this flag unless you have a specific
+ * reason not to, as hardware tends to under-report supported levels.
+ */
+#define AV_HWACCEL_FLAG_IGNORE_LEVEL (1 << 0)
+
+/**
+ * Hardware acceleration can output YUV pixel formats with a different chroma
+ * sampling than 4:2:0 and/or other than 8 bits per component.
+ */
+#define AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH (1 << 1)
+
+/**
+ * Hardware acceleration should still be attempted for decoding when the
+ * codec profile does not match the reported capabilities of the hardware.
+ *
+ * For example, this can be used to try to decode baseline profile H.264
+ * streams in hardware - it will often succeed, because many streams marked
+ * as baseline profile actually conform to constrained baseline profile.
+ *
+ * @warning If the stream is actually not supported then the behaviour is
+ *          undefined, and may include returning entirely incorrect output
+ *          while indicating success.
+ */
+#define AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH (1 << 2)
+
+/**
+ * @}
+ */
+
+#if FF_API_AVPICTURE
+/**
+ * @defgroup lavc_picture AVPicture
+ *
+ * Functions for working with AVPicture
+ * @{
+ */
+
+/**
+ * Picture data structure.
+ *
+ * Up to four components can be stored into it, the last component is
+ * alpha.
+ * @deprecated use AVFrame or imgutils functions instead
+ */
+typedef struct AVPicture {
+    attribute_deprecated
+    uint8_t *data[AV_NUM_DATA_POINTERS];    ///< pointers to the image data planes
+    attribute_deprecated
+    int linesize[AV_NUM_DATA_POINTERS];     ///< number of bytes per line
+} AVPicture;
+
+/**
+ * @}
+ */
+#endif
+
+enum AVSubtitleType {
+    SUBTITLE_NONE,
+
+    SUBTITLE_BITMAP,                ///< A bitmap, pict will be set
+
+    /**
+     * Plain text, the text field must be set by the decoder and is
+     * authoritative. ass and pict fields may contain approximations.
+     */
+    SUBTITLE_TEXT,
+
+    /**
+     * Formatted text, the ass field must be set by the decoder and is
+     * authoritative. pict and text fields may contain approximations.
+     */
+    SUBTITLE_ASS,
+};
+
+#define AV_SUBTITLE_FLAG_FORCED 0x00000001
+
+typedef struct AVSubtitleRect {
+    int x;         ///< top left corner  of pict, undefined when pict is not set
+    int y;         ///< top left corner  of pict, undefined when pict is not set
+    int w;         ///< width            of pict, undefined when pict is not set
+    int h;         ///< height           of pict, undefined when pict is not set
+    int nb_colors; ///< number of colors in pict, undefined when pict is not set
+
+#if FF_API_AVPICTURE
+    /**
+     * @deprecated unused
+     */
+    attribute_deprecated
+    AVPicture pict;
+#endif
+    /**
+     * data+linesize for the bitmap of this subtitle.
+     * Can be set for text/ass as well once they are rendered.
+     */
+    uint8_t *data[4];
+    int linesize[4];
+
+    enum AVSubtitleType type;
+
+    char *text;                     ///< 0 terminated plain UTF-8 text
+
+    /**
+     * 0 terminated ASS/SSA compatible event line.
+     * The presentation of this is unaffected by the other values in this
+     * struct.
+     */
+    char *ass;
+
+    int flags;
+} AVSubtitleRect;
+
+typedef struct AVSubtitle {
+    uint16_t format; /* 0 = graphics */
+    uint32_t start_display_time; /* relative to packet pts, in ms */
+    uint32_t end_display_time; /* relative to packet pts, in ms */
+    unsigned num_rects;
+    AVSubtitleRect **rects;
+    int64_t pts;    ///< Same as packet pts, in AV_TIME_BASE
+} AVSubtitle;
+
+/**
+ * This struct describes the properties of an encoded stream.
+ *
+ * sizeof(AVCodecParameters) is not a part of the public ABI, this struct must
+ * be allocated with avcodec_parameters_alloc() and freed with
+ * avcodec_parameters_free().
+ */
+typedef struct AVCodecParameters {
+    /**
+     * General type of the encoded data.
+     */
+    enum AVMediaType codec_type;
+    /**
+     * Specific type of the encoded data (the codec used).
+     */
+    enum AVCodecID   codec_id;
+    /**
+     * Additional information about the codec (corresponds to the AVI FOURCC).
+     */
+    uint32_t         codec_tag;
+
+    /**
+     * Extra binary data needed for initializing the decoder, codec-dependent.
+     *
+     * Must be allocated with av_malloc() and will be freed by
+     * avcodec_parameters_free(). The allocated size of extradata must be at
+     * least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding
+     * bytes zeroed.
+     */
+    uint8_t *extradata;
+    /**
+     * Size of the extradata content in bytes.
+     */
+    int      extradata_size;
+
+    /**
+     * - video: the pixel format, the value corresponds to enum AVPixelFormat.
+     * - audio: the sample format, the value corresponds to enum AVSampleFormat.
+     */
+    int format;
+
+    /**
+     * The average bitrate of the encoded data (in bits per second).
+     */
+    int64_t bit_rate;
+
+    /**
+     * The number of bits per sample in the codedwords.
+     *
+     * This is basically the bitrate per sample. It is mandatory for a bunch of
+     * formats to actually decode them. It's the number of bits for one sample in
+     * the actual coded bitstream.
+     *
+     * This could be for example 4 for ADPCM
+     * For PCM formats this matches bits_per_raw_sample
+     * Can be 0
+     */
+    int bits_per_coded_sample;
+
+    /**
+     * This is the number of valid bits in each output sample. If the
+     * sample format has more bits, the least significant bits are additional
+     * padding bits, which are always 0. Use right shifts to reduce the sample
+     * to its actual size. For example, audio formats with 24 bit samples will
+     * have bits_per_raw_sample set to 24, and format set to AV_SAMPLE_FMT_S32.
+     * To get the original sample use "(int32_t)sample >> 8"."
+     *
+     * For ADPCM this might be 12 or 16 or similar
+     * Can be 0
+     */
+    int bits_per_raw_sample;
+
+    /**
+     * Codec-specific bitstream restrictions that the stream conforms to.
+     */
+    int profile;
+    int level;
+
+    /**
+     * Video only. The dimensions of the video frame in pixels.
+     */
+    int width;
+    int height;
+
+    /**
+     * Video only. The aspect ratio (width / height) which a single pixel
+     * should have when displayed.
+     *
+     * When the aspect ratio is unknown / undefined, the numerator should be
+     * set to 0 (the denominator may have any value).
+     */
+    AVRational sample_aspect_ratio;
+
+    /**
+     * Video only. The order of the fields in interlaced video.
+     */
+    enum AVFieldOrder                  field_order;
+
+    /**
+     * Video only. Additional colorspace characteristics.
+     */
+    enum AVColorRange                  color_range;
+    enum AVColorPrimaries              color_primaries;
+    enum AVColorTransferCharacteristic color_trc;
+    enum AVColorSpace                  color_space;
+    enum AVChromaLocation              chroma_location;
+
+    /**
+     * Video only. Number of delayed frames.
+     */
+    int video_delay;
+
+    /**
+     * Audio only. The channel layout bitmask. May be 0 if the channel layout is
+     * unknown or unspecified, otherwise the number of bits set must be equal to
+     * the channels field.
+     */
+    uint64_t channel_layout;
+    /**
+     * Audio only. The number of audio channels.
+     */
+    int      channels;
+    /**
+     * Audio only. The number of audio samples per second.
+     */
+    int      sample_rate;
+    /**
+     * Audio only. The number of bytes per coded audio frame, required by some
+     * formats.
+     *
+     * Corresponds to nBlockAlign in WAVEFORMATEX.
+     */
+    int      block_align;
+    /**
+     * Audio only. Audio frame size, if known. Required by some formats to be static.
+     */
+    int      frame_size;
+
+    /**
+     * Audio only. The amount of padding (in samples) 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.
+     */
+    int initial_padding;
+    /**
+     * Audio only. The amount of padding (in samples) appended by the encoder to
+     * the end of the audio. I.e. this number of decoded samples must be
+     * discarded by the caller from the end of the stream to get the original
+     * audio without any trailing padding.
+     */
+    int trailing_padding;
+    /**
+     * Audio only. Number of samples to skip after a discontinuity.
+     */
+    int seek_preroll;
+} AVCodecParameters;
+
+/**
+ * Iterate over all registered codecs.
+ *
+ * @param opaque a pointer where libavcodec will store the iteration state. Must
+ *               point to NULL to start the iteration.
+ *
+ * @return the next registered codec or NULL when the iteration is
+ *         finished
+ */
+const AVCodec *av_codec_iterate(void **opaque);
+
+#if FF_API_NEXT
+/**
+ * If c is NULL, returns the first registered codec,
+ * if c is non-NULL, returns the next registered codec after c,
+ * or NULL if c is the last one.
+ */
+attribute_deprecated
+AVCodec *av_codec_next(const AVCodec *c);
+#endif
+
+/**
+ * Return the LIBAVCODEC_VERSION_INT constant.
+ */
+unsigned avcodec_version(void);
+
+/**
+ * Return the libavcodec build-time configuration.
+ */
+const char *avcodec_configuration(void);
+
+/**
+ * Return the libavcodec license.
+ */
+const char *avcodec_license(void);
+
+#if FF_API_NEXT
+/**
+ * Register the codec codec and initialize libavcodec.
+ *
+ * @warning either this function or avcodec_register_all() must be called
+ * before any other libavcodec functions.
+ *
+ * @see avcodec_register_all()
+ */
+attribute_deprecated
+void avcodec_register(AVCodec *codec);
+
+/**
+ * Register all the codecs, parsers and bitstream filters which were enabled at
+ * configuration time. If you do not call this function you can select exactly
+ * which formats you want to support, by using the individual registration
+ * functions.
+ *
+ * @see avcodec_register
+ * @see av_register_codec_parser
+ * @see av_register_bitstream_filter
+ */
+attribute_deprecated
+void avcodec_register_all(void);
+#endif
+
+/**
+ * Allocate an AVCodecContext and set its fields to default values. The
+ * resulting struct should be freed with avcodec_free_context().
+ *
+ * @param codec if non-NULL, allocate private data and initialize defaults
+ *              for the given codec. It is illegal to then call avcodec_open2()
+ *              with a different codec.
+ *              If NULL, then the codec-specific defaults won't be initialized,
+ *              which may result in suboptimal default settings (this is
+ *              important mainly for encoders, e.g. libx264).
+ *
+ * @return An AVCodecContext filled with default values or NULL on failure.
+ */
+AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
+
+/**
+ * Free the codec context and everything associated with it and write NULL to
+ * the provided pointer.
+ */
+void avcodec_free_context(AVCodecContext **avctx);
+
+#if FF_API_GET_CONTEXT_DEFAULTS
+/**
+ * @deprecated This function should not be used, as closing and opening a codec
+ * context multiple time is not supported. A new codec context should be
+ * allocated for each new use.
+ */
+int avcodec_get_context_defaults3(AVCodecContext *s, const AVCodec *codec);
+#endif
+
+/**
+ * Get the AVClass for AVCodecContext. It can be used in combination with
+ * AV_OPT_SEARCH_FAKE_OBJ for examining options.
+ *
+ * @see av_opt_find().
+ */
+const AVClass *avcodec_get_class(void);
+
+#if FF_API_COPY_CONTEXT
+/**
+ * Get the AVClass for AVFrame. It can be used in combination with
+ * AV_OPT_SEARCH_FAKE_OBJ for examining options.
+ *
+ * @see av_opt_find().
+ */
+const AVClass *avcodec_get_frame_class(void);
+
+/**
+ * Get the AVClass for AVSubtitleRect. It can be used in combination with
+ * AV_OPT_SEARCH_FAKE_OBJ for examining options.
+ *
+ * @see av_opt_find().
+ */
+const AVClass *avcodec_get_subtitle_rect_class(void);
+
+/**
+ * Copy the settings of the source AVCodecContext into the destination
+ * AVCodecContext. The resulting destination codec context will be
+ * unopened, i.e. you are required to call avcodec_open2() before you
+ * can use this AVCodecContext to decode/encode video/audio data.
+ *
+ * @param dest target codec context, should be initialized with
+ *             avcodec_alloc_context3(NULL), but otherwise uninitialized
+ * @param src source codec context
+ * @return AVERROR() on error (e.g. memory allocation error), 0 on success
+ *
+ * @deprecated The semantics of this function are ill-defined and it should not
+ * be used. If you need to transfer the stream parameters from one codec context
+ * to another, use an intermediate AVCodecParameters instance and the
+ * avcodec_parameters_from_context() / avcodec_parameters_to_context()
+ * functions.
+ */
+attribute_deprecated
+int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src);
+#endif
+
+/**
+ * Allocate a new AVCodecParameters and set its fields to default values
+ * (unknown/invalid/0). The returned struct must be freed with
+ * avcodec_parameters_free().
+ */
+AVCodecParameters *avcodec_parameters_alloc(void);
+
+/**
+ * Free an AVCodecParameters instance and everything associated with it and
+ * write NULL to the supplied pointer.
+ */
+void avcodec_parameters_free(AVCodecParameters **par);
+
+/**
+ * Copy the contents of src to dst. Any allocated fields in dst are freed and
+ * replaced with newly allocated duplicates of the corresponding fields in src.
+ *
+ * @return >= 0 on success, a negative AVERROR code on failure.
+ */
+int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src);
+
+/**
+ * Fill the parameters struct based on the values from the supplied codec
+ * context. Any allocated fields in par are freed and replaced with duplicates
+ * of the corresponding fields in codec.
+ *
+ * @return >= 0 on success, a negative AVERROR code on failure
+ */
+int avcodec_parameters_from_context(AVCodecParameters *par,
+                                    const AVCodecContext *codec);
+
+/**
+ * Fill the codec context based on the values from the supplied codec
+ * parameters. Any allocated fields in codec that have a corresponding field in
+ * par are freed and replaced with duplicates of the corresponding field in par.
+ * Fields in codec that do not have a counterpart in par are not touched.
+ *
+ * @return >= 0 on success, a negative AVERROR code on failure.
+ */
+int avcodec_parameters_to_context(AVCodecContext *codec,
+                                  const AVCodecParameters *par);
+
+/**
+ * Initialize the AVCodecContext to use the given AVCodec. Prior to using this
+ * function the context has to be allocated with avcodec_alloc_context3().
+ *
+ * The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
+ * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for
+ * retrieving a codec.
+ *
+ * @warning This function is not thread safe!
+ *
+ * @note Always call this function before using decoding routines (such as
+ * @ref avcodec_receive_frame()).
+ *
+ * @code
+ * avcodec_register_all();
+ * av_dict_set(&opts, "b", "2.5M", 0);
+ * codec = avcodec_find_decoder(AV_CODEC_ID_H264);
+ * if (!codec)
+ *     exit(1);
+ *
+ * context = avcodec_alloc_context3(codec);
+ *
+ * if (avcodec_open2(context, codec, opts) < 0)
+ *     exit(1);
+ * @endcode
+ *
+ * @param avctx The context to initialize.
+ * @param codec The codec to open this context for. If a non-NULL codec has been
+ *              previously passed to avcodec_alloc_context3() or
+ *              for this context, then this parameter MUST be either NULL or
+ *              equal to the previously passed codec.
+ * @param options A dictionary filled with AVCodecContext and codec-private options.
+ *                On return this object will be filled with options that were not found.
+ *
+ * @return zero on success, a negative value on error
+ * @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),
+ *      av_dict_set(), av_opt_find().
+ */
+int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
+
+/**
+ * Close a given AVCodecContext and free all the data associated with it
+ * (but not the AVCodecContext itself).
+ *
+ * Calling this function on an AVCodecContext that hasn't been opened will free
+ * the codec-specific data allocated in avcodec_alloc_context3() with a non-NULL
+ * codec. Subsequent calls will do nothing.
+ *
+ * @note Do not use this function. Use avcodec_free_context() to destroy a
+ * codec context (either open or closed). Opening and closing a codec context
+ * multiple times is not supported anymore -- use multiple codec contexts
+ * instead.
+ */
+int avcodec_close(AVCodecContext *avctx);
+
+/**
+ * Free all allocated data in the given subtitle struct.
+ *
+ * @param sub AVSubtitle to free.
+ */
+void avsubtitle_free(AVSubtitle *sub);
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup lavc_packet
+ * @{
+ */
+
+/**
+ * Allocate an AVPacket and set its fields to default values.  The resulting
+ * struct must be freed using av_packet_free().
+ *
+ * @return An AVPacket filled with default values or NULL on failure.
+ *
+ * @note this only allocates the AVPacket itself, not the data buffers. Those
+ * must be allocated through other means such as av_new_packet.
+ *
+ * @see av_new_packet
+ */
+AVPacket *av_packet_alloc(void);
+
+/**
+ * Create a new packet that references the same data as src.
+ *
+ * This is a shortcut for av_packet_alloc()+av_packet_ref().
+ *
+ * @return newly created AVPacket on success, NULL on error.
+ *
+ * @see av_packet_alloc
+ * @see av_packet_ref
+ */
+AVPacket *av_packet_clone(const AVPacket *src);
+
+/**
+ * Free the packet, if the packet is reference counted, it will be
+ * unreferenced first.
+ *
+ * @param pkt packet to be freed. The pointer will be set to NULL.
+ * @note passing NULL is a no-op.
+ */
+void av_packet_free(AVPacket **pkt);
+
+/**
+ * Initialize optional fields of a packet with default values.
+ *
+ * Note, this does not touch the data and size members, which have to be
+ * initialized separately.
+ *
+ * @param pkt packet
+ */
+void av_init_packet(AVPacket *pkt);
+
+/**
+ * Allocate the payload of a packet and initialize its fields with
+ * default values.
+ *
+ * @param pkt packet
+ * @param size wanted payload size
+ * @return 0 if OK, AVERROR_xxx otherwise
+ */
+int av_new_packet(AVPacket *pkt, int size);
+
+/**
+ * Reduce packet size, correctly zeroing padding
+ *
+ * @param pkt packet
+ * @param size new size
+ */
+void av_shrink_packet(AVPacket *pkt, int size);
+
+/**
+ * Increase packet size, correctly zeroing padding
+ *
+ * @param pkt packet
+ * @param grow_by number of bytes by which to increase the size of the packet
+ */
+int av_grow_packet(AVPacket *pkt, int grow_by);
+
+/**
+ * Initialize a reference-counted packet from av_malloc()ed data.
+ *
+ * @param pkt packet to be initialized. This function will set the data, size,
+ *        buf and destruct fields, all others are left untouched.
+ * @param data Data allocated by av_malloc() to be used as packet data. If this
+ *        function returns successfully, the data is owned by the underlying AVBuffer.
+ *        The caller may not access the data through other means.
+ * @param size size of data in bytes, without the padding. I.e. the full buffer
+ *        size is assumed to be size + AV_INPUT_BUFFER_PADDING_SIZE.
+ *
+ * @return 0 on success, a negative AVERROR on error
+ */
+int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size);
+
+#if FF_API_AVPACKET_OLD_API
+/**
+ * @warning This is a hack - the packet memory allocation stuff is broken. The
+ * packet is allocated if it was not really allocated.
+ *
+ * @deprecated Use av_packet_ref or av_packet_make_refcounted
+ */
+attribute_deprecated
+int av_dup_packet(AVPacket *pkt);
+/**
+ * Copy packet, including contents
+ *
+ * @return 0 on success, negative AVERROR on fail
+ *
+ * @deprecated Use av_packet_ref
+ */
+attribute_deprecated
+int av_copy_packet(AVPacket *dst, const AVPacket *src);
+
+/**
+ * Copy packet side data
+ *
+ * @return 0 on success, negative AVERROR on fail
+ *
+ * @deprecated Use av_packet_copy_props
+ */
+attribute_deprecated
+int av_copy_packet_side_data(AVPacket *dst, const AVPacket *src);
+
+/**
+ * Free a packet.
+ *
+ * @deprecated Use av_packet_unref
+ *
+ * @param pkt packet to free
+ */
+attribute_deprecated
+void av_free_packet(AVPacket *pkt);
+#endif
+/**
+ * Allocate new information of a packet.
+ *
+ * @param pkt packet
+ * @param type side information type
+ * @param size side information size
+ * @return pointer to fresh allocated data or NULL otherwise
+ */
+uint8_t* av_packet_new_side_data(AVPacket *pkt, enum AVPacketSideDataType type,
+                                 int size);
+
+/**
+ * Wrap an existing array as a packet side data.
+ *
+ * @param pkt packet
+ * @param type side information type
+ * @param data the side data array. It must be allocated with the av_malloc()
+ *             family of functions. The ownership of the data is transferred to
+ *             pkt.
+ * @param size side information size
+ * @return a non-negative number on success, a negative AVERROR code on
+ *         failure. On failure, the packet is unchanged and the data remains
+ *         owned by the caller.
+ */
+int av_packet_add_side_data(AVPacket *pkt, enum AVPacketSideDataType type,
+                            uint8_t *data, size_t size);
+
+/**
+ * Shrink the already allocated side data buffer
+ *
+ * @param pkt packet
+ * @param type side information type
+ * @param size new side information size
+ * @return 0 on success, < 0 on failure
+ */
+int av_packet_shrink_side_data(AVPacket *pkt, enum AVPacketSideDataType type,
+                               int size);
+
+/**
+ * Get side information from packet.
+ *
+ * @param pkt packet
+ * @param type desired side information type
+ * @param size pointer for side information size to store (optional)
+ * @return pointer to data if present or NULL otherwise
+ */
+uint8_t* av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type,
+                                 int *size);
+
+#if FF_API_MERGE_SD_API
+attribute_deprecated
+int av_packet_merge_side_data(AVPacket *pkt);
+
+attribute_deprecated
+int av_packet_split_side_data(AVPacket *pkt);
+#endif
+
+const char *av_packet_side_data_name(enum AVPacketSideDataType type);
+
+/**
+ * Pack a dictionary for use in side_data.
+ *
+ * @param dict The dictionary to pack.
+ * @param size pointer to store the size of the returned data
+ * @return pointer to data if successful, NULL otherwise
+ */
+uint8_t *av_packet_pack_dictionary(AVDictionary *dict, int *size);
+/**
+ * Unpack a dictionary from side_data.
+ *
+ * @param data data from side_data
+ * @param size size of the data
+ * @param dict the metadata storage dictionary
+ * @return 0 on success, < 0 on failure
+ */
+int av_packet_unpack_dictionary(const uint8_t *data, int size, AVDictionary **dict);
+
+
+/**
+ * Convenience function to free all the side data stored.
+ * All the other fields stay untouched.
+ *
+ * @param pkt packet
+ */
+void av_packet_free_side_data(AVPacket *pkt);
+
+/**
+ * Setup a new reference to the data described by a given packet
+ *
+ * If src is reference-counted, setup dst as a new reference to the
+ * buffer in src. Otherwise allocate a new buffer in dst and copy the
+ * data from src into it.
+ *
+ * All the other fields are copied from src.
+ *
+ * @see av_packet_unref
+ *
+ * @param dst Destination packet
+ * @param src Source packet
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_packet_ref(AVPacket *dst, const AVPacket *src);
+
+/**
+ * Wipe the packet.
+ *
+ * Unreference the buffer referenced by the packet and reset the
+ * remaining packet fields to their default values.
+ *
+ * @param pkt The packet to be unreferenced.
+ */
+void av_packet_unref(AVPacket *pkt);
+
+/**
+ * Move every field in src to dst and reset src.
+ *
+ * @see av_packet_unref
+ *
+ * @param src Source packet, will be reset
+ * @param dst Destination packet
+ */
+void av_packet_move_ref(AVPacket *dst, AVPacket *src);
+
+/**
+ * Copy only "properties" fields from src to dst.
+ *
+ * Properties for the purpose of this function are all the fields
+ * beside those related to the packet data (buf, data, size)
+ *
+ * @param dst Destination packet
+ * @param src Source packet
+ *
+ * @return 0 on success AVERROR on failure.
+ */
+int av_packet_copy_props(AVPacket *dst, const AVPacket *src);
+
+/**
+ * Ensure the data described by a given packet is reference counted.
+ *
+ * @note This function does not ensure that the reference will be writable.
+ *       Use av_packet_make_writable instead for that purpose.
+ *
+ * @see av_packet_ref
+ * @see av_packet_make_writable
+ *
+ * @param pkt packet whose data should be made reference counted.
+ *
+ * @return 0 on success, a negative AVERROR on error. On failure, the
+ *         packet is unchanged.
+ */
+int av_packet_make_refcounted(AVPacket *pkt);
+
+/**
+ * Create a writable reference for the data described by a given packet,
+ * avoiding data copy if possible.
+ *
+ * @param pkt Packet whose data should be made writable.
+ *
+ * @return 0 on success, a negative AVERROR on failure. On failure, the
+ *         packet is unchanged.
+ */
+int av_packet_make_writable(AVPacket *pkt);
+
+/**
+ * Convert valid timing fields (timestamps / durations) in a packet from one
+ * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be
+ * ignored.
+ *
+ * @param pkt packet on which the conversion will be performed
+ * @param tb_src source timebase, in which the timing fields in pkt are
+ *               expressed
+ * @param tb_dst destination timebase, to which the timing fields will be
+ *               converted
+ */
+void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup lavc_decoding
+ * @{
+ */
+
+/**
+ * Find a registered decoder with a matching codec ID.
+ *
+ * @param id AVCodecID of the requested decoder
+ * @return A decoder if one was found, NULL otherwise.
+ */
+AVCodec *avcodec_find_decoder(enum AVCodecID id);
+
+/**
+ * Find a registered decoder with the specified name.
+ *
+ * @param name name of the requested decoder
+ * @return A decoder if one was found, NULL otherwise.
+ */
+AVCodec *avcodec_find_decoder_by_name(const char *name);
+
+/**
+ * The default callback for AVCodecContext.get_buffer2(). It is made public so
+ * it can be called by custom get_buffer2() implementations for decoders without
+ * AV_CODEC_CAP_DR1 set.
+ */
+int avcodec_default_get_buffer2(AVCodecContext *s, AVFrame *frame, int flags);
+
+/**
+ * Modify width and height values so that they will result in a memory
+ * buffer that is acceptable for the codec if you do not use any horizontal
+ * padding.
+ *
+ * May only be used if a codec with AV_CODEC_CAP_DR1 has been opened.
+ */
+void avcodec_align_dimensions(AVCodecContext *s, int *width, int *height);
+
+/**
+ * Modify width and height values so that they will result in a memory
+ * buffer that is acceptable for the codec if you also ensure that all
+ * line sizes are a multiple of the respective linesize_align[i].
+ *
+ * May only be used if a codec with AV_CODEC_CAP_DR1 has been opened.
+ */
+void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height,
+                               int linesize_align[AV_NUM_DATA_POINTERS]);
+
+/**
+ * Converts AVChromaLocation to swscale x/y chroma position.
+ *
+ * The positions represent the chroma (0,0) position in a coordinates system
+ * with luma (0,0) representing the origin and luma(1,1) representing 256,256
+ *
+ * @param xpos  horizontal chroma sample position
+ * @param ypos  vertical   chroma sample position
+ */
+int avcodec_enum_to_chroma_pos(int *xpos, int *ypos, enum AVChromaLocation pos);
+
+/**
+ * Converts swscale x/y chroma position to AVChromaLocation.
+ *
+ * The positions represent the chroma (0,0) position in a coordinates system
+ * with luma (0,0) representing the origin and luma(1,1) representing 256,256
+ *
+ * @param xpos  horizontal chroma sample position
+ * @param ypos  vertical   chroma sample position
+ */
+enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos);
+
+/**
+ * Decode the audio frame of size avpkt->size from avpkt->data into frame.
+ *
+ * Some decoders may support multiple frames in a single AVPacket. Such
+ * decoders would then just decode the first frame and the return value would be
+ * less than the packet size. In this case, avcodec_decode_audio4 has to be
+ * called again with an AVPacket containing the remaining data in order to
+ * decode the second frame, etc...  Even if no frames are returned, the packet
+ * needs to be fed to the decoder with remaining data until it is completely
+ * consumed or an error occurs.
+ *
+ * Some decoders (those marked with AV_CODEC_CAP_DELAY) have a delay between input
+ * and output. This means that for some packets they will not immediately
+ * produce decoded output and need to be flushed at the end of decoding to get
+ * all the decoded data. Flushing is done by calling this function with packets
+ * with avpkt->data set to NULL and avpkt->size set to 0 until it stops
+ * returning samples. It is safe to flush even those decoders that are not
+ * marked with AV_CODEC_CAP_DELAY, then no samples will be returned.
+ *
+ * @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE
+ *          larger than the actual read bytes because some optimized bitstream
+ *          readers read 32 or 64 bits at once and could read over the end.
+ *
+ * @note The AVCodecContext MUST have been opened with @ref avcodec_open2()
+ * before packets may be fed to the decoder.
+ *
+ * @param      avctx the codec context
+ * @param[out] frame The AVFrame in which to store decoded audio samples.
+ *                   The decoder will allocate a buffer for the decoded frame by
+ *                   calling the AVCodecContext.get_buffer2() callback.
+ *                   When AVCodecContext.refcounted_frames is set to 1, the frame is
+ *                   reference counted and the returned reference belongs to the
+ *                   caller. The caller must release the frame using av_frame_unref()
+ *                   when the frame is no longer needed. The caller may safely write
+ *                   to the frame if av_frame_is_writable() returns 1.
+ *                   When AVCodecContext.refcounted_frames is set to 0, the returned
+ *                   reference belongs to the decoder and is valid only until the
+ *                   next call to this function or until closing or flushing the
+ *                   decoder. The caller may not write to it.
+ * @param[out] got_frame_ptr Zero if no frame could be decoded, otherwise it is
+ *                           non-zero. Note that this field being set to zero
+ *                           does not mean that an error has occurred. For
+ *                           decoders with AV_CODEC_CAP_DELAY set, no given decode
+ *                           call is guaranteed to produce a frame.
+ * @param[in]  avpkt The input AVPacket containing the input buffer.
+ *                   At least avpkt->data and avpkt->size should be set. Some
+ *                   decoders might also require additional fields to be set.
+ * @return A negative error code is returned if an error occurred during
+ *         decoding, otherwise the number of bytes consumed from the input
+ *         AVPacket is returned.
+ *
+* @deprecated Use avcodec_send_packet() and avcodec_receive_frame().
+ */
+attribute_deprecated
+int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame,
+                          int *got_frame_ptr, const AVPacket *avpkt);
+
+/**
+ * Decode the video frame of size avpkt->size from avpkt->data into picture.
+ * Some decoders may support multiple frames in a single AVPacket, such
+ * decoders would then just decode the first frame.
+ *
+ * @warning The input buffer must be AV_INPUT_BUFFER_PADDING_SIZE larger than
+ * the actual read bytes because some optimized bitstream readers read 32 or 64
+ * bits at once and could read over the end.
+ *
+ * @warning The end of the input buffer buf should be set to 0 to ensure that
+ * no overreading happens for damaged MPEG streams.
+ *
+ * @note Codecs which have the AV_CODEC_CAP_DELAY capability set have a delay
+ * between input and output, these need to be fed with avpkt->data=NULL,
+ * avpkt->size=0 at the end to return the remaining frames.
+ *
+ * @note The AVCodecContext MUST have been opened with @ref avcodec_open2()
+ * before packets may be fed to the decoder.
+ *
+ * @param avctx the codec context
+ * @param[out] picture The AVFrame in which the decoded video frame will be stored.
+ *             Use av_frame_alloc() to get an AVFrame. The codec will
+ *             allocate memory for the actual bitmap by calling the
+ *             AVCodecContext.get_buffer2() callback.
+ *             When AVCodecContext.refcounted_frames is set to 1, the frame is
+ *             reference counted and the returned reference belongs to the
+ *             caller. The caller must release the frame using av_frame_unref()
+ *             when the frame is no longer needed. The caller may safely write
+ *             to the frame if av_frame_is_writable() returns 1.
+ *             When AVCodecContext.refcounted_frames is set to 0, the returned
+ *             reference belongs to the decoder and is valid only until the
+ *             next call to this function or until closing or flushing the
+ *             decoder. The caller may not write to it.
+ *
+ * @param[in] avpkt The input AVPacket containing the input buffer.
+ *            You can create such packet with av_init_packet() and by then setting
+ *            data and size, some decoders might in addition need other fields like
+ *            flags&AV_PKT_FLAG_KEY. All decoders are designed to use the least
+ *            fields possible.
+ * @param[in,out] got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero.
+ * @return On error a negative value is returned, otherwise the number of bytes
+ * used or zero if no frame could be decompressed.
+ *
+ * @deprecated Use avcodec_send_packet() and avcodec_receive_frame().
+ */
+attribute_deprecated
+int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
+                         int *got_picture_ptr,
+                         const AVPacket *avpkt);
+
+/**
+ * Decode a subtitle message.
+ * Return a negative value on error, otherwise return the number of bytes used.
+ * If no subtitle could be decompressed, got_sub_ptr is zero.
+ * Otherwise, the subtitle is stored in *sub.
+ * Note that AV_CODEC_CAP_DR1 is not available for subtitle codecs. This is for
+ * simplicity, because the performance difference is expect to be negligible
+ * and reusing a get_buffer written for video codecs would probably perform badly
+ * due to a potentially very different allocation pattern.
+ *
+ * Some decoders (those marked with AV_CODEC_CAP_DELAY) have a delay between input
+ * and output. This means that for some packets they will not immediately
+ * produce decoded output and need to be flushed at the end of decoding to get
+ * all the decoded data. Flushing is done by calling this function with packets
+ * with avpkt->data set to NULL and avpkt->size set to 0 until it stops
+ * returning subtitles. It is safe to flush even those decoders that are not
+ * marked with AV_CODEC_CAP_DELAY, then no subtitles will be returned.
+ *
+ * @note The AVCodecContext MUST have been opened with @ref avcodec_open2()
+ * before packets may be fed to the decoder.
+ *
+ * @param avctx the codec context
+ * @param[out] sub The Preallocated AVSubtitle in which the decoded subtitle will be stored,
+ *                 must be freed with avsubtitle_free if *got_sub_ptr is set.
+ * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero.
+ * @param[in] avpkt The input AVPacket containing the input buffer.
+ */
+int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
+                            int *got_sub_ptr,
+                            AVPacket *avpkt);
+
+/**
+ * Supply raw packet data as input to a decoder.
+ *
+ * Internally, this call will copy relevant AVCodecContext fields, which can
+ * influence decoding per-packet, and apply them when the packet is actually
+ * decoded. (For example AVCodecContext.skip_frame, which might direct the
+ * decoder to drop the frame contained by the packet sent with this function.)
+ *
+ * @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE
+ *          larger than the actual read bytes because some optimized bitstream
+ *          readers read 32 or 64 bits at once and could read over the end.
+ *
+ * @warning Do not mix this API with the legacy API (like avcodec_decode_video2())
+ *          on the same AVCodecContext. It will return unexpected results now
+ *          or in future libavcodec versions.
+ *
+ * @note The AVCodecContext MUST have been opened with @ref avcodec_open2()
+ *       before packets may be fed to the decoder.
+ *
+ * @param avctx codec context
+ * @param[in] avpkt The input AVPacket. Usually, this will be a single video
+ *                  frame, or several complete audio frames.
+ *                  Ownership of the packet remains with the caller, and the
+ *                  decoder will not write to the packet. The decoder may create
+ *                  a reference to the packet data (or copy it if the packet is
+ *                  not reference-counted).
+ *                  Unlike with older APIs, the packet is always fully consumed,
+ *                  and if it contains multiple frames (e.g. some audio codecs),
+ *                  will require you to call avcodec_receive_frame() multiple
+ *                  times afterwards before you can send a new packet.
+ *                  It can be NULL (or an AVPacket with data set to NULL and
+ *                  size set to 0); in this case, it is considered a flush
+ *                  packet, which signals the end of the stream. Sending the
+ *                  first flush packet will return success. Subsequent ones are
+ *                  unnecessary and will return AVERROR_EOF. If the decoder
+ *                  still has frames buffered, it will return them after sending
+ *                  a flush packet.
+ *
+ * @return 0 on success, otherwise negative error code:
+ *      AVERROR(EAGAIN):   input is not accepted in the current state - user
+ *                         must read output with avcodec_receive_frame() (once
+ *                         all output is read, the packet should be resent, and
+ *                         the call will not fail with EAGAIN).
+ *      AVERROR_EOF:       the decoder has been flushed, and no new packets can
+ *                         be sent to it (also returned if more than 1 flush
+ *                         packet is sent)
+ *      AVERROR(EINVAL):   codec not opened, it is an encoder, or requires flush
+ *      AVERROR(ENOMEM):   failed to add packet to internal queue, or similar
+ *      other errors: legitimate decoding errors
+ */
+int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
+
+/**
+ * Return decoded output data from a decoder.
+ *
+ * @param avctx codec context
+ * @param frame This will be set to a reference-counted video or audio
+ *              frame (depending on the decoder type) allocated by the
+ *              decoder. Note that the function will always call
+ *              av_frame_unref(frame) before doing anything else.
+ *
+ * @return
+ *      0:                 success, a frame was returned
+ *      AVERROR(EAGAIN):   output is not available in this state - user must try
+ *                         to send new input
+ *      AVERROR_EOF:       the decoder has been fully flushed, and there will be
+ *                         no more output frames
+ *      AVERROR(EINVAL):   codec not opened, or it is an encoder
+ *      other negative values: legitimate decoding errors
+ */
+int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
+
+/**
+ * Supply a raw video or audio frame to the encoder. Use avcodec_receive_packet()
+ * to retrieve buffered output packets.
+ *
+ * @param avctx     codec context
+ * @param[in] frame AVFrame containing the raw audio or video frame to be encoded.
+ *                  Ownership of the frame remains with the caller, and the
+ *                  encoder will not write to the frame. The encoder may create
+ *                  a reference to the frame data (or copy it if the frame is
+ *                  not reference-counted).
+ *                  It can be NULL, in which case it is considered a flush
+ *                  packet.  This signals the end of the stream. If the encoder
+ *                  still has packets buffered, it will return them after this
+ *                  call. Once flushing mode has been entered, additional flush
+ *                  packets are ignored, and sending frames will return
+ *                  AVERROR_EOF.
+ *
+ *                  For audio:
+ *                  If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame
+ *                  can have any number of samples.
+ *                  If it is not set, frame->nb_samples must be equal to
+ *                  avctx->frame_size for all frames except the last.
+ *                  The final frame may be smaller than avctx->frame_size.
+ * @return 0 on success, otherwise negative error code:
+ *      AVERROR(EAGAIN):   input is not accepted in the current state - user
+ *                         must read output with avcodec_receive_packet() (once
+ *                         all output is read, the packet should be resent, and
+ *                         the call will not fail with EAGAIN).
+ *      AVERROR_EOF:       the encoder has been flushed, and no new frames can
+ *                         be sent to it
+ *      AVERROR(EINVAL):   codec not opened, refcounted_frames not set, it is a
+ *                         decoder, or requires flush
+ *      AVERROR(ENOMEM):   failed to add packet to internal queue, or similar
+ *      other errors: legitimate decoding errors
+ */
+int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);
+
+/**
+ * Read encoded data from the encoder.
+ *
+ * @param avctx codec context
+ * @param avpkt This will be set to a reference-counted packet allocated by the
+ *              encoder. Note that the function will always call
+ *              av_frame_unref(frame) before doing anything else.
+ * @return 0 on success, otherwise negative error code:
+ *      AVERROR(EAGAIN):   output is not available in the current state - user
+ *                         must try to send input
+ *      AVERROR_EOF:       the encoder has been fully flushed, and there will be
+ *                         no more output packets
+ *      AVERROR(EINVAL):   codec not opened, or it is an encoder
+ *      other errors: legitimate decoding errors
+ */
+int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
+
+/**
+ * Create and return a AVHWFramesContext with values adequate for hardware
+ * decoding. This is meant to get called from the get_format callback, and is
+ * a helper for preparing a AVHWFramesContext for AVCodecContext.hw_frames_ctx.
+ * This API is for decoding with certain hardware acceleration modes/APIs only.
+ *
+ * The returned AVHWFramesContext is not initialized. The caller must do this
+ * with av_hwframe_ctx_init().
+ *
+ * Calling this function is not a requirement, but makes it simpler to avoid
+ * codec or hardware API specific details when manually allocating frames.
+ *
+ * Alternatively to this, an API user can set AVCodecContext.hw_device_ctx,
+ * which sets up AVCodecContext.hw_frames_ctx fully automatically, and makes
+ * it unnecessary to call this function or having to care about
+ * AVHWFramesContext initialization at all.
+ *
+ * There are a number of requirements for calling this function:
+ *
+ * - It must be called from get_format with the same avctx parameter that was
+ *   passed to get_format. Calling it outside of get_format is not allowed, and
+ *   can trigger undefined behavior.
+ * - The function is not always supported (see description of return values).
+ *   Even if this function returns successfully, hwaccel initialization could
+ *   fail later. (The degree to which implementations check whether the stream
+ *   is actually supported varies. Some do this check only after the user's
+ *   get_format callback returns.)
+ * - The hw_pix_fmt must be one of the choices suggested by get_format. If the
+ *   user decides to use a AVHWFramesContext prepared with this API function,
+ *   the user must return the same hw_pix_fmt from get_format.
+ * - The device_ref passed to this function must support the given hw_pix_fmt.
+ * - After calling this API function, it is the user's responsibility to
+ *   initialize the AVHWFramesContext (returned by the out_frames_ref parameter),
+ *   and to set AVCodecContext.hw_frames_ctx to it. If done, this must be done
+ *   before returning from get_format (this is implied by the normal
+ *   AVCodecContext.hw_frames_ctx API rules).
+ * - The AVHWFramesContext parameters may change every time time get_format is
+ *   called. Also, AVCodecContext.hw_frames_ctx is reset before get_format. So
+ *   you are inherently required to go through this process again on every
+ *   get_format call.
+ * - It is perfectly possible to call this function without actually using
+ *   the resulting AVHWFramesContext. One use-case might be trying to reuse a
+ *   previously initialized AVHWFramesContext, and calling this API function
+ *   only to test whether the required frame parameters have changed.
+ * - Fields that use dynamically allocated values of any kind must not be set
+ *   by the user unless setting them is explicitly allowed by the documentation.
+ *   If the user sets AVHWFramesContext.free and AVHWFramesContext.user_opaque,
+ *   the new free callback must call the potentially set previous free callback.
+ *   This API call may set any dynamically allocated fields, including the free
+ *   callback.
+ *
+ * The function will set at least the following fields on AVHWFramesContext
+ * (potentially more, depending on hwaccel API):
+ *
+ * - All fields set by av_hwframe_ctx_alloc().
+ * - Set the format field to hw_pix_fmt.
+ * - Set the sw_format field to the most suited and most versatile format. (An
+ *   implication is that this will prefer generic formats over opaque formats
+ *   with arbitrary restrictions, if possible.)
+ * - Set the width/height fields to the coded frame size, rounded up to the
+ *   API-specific minimum alignment.
+ * - Only _if_ the hwaccel requires a pre-allocated pool: set the initial_pool_size
+ *   field to the number of maximum reference surfaces possible with the codec,
+ *   plus 1 surface for the user to work (meaning the user can safely reference
+ *   at most 1 decoded surface at a time), plus additional buffering introduced
+ *   by frame threading. If the hwaccel does not require pre-allocation, the
+ *   field is left to 0, and the decoder will allocate new surfaces on demand
+ *   during decoding.
+ * - Possibly AVHWFramesContext.hwctx fields, depending on the underlying
+ *   hardware API.
+ *
+ * Essentially, out_frames_ref returns the same as av_hwframe_ctx_alloc(), but
+ * with basic frame parameters set.
+ *
+ * The function is stateless, and does not change the AVCodecContext or the
+ * device_ref AVHWDeviceContext.
+ *
+ * @param avctx The context which is currently calling get_format, and which
+ *              implicitly contains all state needed for filling the returned
+ *              AVHWFramesContext properly.
+ * @param device_ref A reference to the AVHWDeviceContext describing the device
+ *                   which will be used by the hardware decoder.
+ * @param hw_pix_fmt The hwaccel format you are going to return from get_format.
+ * @param out_frames_ref On success, set to a reference to an _uninitialized_
+ *                       AVHWFramesContext, created from the given device_ref.
+ *                       Fields will be set to values required for decoding.
+ *                       Not changed if an error is returned.
+ * @return zero on success, a negative value on error. The following error codes
+ *         have special semantics:
+ *      AVERROR(ENOENT): the decoder does not support this functionality. Setup
+ *                       is always manual, or it is a decoder which does not
+ *                       support setting AVCodecContext.hw_frames_ctx at all,
+ *                       or it is a software format.
+ *      AVERROR(EINVAL): it is known that hardware decoding is not supported for
+ *                       this configuration, or the device_ref is not supported
+ *                       for the hwaccel referenced by hw_pix_fmt.
+ */
+int avcodec_get_hw_frames_parameters(AVCodecContext *avctx,
+                                     AVBufferRef *device_ref,
+                                     enum AVPixelFormat hw_pix_fmt,
+                                     AVBufferRef **out_frames_ref);
+
+
+
+/**
+ * @defgroup lavc_parsing Frame parsing
+ * @{
+ */
+
+enum AVPictureStructure {
+    AV_PICTURE_STRUCTURE_UNKNOWN,      //< unknown
+    AV_PICTURE_STRUCTURE_TOP_FIELD,    //< coded as top field
+    AV_PICTURE_STRUCTURE_BOTTOM_FIELD, //< coded as bottom field
+    AV_PICTURE_STRUCTURE_FRAME,        //< coded as frame
+};
+
+typedef struct AVCodecParserContext {
+    void *priv_data;
+    struct AVCodecParser *parser;
+    int64_t frame_offset; /* offset of the current frame */
+    int64_t cur_offset; /* current offset
+                           (incremented by each av_parser_parse()) */
+    int64_t next_frame_offset; /* offset of the next frame */
+    /* video info */
+    int pict_type; /* XXX: Put it back in AVCodecContext. */
+    /**
+     * This field is used for proper frame duration computation in lavf.
+     * It signals, how much longer the frame duration of the current frame
+     * is compared to normal frame duration.
+     *
+     * frame_duration = (1 + repeat_pict) * time_base
+     *
+     * It is used by codecs like H.264 to display telecined material.
+     */
+    int repeat_pict; /* XXX: Put it back in AVCodecContext. */
+    int64_t pts;     /* pts of the current frame */
+    int64_t dts;     /* dts of the current frame */
+
+    /* private data */
+    int64_t last_pts;
+    int64_t last_dts;
+    int fetch_timestamp;
+
+#define AV_PARSER_PTS_NB 4
+    int cur_frame_start_index;
+    int64_t cur_frame_offset[AV_PARSER_PTS_NB];
+    int64_t cur_frame_pts[AV_PARSER_PTS_NB];
+    int64_t cur_frame_dts[AV_PARSER_PTS_NB];
+
+    int flags;
+#define PARSER_FLAG_COMPLETE_FRAMES           0x0001
+#define PARSER_FLAG_ONCE                      0x0002
+/// Set if the parser has a valid file offset
+#define PARSER_FLAG_FETCHED_OFFSET            0x0004
+#define PARSER_FLAG_USE_CODEC_TS              0x1000
+
+    int64_t offset;      ///< byte offset from starting packet start
+    int64_t cur_frame_end[AV_PARSER_PTS_NB];
+
+    /**
+     * Set by parser to 1 for key frames and 0 for non-key frames.
+     * It is initialized to -1, so if the parser doesn't set this flag,
+     * old-style fallback using AV_PICTURE_TYPE_I picture type as key frames
+     * will be used.
+     */
+    int key_frame;
+
+#if FF_API_CONVERGENCE_DURATION
+    /**
+     * @deprecated unused
+     */
+    attribute_deprecated
+    int64_t convergence_duration;
+#endif
+
+    // Timestamp generation support:
+    /**
+     * Synchronization point for start of timestamp generation.
+     *
+     * Set to >0 for sync point, 0 for no sync point and <0 for undefined
+     * (default).
+     *
+     * For example, this corresponds to presence of H.264 buffering period
+     * SEI message.
+     */
+    int dts_sync_point;
+
+    /**
+     * Offset of the current timestamp against last timestamp sync point in
+     * units of AVCodecContext.time_base.
+     *
+     * Set to INT_MIN when dts_sync_point unused. Otherwise, it must
+     * contain a valid timestamp offset.
+     *
+     * Note that the timestamp of sync point has usually a nonzero
+     * dts_ref_dts_delta, which refers to the previous sync point. Offset of
+     * the next frame after timestamp sync point will be usually 1.
+     *
+     * For example, this corresponds to H.264 cpb_removal_delay.
+     */
+    int dts_ref_dts_delta;
+
+    /**
+     * Presentation delay of current frame in units of AVCodecContext.time_base.
+     *
+     * Set to INT_MIN when dts_sync_point unused. Otherwise, it must
+     * contain valid non-negative timestamp delta (presentation time of a frame
+     * must not lie in the past).
+     *
+     * This delay represents the difference between decoding and presentation
+     * time of the frame.
+     *
+     * For example, this corresponds to H.264 dpb_output_delay.
+     */
+    int pts_dts_delta;
+
+    /**
+     * Position of the packet in file.
+     *
+     * Analogous to cur_frame_pts/dts
+     */
+    int64_t cur_frame_pos[AV_PARSER_PTS_NB];
+
+    /**
+     * Byte position of currently parsed frame in stream.
+     */
+    int64_t pos;
+
+    /**
+     * Previous frame byte position.
+     */
+    int64_t last_pos;
+
+    /**
+     * Duration of the current frame.
+     * For audio, this is in units of 1 / AVCodecContext.sample_rate.
+     * For all other types, this is in units of AVCodecContext.time_base.
+     */
+    int duration;
+
+    enum AVFieldOrder field_order;
+
+    /**
+     * Indicate whether a picture is coded as a frame, top field or bottom field.
+     *
+     * For example, H.264 field_pic_flag equal to 0 corresponds to
+     * AV_PICTURE_STRUCTURE_FRAME. An H.264 picture with field_pic_flag
+     * equal to 1 and bottom_field_flag equal to 0 corresponds to
+     * AV_PICTURE_STRUCTURE_TOP_FIELD.
+     */
+    enum AVPictureStructure picture_structure;
+
+    /**
+     * Picture number incremented in presentation or output order.
+     * This field may be reinitialized at the first picture of a new sequence.
+     *
+     * For example, this corresponds to H.264 PicOrderCnt.
+     */
+    int output_picture_number;
+
+    /**
+     * Dimensions of the decoded video intended for presentation.
+     */
+    int width;
+    int height;
+
+    /**
+     * Dimensions of the coded video.
+     */
+    int coded_width;
+    int coded_height;
+
+    /**
+     * The format of the coded data, corresponds to enum AVPixelFormat for video
+     * and for enum AVSampleFormat for audio.
+     *
+     * Note that a decoder can have considerable freedom in how exactly it
+     * decodes the data, so the format reported here might be different from the
+     * one returned by a decoder.
+     */
+    int format;
+} AVCodecParserContext;
+
+typedef struct AVCodecParser {
+    int codec_ids[5]; /* several codec IDs are permitted */
+    int priv_data_size;
+    int (*parser_init)(AVCodecParserContext *s);
+    /* This callback never returns an error, a negative value means that
+     * the frame start was in a previous packet. */
+    int (*parser_parse)(AVCodecParserContext *s,
+                        AVCodecContext *avctx,
+                        const uint8_t **poutbuf, int *poutbuf_size,
+                        const uint8_t *buf, int buf_size);
+    void (*parser_close)(AVCodecParserContext *s);
+    int (*split)(AVCodecContext *avctx, const uint8_t *buf, int buf_size);
+    struct AVCodecParser *next;
+} AVCodecParser;
+
+/**
+ * Iterate over all registered codec parsers.
+ *
+ * @param opaque a pointer where libavcodec will store the iteration state. Must
+ *               point to NULL to start the iteration.
+ *
+ * @return the next registered codec parser or NULL when the iteration is
+ *         finished
+ */
+const AVCodecParser *av_parser_iterate(void **opaque);
+
+attribute_deprecated
+AVCodecParser *av_parser_next(const AVCodecParser *c);
+
+attribute_deprecated
+void av_register_codec_parser(AVCodecParser *parser);
+AVCodecParserContext *av_parser_init(int codec_id);
+
+/**
+ * Parse a packet.
+ *
+ * @param s             parser context.
+ * @param avctx         codec context.
+ * @param poutbuf       set to pointer to parsed buffer or NULL if not yet finished.
+ * @param poutbuf_size  set to size of parsed buffer or zero if not yet finished.
+ * @param buf           input buffer.
+ * @param buf_size      buffer size in bytes without the padding. I.e. the full buffer
+                        size is assumed to be buf_size + AV_INPUT_BUFFER_PADDING_SIZE.
+                        To signal EOF, this should be 0 (so that the last frame
+                        can be output).
+ * @param pts           input presentation timestamp.
+ * @param dts           input decoding timestamp.
+ * @param pos           input byte position in stream.
+ * @return the number of bytes of the input bitstream used.
+ *
+ * Example:
+ * @code
+ *   while(in_len){
+ *       len = av_parser_parse2(myparser, AVCodecContext, &data, &size,
+ *                                        in_data, in_len,
+ *                                        pts, dts, pos);
+ *       in_data += len;
+ *       in_len  -= len;
+ *
+ *       if(size)
+ *          decode_frame(data, size);
+ *   }
+ * @endcode
+ */
+int av_parser_parse2(AVCodecParserContext *s,
+                     AVCodecContext *avctx,
+                     uint8_t **poutbuf, int *poutbuf_size,
+                     const uint8_t *buf, int buf_size,
+                     int64_t pts, int64_t dts,
+                     int64_t pos);
+
+/**
+ * @return 0 if the output buffer is a subset of the input, 1 if it is allocated and must be freed
+ * @deprecated use AVBitStreamFilter
+ */
+int av_parser_change(AVCodecParserContext *s,
+                     AVCodecContext *avctx,
+                     uint8_t **poutbuf, int *poutbuf_size,
+                     const uint8_t *buf, int buf_size, int keyframe);
+void av_parser_close(AVCodecParserContext *s);
+
+/**
+ * @}
+ * @}
+ */
+
+/**
+ * @addtogroup lavc_encoding
+ * @{
+ */
+
+/**
+ * Find a registered encoder with a matching codec ID.
+ *
+ * @param id AVCodecID of the requested encoder
+ * @return An encoder if one was found, NULL otherwise.
+ */
+AVCodec *avcodec_find_encoder(enum AVCodecID id);
+
+/**
+ * Find a registered encoder with the specified name.
+ *
+ * @param name name of the requested encoder
+ * @return An encoder if one was found, NULL otherwise.
+ */
+AVCodec *avcodec_find_encoder_by_name(const char *name);
+
+/**
+ * Encode a frame of audio.
+ *
+ * Takes input samples from frame and writes the next output packet, if
+ * available, to avpkt. The output packet does not necessarily contain data for
+ * the most recent frame, as encoders can delay, split, and combine input frames
+ * internally as needed.
+ *
+ * @param avctx     codec context
+ * @param avpkt     output AVPacket.
+ *                  The user can supply an output buffer by setting
+ *                  avpkt->data and avpkt->size prior to calling the
+ *                  function, but if the size of the user-provided data is not
+ *                  large enough, encoding will fail. If avpkt->data and
+ *                  avpkt->size are set, avpkt->destruct must also be set. All
+ *                  other AVPacket fields will be reset by the encoder using
+ *                  av_init_packet(). If avpkt->data is NULL, the encoder will
+ *                  allocate it. The encoder will set avpkt->size to the size
+ *                  of the output packet.
+ *
+ *                  If this function fails or produces no output, avpkt will be
+ *                  freed using av_packet_unref().
+ * @param[in] frame AVFrame containing the raw audio data to be encoded.
+ *                  May be NULL when flushing an encoder that has the
+ *                  AV_CODEC_CAP_DELAY capability set.
+ *                  If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame
+ *                  can have any number of samples.
+ *                  If it is not set, frame->nb_samples must be equal to
+ *                  avctx->frame_size for all frames except the last.
+ *                  The final frame may be smaller than avctx->frame_size.
+ * @param[out] got_packet_ptr This field is set to 1 by libavcodec if the
+ *                            output packet is non-empty, and to 0 if it is
+ *                            empty. If the function returns an error, the
+ *                            packet can be assumed to be invalid, and the
+ *                            value of got_packet_ptr is undefined and should
+ *                            not be used.
+ * @return          0 on success, negative error code on failure
+ *
+ * @deprecated use avcodec_send_frame()/avcodec_receive_packet() instead
+ */
+attribute_deprecated
+int avcodec_encode_audio2(AVCodecContext *avctx, AVPacket *avpkt,
+                          const AVFrame *frame, int *got_packet_ptr);
+
+/**
+ * Encode a frame of video.
+ *
+ * Takes input raw video data from frame and writes the next output packet, if
+ * available, to avpkt. The output packet does not necessarily contain data for
+ * the most recent frame, as encoders can delay and reorder input frames
+ * internally as needed.
+ *
+ * @param avctx     codec context
+ * @param avpkt     output AVPacket.
+ *                  The user can supply an output buffer by setting
+ *                  avpkt->data and avpkt->size prior to calling the
+ *                  function, but if the size of the user-provided data is not
+ *                  large enough, encoding will fail. All other AVPacket fields
+ *                  will be reset by the encoder using av_init_packet(). If
+ *                  avpkt->data is NULL, the encoder will allocate it.
+ *                  The encoder will set avpkt->size to the size of the
+ *                  output packet. The returned data (if any) belongs to the
+ *                  caller, he is responsible for freeing it.
+ *
+ *                  If this function fails or produces no output, avpkt will be
+ *                  freed using av_packet_unref().
+ * @param[in] frame AVFrame containing the raw video data to be encoded.
+ *                  May be NULL when flushing an encoder that has the
+ *                  AV_CODEC_CAP_DELAY capability set.
+ * @param[out] got_packet_ptr This field is set to 1 by libavcodec if the
+ *                            output packet is non-empty, and to 0 if it is
+ *                            empty. If the function returns an error, the
+ *                            packet can be assumed to be invalid, and the
+ *                            value of got_packet_ptr is undefined and should
+ *                            not be used.
+ * @return          0 on success, negative error code on failure
+ *
+ * @deprecated use avcodec_send_frame()/avcodec_receive_packet() instead
+ */
+attribute_deprecated
+int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt,
+                          const AVFrame *frame, int *got_packet_ptr);
+
+int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
+                            const AVSubtitle *sub);
+
+
+/**
+ * @}
+ */
+
+#if FF_API_AVPICTURE
+/**
+ * @addtogroup lavc_picture
+ * @{
+ */
+
+/**
+ * @deprecated unused
+ */
+attribute_deprecated
+int avpicture_alloc(AVPicture *picture, enum AVPixelFormat pix_fmt, int width, int height);
+
+/**
+ * @deprecated unused
+ */
+attribute_deprecated
+void avpicture_free(AVPicture *picture);
+
+/**
+ * @deprecated use av_image_fill_arrays() instead.
+ */
+attribute_deprecated
+int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
+                   enum AVPixelFormat pix_fmt, int width, int height);
+
+/**
+ * @deprecated use av_image_copy_to_buffer() instead.
+ */
+attribute_deprecated
+int avpicture_layout(const AVPicture *src, enum AVPixelFormat pix_fmt,
+                     int width, int height,
+                     unsigned char *dest, int dest_size);
+
+/**
+ * @deprecated use av_image_get_buffer_size() instead.
+ */
+attribute_deprecated
+int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height);
+
+/**
+ * @deprecated av_image_copy() instead.
+ */
+attribute_deprecated
+void av_picture_copy(AVPicture *dst, const AVPicture *src,
+                     enum AVPixelFormat pix_fmt, int width, int height);
+
+/**
+ * @deprecated unused
+ */
+attribute_deprecated
+int av_picture_crop(AVPicture *dst, const AVPicture *src,
+                    enum AVPixelFormat pix_fmt, int top_band, int left_band);
+
+/**
+ * @deprecated unused
+ */
+attribute_deprecated
+int av_picture_pad(AVPicture *dst, const AVPicture *src, int height, int width, enum AVPixelFormat pix_fmt,
+            int padtop, int padbottom, int padleft, int padright, int *color);
+
+/**
+ * @}
+ */
+#endif
+
+/**
+ * @defgroup lavc_misc Utility functions
+ * @ingroup libavc
+ *
+ * Miscellaneous utility functions related to both encoding and decoding
+ * (or neither).
+ * @{
+ */
+
+/**
+ * @defgroup lavc_misc_pixfmt Pixel formats
+ *
+ * Functions for working with pixel formats.
+ * @{
+ */
+
+#if FF_API_GETCHROMA
+/**
+ * @deprecated Use av_pix_fmt_get_chroma_sub_sample
+ */
+
+attribute_deprecated
+void avcodec_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, int *h_shift, int *v_shift);
+#endif
+
+/**
+ * Return a value representing the fourCC code associated to the
+ * pixel format pix_fmt, or 0 if no associated fourCC code can be
+ * found.
+ */
+unsigned int avcodec_pix_fmt_to_codec_tag(enum AVPixelFormat pix_fmt);
+
+/**
+ * @deprecated see av_get_pix_fmt_loss()
+ */
+int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, enum AVPixelFormat src_pix_fmt,
+                             int has_alpha);
+
+/**
+ * Find the best pixel format to convert to given a certain source pixel
+ * format.  When converting from one pixel format to another, information loss
+ * may occur.  For example, when converting from RGB24 to GRAY, the color
+ * information will be lost. Similarly, other losses occur when converting from
+ * some formats to other formats. avcodec_find_best_pix_fmt_of_2() searches which of
+ * the given pixel formats should be used to suffer the least amount of loss.
+ * The pixel formats from which it chooses one, are determined by the
+ * pix_fmt_list parameter.
+ *
+ *
+ * @param[in] pix_fmt_list AV_PIX_FMT_NONE terminated array of pixel formats to choose from
+ * @param[in] src_pix_fmt source pixel format
+ * @param[in] has_alpha Whether the source pixel format alpha channel is used.
+ * @param[out] loss_ptr Combination of flags informing you what kind of losses will occur.
+ * @return The best pixel format to convert to or -1 if none was found.
+ */
+enum AVPixelFormat avcodec_find_best_pix_fmt_of_list(const enum AVPixelFormat *pix_fmt_list,
+                                            enum AVPixelFormat src_pix_fmt,
+                                            int has_alpha, int *loss_ptr);
+
+/**
+ * @deprecated see av_find_best_pix_fmt_of_2()
+ */
+enum AVPixelFormat avcodec_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2,
+                                            enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr);
+
+attribute_deprecated
+enum AVPixelFormat avcodec_find_best_pix_fmt2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2,
+                                            enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr);
+
+enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
+
+/**
+ * @}
+ */
+
+#if FF_API_TAG_STRING
+/**
+ * Put a string representing the codec tag codec_tag in buf.
+ *
+ * @param buf       buffer to place codec tag in
+ * @param buf_size size in bytes of buf
+ * @param codec_tag codec tag to assign
+ * @return the length of the string that would have been generated if
+ * enough space had been available, excluding the trailing null
+ *
+ * @deprecated see av_fourcc_make_string() and av_fourcc2str().
+ */
+attribute_deprecated
+size_t av_get_codec_tag_string(char *buf, size_t buf_size, unsigned int codec_tag);
+#endif
+
+void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode);
+
+/**
+ * Return a name for the specified profile, if available.
+ *
+ * @param codec the codec that is searched for the given profile
+ * @param profile the profile value for which a name is requested
+ * @return A name for the profile if found, NULL otherwise.
+ */
+const char *av_get_profile_name(const AVCodec *codec, int profile);
+
+/**
+ * Return a name for the specified profile, if available.
+ *
+ * @param codec_id the ID of the codec to which the requested profile belongs
+ * @param profile the profile value for which a name is requested
+ * @return A name for the profile if found, NULL otherwise.
+ *
+ * @note unlike av_get_profile_name(), which searches a list of profiles
+ *       supported by a specific decoder or encoder implementation, this
+ *       function searches the list of profiles from the AVCodecDescriptor
+ */
+const char *avcodec_profile_name(enum AVCodecID codec_id, int profile);
+
+int avcodec_default_execute(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2),void *arg, int *ret, int count, int size);
+int avcodec_default_execute2(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2, int, int),void *arg, int *ret, int count);
+//FIXME func typedef
+
+/**
+ * Fill AVFrame audio data and linesize pointers.
+ *
+ * The buffer buf must be a preallocated buffer with a size big enough
+ * to contain the specified samples amount. The filled AVFrame data
+ * pointers will point to this buffer.
+ *
+ * AVFrame extended_data channel pointers are allocated if necessary for
+ * planar audio.
+ *
+ * @param frame       the AVFrame
+ *                    frame->nb_samples must be set prior to calling the
+ *                    function. This function fills in frame->data,
+ *                    frame->extended_data, frame->linesize[0].
+ * @param nb_channels channel count
+ * @param sample_fmt  sample format
+ * @param buf         buffer to use for frame data
+ * @param buf_size    size of buffer
+ * @param align       plane size sample alignment (0 = default)
+ * @return            >=0 on success, negative error code on failure
+ * @todo return the size in bytes required to store the samples in
+ * case of success, at the next libavutil bump
+ */
+int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels,
+                             enum AVSampleFormat sample_fmt, const uint8_t *buf,
+                             int buf_size, int align);
+
+/**
+ * Reset the internal decoder state / flush internal buffers. Should be called
+ * e.g. when seeking or when switching to a different stream.
+ *
+ * @note when refcounted frames are not used (i.e. avctx->refcounted_frames is 0),
+ * this invalidates the frames previously returned from the decoder. When
+ * refcounted frames are used, the decoder just releases any references it might
+ * keep internally, but the caller's reference remains valid.
+ */
+void avcodec_flush_buffers(AVCodecContext *avctx);
+
+/**
+ * Return codec bits per sample.
+ *
+ * @param[in] codec_id the codec
+ * @return Number of bits per sample or zero if unknown for the given codec.
+ */
+int av_get_bits_per_sample(enum AVCodecID codec_id);
+
+/**
+ * Return the PCM codec associated with a sample format.
+ * @param be  endianness, 0 for little, 1 for big,
+ *            -1 (or anything else) for native
+ * @return  AV_CODEC_ID_PCM_* or AV_CODEC_ID_NONE
+ */
+enum AVCodecID av_get_pcm_codec(enum AVSampleFormat fmt, int be);
+
+/**
+ * Return codec bits per sample.
+ * Only return non-zero if the bits per sample is exactly correct, not an
+ * approximation.
+ *
+ * @param[in] codec_id the codec
+ * @return Number of bits per sample or zero if unknown for the given codec.
+ */
+int av_get_exact_bits_per_sample(enum AVCodecID codec_id);
+
+/**
+ * Return audio frame duration.
+ *
+ * @param avctx        codec context
+ * @param frame_bytes  size of the frame, or 0 if unknown
+ * @return             frame duration, in samples, if known. 0 if not able to
+ *                     determine.
+ */
+int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes);
+
+/**
+ * This function is the same as av_get_audio_frame_duration(), except it works
+ * with AVCodecParameters instead of an AVCodecContext.
+ */
+int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes);
+
+#if FF_API_OLD_BSF
+typedef struct AVBitStreamFilterContext {
+    void *priv_data;
+    const struct AVBitStreamFilter *filter;
+    AVCodecParserContext *parser;
+    struct AVBitStreamFilterContext *next;
+    /**
+     * Internal default arguments, used if NULL is passed to av_bitstream_filter_filter().
+     * Not for access by library users.
+     */
+    char *args;
+} AVBitStreamFilterContext;
+#endif
+
+typedef struct AVBSFInternal AVBSFInternal;
+
+/**
+ * The bitstream filter state.
+ *
+ * This struct must be allocated with av_bsf_alloc() and freed with
+ * av_bsf_free().
+ *
+ * The fields in the struct will only be changed (by the caller or by the
+ * filter) as described in their documentation, and are to be considered
+ * immutable otherwise.
+ */
+typedef struct AVBSFContext {
+    /**
+     * A class for logging and AVOptions
+     */
+    const AVClass *av_class;
+
+    /**
+     * The bitstream filter this context is an instance of.
+     */
+    const struct AVBitStreamFilter *filter;
+
+    /**
+     * Opaque libavcodec internal data. Must not be touched by the caller in any
+     * way.
+     */
+    AVBSFInternal *internal;
+
+    /**
+     * Opaque filter-specific private data. If filter->priv_class is non-NULL,
+     * this is an AVOptions-enabled struct.
+     */
+    void *priv_data;
+
+    /**
+     * Parameters of the input stream. This field is allocated in
+     * av_bsf_alloc(), it needs to be filled by the caller before
+     * av_bsf_init().
+     */
+    AVCodecParameters *par_in;
+
+    /**
+     * Parameters of the output stream. This field is allocated in
+     * av_bsf_alloc(), it is set by the filter in av_bsf_init().
+     */
+    AVCodecParameters *par_out;
+
+    /**
+     * The timebase used for the timestamps of the input packets. Set by the
+     * caller before av_bsf_init().
+     */
+    AVRational time_base_in;
+
+    /**
+     * The timebase used for the timestamps of the output packets. Set by the
+     * filter in av_bsf_init().
+     */
+    AVRational time_base_out;
+} AVBSFContext;
+
+typedef struct AVBitStreamFilter {
+    const char *name;
+
+    /**
+     * A list of codec ids supported by the filter, terminated by
+     * AV_CODEC_ID_NONE.
+     * May be NULL, in that case the bitstream filter works with any codec id.
+     */
+    const enum AVCodecID *codec_ids;
+
+    /**
+     * A class for the private data, used to declare bitstream filter private
+     * AVOptions. This field is NULL for bitstream filters that do not declare
+     * any options.
+     *
+     * If this field is non-NULL, the first member of the filter private data
+     * must be a pointer to AVClass, which will be set by libavcodec generic
+     * code to this class.
+     */
+    const AVClass *priv_class;
+
+    /*****************************************************************
+     * No fields below this line are part of the public API. They
+     * may not be used outside of libavcodec and can be changed and
+     * removed at will.
+     * New public fields should be added right above.
+     *****************************************************************
+     */
+
+    int priv_data_size;
+    int (*init)(AVBSFContext *ctx);
+    int (*filter)(AVBSFContext *ctx, AVPacket *pkt);
+    void (*close)(AVBSFContext *ctx);
+    void (*flush)(AVBSFContext *ctx);
+} AVBitStreamFilter;
+
+#if FF_API_OLD_BSF
+/**
+ * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext)
+ * is deprecated. Use the new bitstream filtering API (using AVBSFContext).
+ */
+attribute_deprecated
+void av_register_bitstream_filter(AVBitStreamFilter *bsf);
+/**
+ * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext)
+ * is deprecated. Use av_bsf_get_by_name(), av_bsf_alloc(), and av_bsf_init()
+ * from the new bitstream filtering API (using AVBSFContext).
+ */
+attribute_deprecated
+AVBitStreamFilterContext *av_bitstream_filter_init(const char *name);
+/**
+ * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext)
+ * is deprecated. Use av_bsf_send_packet() and av_bsf_receive_packet() from the
+ * new bitstream filtering API (using AVBSFContext).
+ */
+attribute_deprecated
+int av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc,
+                               AVCodecContext *avctx, const char *args,
+                               uint8_t **poutbuf, int *poutbuf_size,
+                               const uint8_t *buf, int buf_size, int keyframe);
+/**
+ * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext)
+ * is deprecated. Use av_bsf_free() from the new bitstream filtering API (using
+ * AVBSFContext).
+ */
+attribute_deprecated
+void av_bitstream_filter_close(AVBitStreamFilterContext *bsf);
+/**
+ * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext)
+ * is deprecated. Use av_bsf_iterate() from the new bitstream filtering API (using
+ * AVBSFContext).
+ */
+attribute_deprecated
+const AVBitStreamFilter *av_bitstream_filter_next(const AVBitStreamFilter *f);
+#endif
+
+/**
+ * @return a bitstream filter with the specified name or NULL if no such
+ *         bitstream filter exists.
+ */
+const AVBitStreamFilter *av_bsf_get_by_name(const char *name);
+
+/**
+ * Iterate over all registered bitstream filters.
+ *
+ * @param opaque a pointer where libavcodec will store the iteration state. Must
+ *               point to NULL to start the iteration.
+ *
+ * @return the next registered bitstream filter or NULL when the iteration is
+ *         finished
+ */
+const AVBitStreamFilter *av_bsf_iterate(void **opaque);
+#if FF_API_NEXT
+attribute_deprecated
+const AVBitStreamFilter *av_bsf_next(void **opaque);
+#endif
+
+/**
+ * Allocate a context for a given bitstream filter. The caller must fill in the
+ * context parameters as described in the documentation and then call
+ * av_bsf_init() before sending any data to the filter.
+ *
+ * @param filter the filter for which to allocate an instance.
+ * @param ctx a pointer into which the pointer to the newly-allocated context
+ *            will be written. It must be freed with av_bsf_free() after the
+ *            filtering is done.
+ *
+ * @return 0 on success, a negative AVERROR code on failure
+ */
+int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **ctx);
+
+/**
+ * Prepare the filter for use, after all the parameters and options have been
+ * set.
+ */
+int av_bsf_init(AVBSFContext *ctx);
+
+/**
+ * Submit a packet for filtering.
+ *
+ * After sending each packet, the filter must be completely drained by calling
+ * av_bsf_receive_packet() repeatedly until it returns AVERROR(EAGAIN) or
+ * AVERROR_EOF.
+ *
+ * @param pkt the packet to filter. The bitstream filter will take ownership of
+ * the packet and reset the contents of pkt. pkt is not touched if an error occurs.
+ * This parameter may be NULL, which signals the end of the stream (i.e. no more
+ * packets will be sent). That will cause the filter to output any packets it
+ * may have buffered internally.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt);
+
+/**
+ * Retrieve a filtered packet.
+ *
+ * @param[out] pkt this struct will be filled with the contents of the filtered
+ *                 packet. It is owned by the caller and must be freed using
+ *                 av_packet_unref() when it is no longer needed.
+ *                 This parameter should be "clean" (i.e. freshly allocated
+ *                 with av_packet_alloc() or unreffed with av_packet_unref())
+ *                 when this function is called. If this function returns
+ *                 successfully, the contents of pkt will be completely
+ *                 overwritten by the returned data. On failure, pkt is not
+ *                 touched.
+ *
+ * @return 0 on success. AVERROR(EAGAIN) if more packets need to be sent to the
+ * filter (using av_bsf_send_packet()) to get more output. AVERROR_EOF if there
+ * will be no further output from the filter. Another negative AVERROR value if
+ * an error occurs.
+ *
+ * @note one input packet may result in several output packets, so after sending
+ * a packet with av_bsf_send_packet(), this function needs to be called
+ * repeatedly until it stops returning 0. It is also possible for a filter to
+ * output fewer packets than were sent to it, so this function may return
+ * AVERROR(EAGAIN) immediately after a successful av_bsf_send_packet() call.
+ */
+int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt);
+
+/**
+ * Reset the internal bitstream filter state / flush internal buffers.
+ */
+void av_bsf_flush(AVBSFContext *ctx);
+
+/**
+ * Free a bitstream filter context and everything associated with it; write NULL
+ * into the supplied pointer.
+ */
+void av_bsf_free(AVBSFContext **ctx);
+
+/**
+ * Get the AVClass for AVBSFContext. It can be used in combination with
+ * AV_OPT_SEARCH_FAKE_OBJ for examining options.
+ *
+ * @see av_opt_find().
+ */
+const AVClass *av_bsf_get_class(void);
+
+/**
+ * Structure for chain/list of bitstream filters.
+ * Empty list can be allocated by av_bsf_list_alloc().
+ */
+typedef struct AVBSFList AVBSFList;
+
+/**
+ * Allocate empty list of bitstream filters.
+ * The list must be later freed by av_bsf_list_free()
+ * or finalized by av_bsf_list_finalize().
+ *
+ * @return Pointer to @ref AVBSFList on success, NULL in case of failure
+ */
+AVBSFList *av_bsf_list_alloc(void);
+
+/**
+ * Free list of bitstream filters.
+ *
+ * @param lst Pointer to pointer returned by av_bsf_list_alloc()
+ */
+void av_bsf_list_free(AVBSFList **lst);
+
+/**
+ * Append bitstream filter to the list of bitstream filters.
+ *
+ * @param lst List to append to
+ * @param bsf Filter context to be appended
+ *
+ * @return >=0 on success, negative AVERROR in case of failure
+ */
+int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf);
+
+/**
+ * Construct new bitstream filter context given it's name and options
+ * and append it to the list of bitstream filters.
+ *
+ * @param lst      List to append to
+ * @param bsf_name Name of the bitstream filter
+ * @param options  Options for the bitstream filter, can be set to NULL
+ *
+ * @return >=0 on success, negative AVERROR in case of failure
+ */
+int av_bsf_list_append2(AVBSFList *lst, const char * bsf_name, AVDictionary **options);
+/**
+ * Finalize list of bitstream filters.
+ *
+ * This function will transform @ref AVBSFList to single @ref AVBSFContext,
+ * so the whole chain of bitstream filters can be treated as single filter
+ * freshly allocated by av_bsf_alloc().
+ * If the call is successful, @ref AVBSFList structure is freed and lst
+ * will be set to NULL. In case of failure, caller is responsible for
+ * freeing the structure by av_bsf_list_free()
+ *
+ * @param      lst Filter list structure to be transformed
+ * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure
+ *                 representing the chain of bitstream filters
+ *
+ * @return >=0 on success, negative AVERROR in case of failure
+ */
+int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf);
+
+/**
+ * Parse string describing list of bitstream filters and create single
+ * @ref AVBSFContext describing the whole chain of bitstream filters.
+ * Resulting @ref AVBSFContext can be treated as any other @ref AVBSFContext freshly
+ * allocated by av_bsf_alloc().
+ *
+ * @param      str String describing chain of bitstream filters in format
+ *                 `bsf1[=opt1=val1:opt2=val2][,bsf2]`
+ * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure
+ *                 representing the chain of bitstream filters
+ *
+ * @return >=0 on success, negative AVERROR in case of failure
+ */
+int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf);
+
+/**
+ * Get null/pass-through bitstream filter.
+ *
+ * @param[out] bsf Pointer to be set to new instance of pass-through bitstream filter
+ *
+ * @return
+ */
+int av_bsf_get_null_filter(AVBSFContext **bsf);
+
+/* memory */
+
+/**
+ * Same behaviour av_fast_malloc but the buffer has additional
+ * AV_INPUT_BUFFER_PADDING_SIZE at the end which will always be 0.
+ *
+ * In addition the whole buffer will initially and after resizes
+ * be 0-initialized so that no uninitialized data will ever appear.
+ */
+void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size);
+
+/**
+ * Same behaviour av_fast_padded_malloc except that buffer will always
+ * be 0-initialized after call.
+ */
+void av_fast_padded_mallocz(void *ptr, unsigned int *size, size_t min_size);
+
+/**
+ * Encode extradata length to a buffer. Used by xiph codecs.
+ *
+ * @param s buffer to write to; must be at least (v/255+1) bytes long
+ * @param v size of extradata in bytes
+ * @return number of bytes written to the buffer.
+ */
+unsigned int av_xiphlacing(unsigned char *s, unsigned int v);
+
+#if FF_API_USER_VISIBLE_AVHWACCEL
+/**
+ * Register the hardware accelerator hwaccel.
+ *
+ * @deprecated  This function doesn't do anything.
+ */
+attribute_deprecated
+void av_register_hwaccel(AVHWAccel *hwaccel);
+
+/**
+ * If hwaccel is NULL, returns the first registered hardware accelerator,
+ * if hwaccel is non-NULL, returns the next registered hardware accelerator
+ * after hwaccel, or NULL if hwaccel is the last one.
+ *
+ * @deprecated  AVHWaccel structures contain no user-serviceable parts, so
+ *              this function should not be used.
+ */
+attribute_deprecated
+AVHWAccel *av_hwaccel_next(const AVHWAccel *hwaccel);
+#endif
+
+#if FF_API_LOCKMGR
+/**
+ * Lock operation used by lockmgr
+ *
+ * @deprecated Deprecated together with av_lockmgr_register().
+ */
+enum AVLockOp {
+  AV_LOCK_CREATE,  ///< Create a mutex
+  AV_LOCK_OBTAIN,  ///< Lock the mutex
+  AV_LOCK_RELEASE, ///< Unlock the mutex
+  AV_LOCK_DESTROY, ///< Free mutex resources
+};
+
+/**
+ * Register a user provided lock manager supporting the operations
+ * specified by AVLockOp. The "mutex" argument to the function points
+ * to a (void *) where the lockmgr should store/get a pointer to a user
+ * allocated mutex. It is NULL upon AV_LOCK_CREATE and equal to the
+ * value left by the last call for all other ops. If the lock manager is
+ * unable to perform the op then it should leave the mutex in the same
+ * state as when it was called and return a non-zero value. However,
+ * when called with AV_LOCK_DESTROY the mutex will always be assumed to
+ * have been successfully destroyed. If av_lockmgr_register succeeds
+ * it will return a non-negative value, if it fails it will return a
+ * negative value and destroy all mutex and unregister all callbacks.
+ * av_lockmgr_register is not thread-safe, it must be called from a
+ * single thread before any calls which make use of locking are used.
+ *
+ * @param cb User defined callback. av_lockmgr_register invokes calls
+ *           to this callback and the previously registered callback.
+ *           The callback will be used to create more than one mutex
+ *           each of which must be backed by its own underlying locking
+ *           mechanism (i.e. do not use a single static object to
+ *           implement your lock manager). If cb is set to NULL the
+ *           lockmgr will be unregistered.
+ *
+ * @deprecated This function does nothing, and always returns 0. Be sure to
+ *             build with thread support to get basic thread safety.
+ */
+attribute_deprecated
+int av_lockmgr_register(int (*cb)(void **mutex, enum AVLockOp op));
+#endif
+
+/**
+ * Get the type of the given codec.
+ */
+enum AVMediaType avcodec_get_type(enum AVCodecID codec_id);
+
+/**
+ * Get the name of a codec.
+ * @return  a static string identifying the codec; never NULL
+ */
+const char *avcodec_get_name(enum AVCodecID id);
+
+/**
+ * @return a positive value if s is open (i.e. avcodec_open2() was called on it
+ * with no corresponding avcodec_close()), 0 otherwise.
+ */
+int avcodec_is_open(AVCodecContext *s);
+
+/**
+ * @return a non-zero number if codec is an encoder, zero otherwise
+ */
+int av_codec_is_encoder(const AVCodec *codec);
+
+/**
+ * @return a non-zero number if codec is a decoder, zero otherwise
+ */
+int av_codec_is_decoder(const AVCodec *codec);
+
+/**
+ * @return descriptor for given codec ID or NULL if no descriptor exists.
+ */
+const AVCodecDescriptor *avcodec_descriptor_get(enum AVCodecID id);
+
+/**
+ * Iterate over all codec descriptors known to libavcodec.
+ *
+ * @param prev previous descriptor. NULL to get the first descriptor.
+ *
+ * @return next descriptor or NULL after the last descriptor
+ */
+const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev);
+
+/**
+ * @return codec descriptor with the given name or NULL if no such descriptor
+ *         exists.
+ */
+const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
+
+/**
+ * Allocate a CPB properties structure and initialize its fields to default
+ * values.
+ *
+ * @param size if non-NULL, the size of the allocated struct will be written
+ *             here. This is useful for embedding it in side data.
+ *
+ * @return the newly allocated struct or NULL on failure
+ */
+AVCPBProperties *av_cpb_properties_alloc(size_t *size);
+
+/**
+ * @}
+ */
+
+#endif /* AVCODEC_AVCODEC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avdct.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avdct.h
new file mode 100644
index 0000000..272422e
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avdct.h
@@ -0,0 +1,84 @@
+/*
+ * 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_AVDCT_H
+#define AVCODEC_AVDCT_H
+
+#include "libavutil/opt.h"
+
+/**
+ * AVDCT context.
+ * @note function pointers can be NULL if the specific features have been
+ *       disabled at build time.
+ */
+typedef struct AVDCT {
+    const AVClass *av_class;
+
+    void (*idct)(int16_t *block /* align 16 */);
+
+    /**
+     * IDCT input permutation.
+     * Several optimized IDCTs need a permutated input (relative to the
+     * normal order of the reference IDCT).
+     * This permutation must be performed before the idct_put/add.
+     * Note, normally this can be merged with the zigzag/alternate scan<br>
+     * An example to avoid confusion:
+     * - (->decode coeffs -> zigzag reorder -> dequant -> reference IDCT -> ...)
+     * - (x -> reference DCT -> reference IDCT -> x)
+     * - (x -> reference DCT -> simple_mmx_perm = idct_permutation
+     *    -> simple_idct_mmx -> x)
+     * - (-> decode coeffs -> zigzag reorder -> simple_mmx_perm -> dequant
+     *    -> simple_idct_mmx -> ...)
+     */
+    uint8_t idct_permutation[64];
+
+    void (*fdct)(int16_t *block /* align 16 */);
+
+
+    /**
+     * DCT algorithm.
+     * must use AVOptions to set this field.
+     */
+    int dct_algo;
+
+    /**
+     * IDCT algorithm.
+     * must use AVOptions to set this field.
+     */
+    int idct_algo;
+
+    void (*get_pixels)(int16_t *block /* align 16 */,
+                       const uint8_t *pixels /* align 8 */,
+                       ptrdiff_t line_size);
+
+    int bits_per_sample;
+} AVDCT;
+
+/**
+ * Allocates a AVDCT context.
+ * This needs to be initialized with avcodec_dct_init() after optionally
+ * configuring it with AVOptions.
+ *
+ * To free it use av_free()
+ */
+AVDCT *avcodec_dct_alloc(void);
+int avcodec_dct_init(AVDCT *);
+
+const AVClass *avcodec_dct_get_class(void);
+
+#endif /* AVCODEC_AVDCT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avfft.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avfft.h
new file mode 100644
index 0000000..0c0f9b8
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/avfft.h
@@ -0,0 +1,118 @@
+/*
+ * 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_AVFFT_H
+#define AVCODEC_AVFFT_H
+
+/**
+ * @file
+ * @ingroup lavc_fft
+ * FFT functions
+ */
+
+/**
+ * @defgroup lavc_fft FFT functions
+ * @ingroup lavc_misc
+ *
+ * @{
+ */
+
+typedef float FFTSample;
+
+typedef struct FFTComplex {
+    FFTSample re, im;
+} FFTComplex;
+
+typedef struct FFTContext FFTContext;
+
+/**
+ * Set up a complex FFT.
+ * @param nbits           log2 of the length of the input array
+ * @param inverse         if 0 perform the forward transform, if 1 perform the inverse
+ */
+FFTContext *av_fft_init(int nbits, int inverse);
+
+/**
+ * Do the permutation needed BEFORE calling ff_fft_calc().
+ */
+void av_fft_permute(FFTContext *s, FFTComplex *z);
+
+/**
+ * Do a complex FFT with the parameters defined in av_fft_init(). The
+ * input data must be permuted before. No 1.0/sqrt(n) normalization is done.
+ */
+void av_fft_calc(FFTContext *s, FFTComplex *z);
+
+void av_fft_end(FFTContext *s);
+
+FFTContext *av_mdct_init(int nbits, int inverse, double scale);
+void av_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input);
+void av_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input);
+void av_mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input);
+void av_mdct_end(FFTContext *s);
+
+/* Real Discrete Fourier Transform */
+
+enum RDFTransformType {
+    DFT_R2C,
+    IDFT_C2R,
+    IDFT_R2C,
+    DFT_C2R,
+};
+
+typedef struct RDFTContext RDFTContext;
+
+/**
+ * Set up a real FFT.
+ * @param nbits           log2 of the length of the input array
+ * @param trans           the type of transform
+ */
+RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans);
+void av_rdft_calc(RDFTContext *s, FFTSample *data);
+void av_rdft_end(RDFTContext *s);
+
+/* Discrete Cosine Transform */
+
+typedef struct DCTContext DCTContext;
+
+enum DCTTransformType {
+    DCT_II = 0,
+    DCT_III,
+    DCT_I,
+    DST_I,
+};
+
+/**
+ * Set up DCT.
+ *
+ * @param nbits           size of the input array:
+ *                        (1 << nbits)     for DCT-II, DCT-III and DST-I
+ *                        (1 << nbits) + 1 for DCT-I
+ * @param type            the type of transform
+ *
+ * @note the first element of the input of DST-I is ignored
+ */
+DCTContext *av_dct_init(int nbits, enum DCTTransformType type);
+void av_dct_calc(DCTContext *s, FFTSample *data);
+void av_dct_end (DCTContext *s);
+
+/**
+ * @}
+ */
+
+#endif /* AVCODEC_AVFFT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/d3d11va.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/d3d11va.h
new file mode 100644
index 0000000..6816b6c
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/d3d11va.h
@@ -0,0 +1,112 @@
+/*
+ * Direct3D11 HW acceleration
+ *
+ * copyright (c) 2009 Laurent Aimar
+ * copyright (c) 2015 Steve Lhomme
+ *
+ * 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_D3D11VA_H
+#define AVCODEC_D3D11VA_H
+
+/**
+ * @file
+ * @ingroup lavc_codec_hwaccel_d3d11va
+ * Public libavcodec D3D11VA header.
+ */
+
+#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0602
+#endif
+
+#include <stdint.h>
+#include <d3d11.h>
+
+/**
+ * @defgroup lavc_codec_hwaccel_d3d11va Direct3D11
+ * @ingroup lavc_codec_hwaccel
+ *
+ * @{
+ */
+
+#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for Direct3D11 and old UVD/UVD+ ATI video cards
+#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for Direct3D11 and old Intel GPUs with ClearVideo interface
+
+/**
+ * This structure is used to provides the necessary configurations and data
+ * to the Direct3D11 FFmpeg HWAccel implementation.
+ *
+ * The application must make it available as AVCodecContext.hwaccel_context.
+ *
+ * Use av_d3d11va_alloc_context() exclusively to allocate an AVD3D11VAContext.
+ */
+typedef struct AVD3D11VAContext {
+    /**
+     * D3D11 decoder object
+     */
+    ID3D11VideoDecoder *decoder;
+
+    /**
+      * D3D11 VideoContext
+      */
+    ID3D11VideoContext *video_context;
+
+    /**
+     * D3D11 configuration used to create the decoder
+     */
+    D3D11_VIDEO_DECODER_CONFIG *cfg;
+
+    /**
+     * The number of surface in the surface array
+     */
+    unsigned surface_count;
+
+    /**
+     * The array of Direct3D surfaces used to create the decoder
+     */
+    ID3D11VideoDecoderOutputView **surface;
+
+    /**
+     * A bit field configuring the workarounds needed for using the decoder
+     */
+    uint64_t workaround;
+
+    /**
+     * Private to the FFmpeg AVHWAccel implementation
+     */
+    unsigned report_id;
+
+    /**
+      * Mutex to access video_context
+      */
+    HANDLE  context_mutex;
+} AVD3D11VAContext;
+
+/**
+ * Allocate an AVD3D11VAContext.
+ *
+ * @return Newly-allocated AVD3D11VAContext or NULL on failure.
+ */
+AVD3D11VAContext *av_d3d11va_alloc_context(void);
+
+/**
+ * @}
+ */
+
+#endif /* AVCODEC_D3D11VA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dirac.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dirac.h
new file mode 100644
index 0000000..e6d9d34
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dirac.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2007 Marco Gerards <marco@gnu.org>
+ * Copyright (C) 2009 David Conrad
+ * Copyright (C) 2011 Jordi Ortiz
+ *
+ * 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_DIRAC_H
+#define AVCODEC_DIRAC_H
+
+/**
+ * @file
+ * Interface to Dirac Decoder/Encoder
+ * @author Marco Gerards <marco@gnu.org>
+ * @author David Conrad
+ * @author Jordi Ortiz
+ */
+
+#include "avcodec.h"
+
+/**
+ * The spec limits the number of wavelet decompositions to 4 for both
+ * level 1 (VC-2) and 128 (long-gop default).
+ * 5 decompositions is the maximum before >16-bit buffers are needed.
+ * Schroedinger allows this for DD 9,7 and 13,7 wavelets only, limiting
+ * the others to 4 decompositions (or 3 for the fidelity filter).
+ *
+ * We use this instead of MAX_DECOMPOSITIONS to save some memory.
+ */
+#define MAX_DWT_LEVELS 5
+
+/**
+ * Parse code values:
+ *
+ * Dirac Specification ->
+ * 9.6.1  Table 9.1
+ *
+ * VC-2 Specification  ->
+ * 10.4.1 Table 10.1
+ */
+
+enum DiracParseCodes {
+    DIRAC_PCODE_SEQ_HEADER      = 0x00,
+    DIRAC_PCODE_END_SEQ         = 0x10,
+    DIRAC_PCODE_AUX             = 0x20,
+    DIRAC_PCODE_PAD             = 0x30,
+    DIRAC_PCODE_PICTURE_CODED   = 0x08,
+    DIRAC_PCODE_PICTURE_RAW     = 0x48,
+    DIRAC_PCODE_PICTURE_LOW_DEL = 0xC8,
+    DIRAC_PCODE_PICTURE_HQ      = 0xE8,
+    DIRAC_PCODE_INTER_NOREF_CO1 = 0x0A,
+    DIRAC_PCODE_INTER_NOREF_CO2 = 0x09,
+    DIRAC_PCODE_INTER_REF_CO1   = 0x0D,
+    DIRAC_PCODE_INTER_REF_CO2   = 0x0E,
+    DIRAC_PCODE_INTRA_REF_CO    = 0x0C,
+    DIRAC_PCODE_INTRA_REF_RAW   = 0x4C,
+    DIRAC_PCODE_INTRA_REF_PICT  = 0xCC,
+    DIRAC_PCODE_MAGIC           = 0x42424344,
+};
+
+typedef struct DiracVersionInfo {
+    int major;
+    int minor;
+} DiracVersionInfo;
+
+typedef struct AVDiracSeqHeader {
+    unsigned width;
+    unsigned height;
+    uint8_t chroma_format;          ///< 0: 444  1: 422  2: 420
+
+    uint8_t interlaced;
+    uint8_t top_field_first;
+
+    uint8_t frame_rate_index;       ///< index into dirac_frame_rate[]
+    uint8_t aspect_ratio_index;     ///< index into dirac_aspect_ratio[]
+
+    uint16_t clean_width;
+    uint16_t clean_height;
+    uint16_t clean_left_offset;
+    uint16_t clean_right_offset;
+
+    uint8_t pixel_range_index;      ///< index into dirac_pixel_range_presets[]
+    uint8_t color_spec_index;       ///< index into dirac_color_spec_presets[]
+
+    int profile;
+    int level;
+
+    AVRational framerate;
+    AVRational sample_aspect_ratio;
+
+    enum AVPixelFormat pix_fmt;
+    enum AVColorRange color_range;
+    enum AVColorPrimaries color_primaries;
+    enum AVColorTransferCharacteristic color_trc;
+    enum AVColorSpace colorspace;
+
+    DiracVersionInfo version;
+    int bit_depth;
+} AVDiracSeqHeader;
+
+/**
+ * Parse a Dirac sequence header.
+ *
+ * @param dsh this function will allocate and fill an AVDiracSeqHeader struct
+ *            and write it into this pointer. The caller must free it with
+ *            av_free().
+ * @param buf the data buffer
+ * @param buf_size the size of the data buffer in bytes
+ * @param log_ctx if non-NULL, this function will log errors here
+ * @return 0 on success, a negative AVERROR code on failure
+ */
+int av_dirac_parse_sequence_header(AVDiracSeqHeader **dsh,
+                                   const uint8_t *buf, size_t buf_size,
+                                   void *log_ctx);
+
+#endif /* AVCODEC_DIRAC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dv_profile.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dv_profile.h
new file mode 100644
index 0000000..9380a66
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dv_profile.h
@@ -0,0 +1,83 @@
+/*
+ * 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_DV_PROFILE_H
+#define AVCODEC_DV_PROFILE_H
+
+#include <stdint.h>
+
+#include "libavutil/pixfmt.h"
+#include "libavutil/rational.h"
+#include "avcodec.h"
+
+/* minimum number of bytes to read from a DV stream in order to
+ * determine the profile */
+#define DV_PROFILE_BYTES (6 * 80) /* 6 DIF blocks */
+
+
+/*
+ * AVDVProfile is used to express the differences between various
+ * DV flavors. For now it's primarily used for differentiating
+ * 525/60 and 625/50, but the plans are to use it for various
+ * DV specs as well (e.g. SMPTE314M vs. IEC 61834).
+ */
+typedef struct AVDVProfile {
+    int              dsf;                   /* value of the dsf in the DV header */
+    int              video_stype;           /* stype for VAUX source pack */
+    int              frame_size;            /* total size of one frame in bytes */
+    int              difseg_size;           /* number of DIF segments per DIF channel */
+    int              n_difchan;             /* number of DIF channels per frame */
+    AVRational       time_base;             /* 1/framerate */
+    int              ltc_divisor;           /* FPS from the LTS standpoint */
+    int              height;                /* picture height in pixels */
+    int              width;                 /* picture width in pixels */
+    AVRational       sar[2];                /* sample aspect ratios for 4:3 and 16:9 */
+    enum AVPixelFormat pix_fmt;             /* picture pixel format */
+    int              bpm;                   /* blocks per macroblock */
+    const uint8_t   *block_sizes;           /* AC block sizes, in bits */
+    int              audio_stride;          /* size of audio_shuffle table */
+    int              audio_min_samples[3];  /* min amount of audio samples */
+                                            /* for 48kHz, 44.1kHz and 32kHz */
+    int              audio_samples_dist[5]; /* how many samples are supposed to be */
+                                            /* in each frame in a 5 frames window */
+    const uint8_t  (*audio_shuffle)[9];     /* PCM shuffling table */
+} AVDVProfile;
+
+/**
+ * Get a DV profile for the provided compressed frame.
+ *
+ * @param sys the profile used for the previous frame, may be NULL
+ * @param frame the compressed data buffer
+ * @param buf_size size of the buffer in bytes
+ * @return the DV profile for the supplied data or NULL on failure
+ */
+const AVDVProfile *av_dv_frame_profile(const AVDVProfile *sys,
+                                       const uint8_t *frame, unsigned buf_size);
+
+/**
+ * Get a DV profile for the provided stream parameters.
+ */
+const AVDVProfile *av_dv_codec_profile(int width, int height, enum AVPixelFormat pix_fmt);
+
+/**
+ * Get a DV profile for the provided stream parameters.
+ * The frame rate is used as a best-effort parameter.
+ */
+const AVDVProfile *av_dv_codec_profile2(int width, int height, enum AVPixelFormat pix_fmt, AVRational frame_rate);
+
+#endif /* AVCODEC_DV_PROFILE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dxva2.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dxva2.h
new file mode 100644
index 0000000..22c9399
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/dxva2.h
@@ -0,0 +1,93 @@
+/*
+ * DXVA2 HW acceleration
+ *
+ * copyright (c) 2009 Laurent Aimar
+ *
+ * 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_DXVA2_H
+#define AVCODEC_DXVA2_H
+
+/**
+ * @file
+ * @ingroup lavc_codec_hwaccel_dxva2
+ * Public libavcodec DXVA2 header.
+ */
+
+#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0602
+#endif
+
+#include <stdint.h>
+#include <d3d9.h>
+#include <dxva2api.h>
+
+/**
+ * @defgroup lavc_codec_hwaccel_dxva2 DXVA2
+ * @ingroup lavc_codec_hwaccel
+ *
+ * @{
+ */
+
+#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2 and old UVD/UVD+ ATI video cards
+#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO    2 ///< Work around for DXVA2 and old Intel GPUs with ClearVideo interface
+
+/**
+ * This structure is used to provides the necessary configurations and data
+ * to the DXVA2 FFmpeg HWAccel implementation.
+ *
+ * The application must make it available as AVCodecContext.hwaccel_context.
+ */
+struct dxva_context {
+    /**
+     * DXVA2 decoder object
+     */
+    IDirectXVideoDecoder *decoder;
+
+    /**
+     * DXVA2 configuration used to create the decoder
+     */
+    const DXVA2_ConfigPictureDecode *cfg;
+
+    /**
+     * The number of surface in the surface array
+     */
+    unsigned surface_count;
+
+    /**
+     * The array of Direct3D surfaces used to create the decoder
+     */
+    LPDIRECT3DSURFACE9 *surface;
+
+    /**
+     * A bit field configuring the workarounds needed for using the decoder
+     */
+    uint64_t workaround;
+
+    /**
+     * Private to the FFmpeg AVHWAccel implementation
+     */
+    unsigned report_id;
+};
+
+/**
+ * @}
+ */
+
+#endif /* AVCODEC_DXVA2_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/jni.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/jni.h
new file mode 100644
index 0000000..dd99e92
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/jni.h
@@ -0,0 +1,46 @@
+/*
+ * JNI public API functions
+ *
+ * Copyright (c) 2015-2016 Matthieu Bouron <matthieu.bouron stupeflix.com>
+ *
+ * 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_JNI_H
+#define AVCODEC_JNI_H
+
+/*
+ * Manually set a Java virtual machine which will be used to retrieve the JNI
+ * environment. Once a Java VM is set it cannot be changed afterwards, meaning
+ * you can call multiple times av_jni_set_java_vm with the same Java VM pointer
+ * however it will error out if you try to set a different Java VM.
+ *
+ * @param vm Java virtual machine
+ * @param log_ctx context used for logging, can be NULL
+ * @return 0 on success, < 0 otherwise
+ */
+int av_jni_set_java_vm(void *vm, void *log_ctx);
+
+/*
+ * Get the Java virtual machine which has been set with av_jni_set_java_vm.
+ *
+ * @param vm Java virtual machine
+ * @return a pointer to the Java virtual machine
+ */
+void *av_jni_get_java_vm(void *log_ctx);
+
+#endif /* AVCODEC_JNI_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/mediacodec.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/mediacodec.h
new file mode 100644
index 0000000..4c8545d
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/mediacodec.h
@@ -0,0 +1,101 @@
+/*
+ * Android MediaCodec public API
+ *
+ * Copyright (c) 2016 Matthieu Bouron <matthieu.bouron stupeflix.com>
+ *
+ * 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_MEDIACODEC_H
+#define AVCODEC_MEDIACODEC_H
+
+#include "libavcodec/avcodec.h"
+
+/**
+ * This structure holds a reference to a android/view/Surface object that will
+ * be used as output by the decoder.
+ *
+ */
+typedef struct AVMediaCodecContext {
+
+    /**
+     * android/view/Surface object reference.
+     */
+    void *surface;
+
+} AVMediaCodecContext;
+
+/**
+ * Allocate and initialize a MediaCodec context.
+ *
+ * When decoding with MediaCodec is finished, the caller must free the
+ * MediaCodec context with av_mediacodec_default_free.
+ *
+ * @return a pointer to a newly allocated AVMediaCodecContext on success, NULL otherwise
+ */
+AVMediaCodecContext *av_mediacodec_alloc_context(void);
+
+/**
+ * Convenience function that sets up the MediaCodec context.
+ *
+ * @param avctx codec context
+ * @param ctx MediaCodec context to initialize
+ * @param surface reference to an android/view/Surface
+ * @return 0 on success, < 0 otherwise
+ */
+int av_mediacodec_default_init(AVCodecContext *avctx, AVMediaCodecContext *ctx, void *surface);
+
+/**
+ * This function must be called to free the MediaCodec context initialized with
+ * av_mediacodec_default_init().
+ *
+ * @param avctx codec context
+ */
+void av_mediacodec_default_free(AVCodecContext *avctx);
+
+/**
+ * Opaque structure representing a MediaCodec buffer to render.
+ */
+typedef struct MediaCodecBuffer AVMediaCodecBuffer;
+
+/**
+ * Release a MediaCodec buffer and render it to the surface that is associated
+ * with the decoder. This function should only be called once on a given
+ * buffer, once released the underlying buffer returns to the codec, thus
+ * subsequent calls to this function will have no effect.
+ *
+ * @param buffer the buffer to render
+ * @param render 1 to release and render the buffer to the surface or 0 to
+ * discard the buffer
+ * @return 0 on success, < 0 otherwise
+ */
+int av_mediacodec_release_buffer(AVMediaCodecBuffer *buffer, int render);
+
+/**
+ * Release a MediaCodec buffer and render it at the given time to the surface
+ * that is associated with the decoder. The timestamp must be within one second
+ * of the current java/lang/System#nanoTime() (which is implemented using
+ * CLOCK_MONOTONIC on Android). See the Android MediaCodec documentation
+ * of android/media/MediaCodec#releaseOutputBuffer(int,long) for more details.
+ *
+ * @param buffer the buffer to render
+ * @param time timestamp in nanoseconds of when to render the buffer
+ * @return 0 on success, < 0 otherwise
+ */
+int av_mediacodec_render_buffer_at_time(AVMediaCodecBuffer *buffer, int64_t time);
+
+#endif /* AVCODEC_MEDIACODEC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/qsv.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/qsv.h
new file mode 100644
index 0000000..b77158e
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/qsv.h
@@ -0,0 +1,107 @@
+/*
+ * Intel MediaSDK QSV public API
+ *
+ * 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_QSV_H
+#define AVCODEC_QSV_H
+
+#include <mfx/mfxvideo.h>
+
+#include "libavutil/buffer.h"
+
+/**
+ * This struct is used for communicating QSV parameters between libavcodec and
+ * the caller. It is managed by the caller and must be assigned to
+ * AVCodecContext.hwaccel_context.
+ * - decoding: hwaccel_context must be set on return from the get_format()
+ *             callback
+ * - encoding: hwaccel_context must be set before avcodec_open2()
+ */
+typedef struct AVQSVContext {
+    /**
+     * If non-NULL, the session to use for encoding or decoding.
+     * Otherwise, libavcodec will try to create an internal session.
+     */
+    mfxSession session;
+
+    /**
+     * The IO pattern to use.
+     */
+    int iopattern;
+
+    /**
+     * Extra buffers to pass to encoder or decoder initialization.
+     */
+    mfxExtBuffer **ext_buffers;
+    int         nb_ext_buffers;
+
+    /**
+     * Encoding only. If this field is set to non-zero by the caller, libavcodec
+     * will create an mfxExtOpaqueSurfaceAlloc extended buffer and pass it to
+     * the encoder initialization. This only makes sense if iopattern is also
+     * set to MFX_IOPATTERN_IN_OPAQUE_MEMORY.
+     *
+     * The number of allocated opaque surfaces will be the sum of the number
+     * required by the encoder and the user-provided value nb_opaque_surfaces.
+     * The array of the opaque surfaces will be exported to the caller through
+     * the opaque_surfaces field.
+     */
+    int opaque_alloc;
+
+    /**
+     * Encoding only, and only if opaque_alloc is set to non-zero. Before
+     * calling avcodec_open2(), the caller should set this field to the number
+     * of extra opaque surfaces to allocate beyond what is required by the
+     * encoder.
+     *
+     * On return from avcodec_open2(), this field will be set by libavcodec to
+     * the total number of allocated opaque surfaces.
+     */
+    int nb_opaque_surfaces;
+
+    /**
+     * Encoding only, and only if opaque_alloc is set to non-zero. On return
+     * from avcodec_open2(), this field will be used by libavcodec to export the
+     * array of the allocated opaque surfaces to the caller, so they can be
+     * passed to other parts of the pipeline.
+     *
+     * The buffer reference exported here is owned and managed by libavcodec,
+     * the callers should make their own reference with av_buffer_ref() and free
+     * it with av_buffer_unref() when it is no longer needed.
+     *
+     * The buffer data is an nb_opaque_surfaces-sized array of mfxFrameSurface1.
+     */
+    AVBufferRef *opaque_surfaces;
+
+    /**
+     * Encoding only, and only if opaque_alloc is set to non-zero. On return
+     * from avcodec_open2(), this field will be set to the surface type used in
+     * the opaque allocation request.
+     */
+    int opaque_alloc_type;
+} AVQSVContext;
+
+/**
+ * Allocate a new context.
+ *
+ * It must be freed by the caller with av_free().
+ */
+AVQSVContext *av_qsv_alloc_context(void);
+
+#endif /* AVCODEC_QSV_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vaapi.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vaapi.h
new file mode 100644
index 0000000..2cf7da5
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vaapi.h
@@ -0,0 +1,86 @@
+/*
+ * Video Acceleration API (shared data between FFmpeg and the video player)
+ * HW decode acceleration for MPEG-2, MPEG-4, H.264 and VC-1
+ *
+ * Copyright (C) 2008-2009 Splitted-Desktop Systems
+ *
+ * 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_VAAPI_H
+#define AVCODEC_VAAPI_H
+
+/**
+ * @file
+ * @ingroup lavc_codec_hwaccel_vaapi
+ * Public libavcodec VA API header.
+ */
+
+#include <stdint.h>
+#include "libavutil/attributes.h"
+#include "version.h"
+
+#if FF_API_STRUCT_VAAPI_CONTEXT
+
+/**
+ * @defgroup lavc_codec_hwaccel_vaapi VA API Decoding
+ * @ingroup lavc_codec_hwaccel
+ * @{
+ */
+
+/**
+ * This structure is used to share data between the FFmpeg library and
+ * the client video application.
+ * This shall be zero-allocated and available as
+ * AVCodecContext.hwaccel_context. All user members can be set once
+ * during initialization or through each AVCodecContext.get_buffer()
+ * function call. In any case, they must be valid prior to calling
+ * decoding functions.
+ *
+ * Deprecated: use AVCodecContext.hw_frames_ctx instead.
+ */
+struct attribute_deprecated vaapi_context {
+    /**
+     * Window system dependent data
+     *
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    void *display;
+
+    /**
+     * Configuration ID
+     *
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    uint32_t config_id;
+
+    /**
+     * Context ID (video decode pipeline)
+     *
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    uint32_t context_id;
+};
+
+/* @} */
+
+#endif /* FF_API_STRUCT_VAAPI_CONTEXT */
+
+#endif /* AVCODEC_VAAPI_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vdpau.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vdpau.h
new file mode 100644
index 0000000..4d99943
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vdpau.h
@@ -0,0 +1,176 @@
+/*
+ * The Video Decode and Presentation API for UNIX (VDPAU) is used for
+ * hardware-accelerated decoding of MPEG-1/2, H.264 and VC-1.
+ *
+ * Copyright (C) 2008 NVIDIA
+ *
+ * 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_VDPAU_H
+#define AVCODEC_VDPAU_H
+
+/**
+ * @file
+ * @ingroup lavc_codec_hwaccel_vdpau
+ * Public libavcodec VDPAU header.
+ */
+
+
+/**
+ * @defgroup lavc_codec_hwaccel_vdpau VDPAU Decoder and Renderer
+ * @ingroup lavc_codec_hwaccel
+ *
+ * VDPAU hardware acceleration has two modules
+ * - VDPAU decoding
+ * - VDPAU presentation
+ *
+ * The VDPAU decoding module parses all headers using FFmpeg
+ * parsing mechanisms and uses VDPAU for the actual decoding.
+ *
+ * As per the current implementation, the actual decoding
+ * and rendering (API calls) are done as part of the VDPAU
+ * presentation (vo_vdpau.c) module.
+ *
+ * @{
+ */
+
+#include <vdpau/vdpau.h>
+
+#include "libavutil/avconfig.h"
+#include "libavutil/attributes.h"
+
+#include "avcodec.h"
+#include "version.h"
+
+struct AVCodecContext;
+struct AVFrame;
+
+typedef int (*AVVDPAU_Render2)(struct AVCodecContext *, struct AVFrame *,
+                               const VdpPictureInfo *, uint32_t,
+                               const VdpBitstreamBuffer *);
+
+/**
+ * This structure is used to share data between the libavcodec library and
+ * the client video application.
+ * The user shall allocate the structure via the av_alloc_vdpau_hwaccel
+ * function and make it available as
+ * AVCodecContext.hwaccel_context. Members can be set by the user once
+ * during initialization or through each AVCodecContext.get_buffer()
+ * function call. In any case, they must be valid prior to calling
+ * decoding functions.
+ *
+ * The size of this structure is not a part of the public ABI and must not
+ * be used outside of libavcodec. Use av_vdpau_alloc_context() to allocate an
+ * AVVDPAUContext.
+ */
+typedef struct AVVDPAUContext {
+    /**
+     * VDPAU decoder handle
+     *
+     * Set by user.
+     */
+    VdpDecoder decoder;
+
+    /**
+     * VDPAU decoder render callback
+     *
+     * Set by the user.
+     */
+    VdpDecoderRender *render;
+
+    AVVDPAU_Render2 render2;
+} AVVDPAUContext;
+
+/**
+ * @brief allocation function for AVVDPAUContext
+ *
+ * Allows extending the struct without breaking API/ABI
+ */
+AVVDPAUContext *av_alloc_vdpaucontext(void);
+
+AVVDPAU_Render2 av_vdpau_hwaccel_get_render2(const AVVDPAUContext *);
+void av_vdpau_hwaccel_set_render2(AVVDPAUContext *, AVVDPAU_Render2);
+
+/**
+ * Associate a VDPAU device with a codec context for hardware acceleration.
+ * This function is meant to be called from the get_format() codec callback,
+ * or earlier. It can also be called after avcodec_flush_buffers() to change
+ * the underlying VDPAU device mid-stream (e.g. to recover from non-transparent
+ * display preemption).
+ *
+ * @note get_format() must return AV_PIX_FMT_VDPAU if this function completes
+ * successfully.
+ *
+ * @param avctx decoding context whose get_format() callback is invoked
+ * @param device VDPAU device handle to use for hardware acceleration
+ * @param get_proc_address VDPAU device driver
+ * @param flags zero of more OR'd AV_HWACCEL_FLAG_* flags
+ *
+ * @return 0 on success, an AVERROR code on failure.
+ */
+int av_vdpau_bind_context(AVCodecContext *avctx, VdpDevice device,
+                          VdpGetProcAddress *get_proc_address, unsigned flags);
+
+/**
+ * Gets the parameters to create an adequate VDPAU video surface for the codec
+ * context using VDPAU hardware decoding acceleration.
+ *
+ * @note Behavior is undefined if the context was not successfully bound to a
+ * VDPAU device using av_vdpau_bind_context().
+ *
+ * @param avctx the codec context being used for decoding the stream
+ * @param type storage space for the VDPAU video surface chroma type
+ *              (or NULL to ignore)
+ * @param width storage space for the VDPAU video surface pixel width
+ *              (or NULL to ignore)
+ * @param height storage space for the VDPAU video surface pixel height
+ *              (or NULL to ignore)
+ *
+ * @return 0 on success, a negative AVERROR code on failure.
+ */
+int av_vdpau_get_surface_parameters(AVCodecContext *avctx, VdpChromaType *type,
+                                    uint32_t *width, uint32_t *height);
+
+/**
+ * Allocate an AVVDPAUContext.
+ *
+ * @return Newly-allocated AVVDPAUContext or NULL on failure.
+ */
+AVVDPAUContext *av_vdpau_alloc_context(void);
+
+#if FF_API_VDPAU_PROFILE
+/**
+ * Get a decoder profile that should be used for initializing a VDPAU decoder.
+ * Should be called from the AVCodecContext.get_format() callback.
+ *
+ * @deprecated Use av_vdpau_bind_context() instead.
+ *
+ * @param avctx the codec context being used for decoding the stream
+ * @param profile a pointer into which the result will be written on success.
+ *                The contents of profile are undefined if this function returns
+ *                an error.
+ *
+ * @return 0 on success (non-negative), a negative AVERROR on failure.
+ */
+attribute_deprecated
+int av_vdpau_get_profile(AVCodecContext *avctx, VdpDecoderProfile *profile);
+#endif
+
+/* @}*/
+
+#endif /* AVCODEC_VDPAU_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/version.h
new file mode 100644
index 0000000..782ba97
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/version.h
@@ -0,0 +1,137 @@
+/*
+ * 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_VERSION_H
+#define AVCODEC_VERSION_H
+
+/**
+ * @file
+ * @ingroup libavc
+ * Libavcodec version macros.
+ */
+
+#include "libavutil/version.h"
+
+#define LIBAVCODEC_VERSION_MAJOR  58
+#define LIBAVCODEC_VERSION_MINOR  35
+#define LIBAVCODEC_VERSION_MICRO 100
+
+#define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
+                                               LIBAVCODEC_VERSION_MINOR, \
+                                               LIBAVCODEC_VERSION_MICRO)
+#define LIBAVCODEC_VERSION      AV_VERSION(LIBAVCODEC_VERSION_MAJOR,    \
+                                           LIBAVCODEC_VERSION_MINOR,    \
+                                           LIBAVCODEC_VERSION_MICRO)
+#define LIBAVCODEC_BUILD        LIBAVCODEC_VERSION_INT
+
+#define LIBAVCODEC_IDENT        "Lavc" AV_STRINGIFY(LIBAVCODEC_VERSION)
+
+/**
+ * FF_API_* defines may be placed below to indicate public API that will be
+ * dropped at a future version bump. The defines themselves are not part of
+ * the public API and may change, break or disappear at any time.
+ *
+ * @note, when bumping the major version it is recommended to manually
+ * disable each FF_API_* in its own commit instead of disabling them all
+ * at once through the bump. This improves the git bisect-ability of the change.
+ */
+
+#ifndef FF_API_LOWRES
+#define FF_API_LOWRES            (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_DEBUG_MV
+#define FF_API_DEBUG_MV          (LIBAVCODEC_VERSION_MAJOR < 58)
+#endif
+#ifndef FF_API_AVCTX_TIMEBASE
+#define FF_API_AVCTX_TIMEBASE    (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_CODED_FRAME
+#define FF_API_CODED_FRAME       (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_SIDEDATA_ONLY_PKT
+#define FF_API_SIDEDATA_ONLY_PKT (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_VDPAU_PROFILE
+#define FF_API_VDPAU_PROFILE     (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_CONVERGENCE_DURATION
+#define FF_API_CONVERGENCE_DURATION (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_AVPICTURE
+#define FF_API_AVPICTURE         (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_AVPACKET_OLD_API
+#define FF_API_AVPACKET_OLD_API (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_RTP_CALLBACK
+#define FF_API_RTP_CALLBACK      (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_VBV_DELAY
+#define FF_API_VBV_DELAY         (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_CODER_TYPE
+#define FF_API_CODER_TYPE        (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_STAT_BITS
+#define FF_API_STAT_BITS         (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_PRIVATE_OPT
+#define FF_API_PRIVATE_OPT      (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_ASS_TIMING
+#define FF_API_ASS_TIMING       (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_OLD_BSF
+#define FF_API_OLD_BSF          (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_COPY_CONTEXT
+#define FF_API_COPY_CONTEXT     (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_GET_CONTEXT_DEFAULTS
+#define FF_API_GET_CONTEXT_DEFAULTS (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_NVENC_OLD_NAME
+#define FF_API_NVENC_OLD_NAME    (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_STRUCT_VAAPI_CONTEXT
+#define FF_API_STRUCT_VAAPI_CONTEXT (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_MERGE_SD_API
+#define FF_API_MERGE_SD_API      (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_TAG_STRING
+#define FF_API_TAG_STRING        (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_GETCHROMA
+#define FF_API_GETCHROMA         (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_CODEC_GET_SET
+#define FF_API_CODEC_GET_SET     (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_USER_VISIBLE_AVHWACCEL
+#define FF_API_USER_VISIBLE_AVHWACCEL (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_LOCKMGR
+#define FF_API_LOCKMGR (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_NEXT
+#define FF_API_NEXT              (LIBAVCODEC_VERSION_MAJOR < 59)
+#endif
+
+
+#endif /* AVCODEC_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/videotoolbox.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/videotoolbox.h
new file mode 100644
index 0000000..af2db0d
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/videotoolbox.h
@@ -0,0 +1,127 @@
+/*
+ * Videotoolbox hardware acceleration
+ *
+ * copyright (c) 2012 Sebastien Zwickert
+ *
+ * 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_VIDEOTOOLBOX_H
+#define AVCODEC_VIDEOTOOLBOX_H
+
+/**
+ * @file
+ * @ingroup lavc_codec_hwaccel_videotoolbox
+ * Public libavcodec Videotoolbox header.
+ */
+
+#include <stdint.h>
+
+#define Picture QuickdrawPicture
+#include <VideoToolbox/VideoToolbox.h>
+#undef Picture
+
+#include "libavcodec/avcodec.h"
+
+/**
+ * This struct holds all the information that needs to be passed
+ * between the caller and libavcodec for initializing Videotoolbox decoding.
+ * Its size is not a part of the public ABI, it must be allocated with
+ * av_videotoolbox_alloc_context() and freed with av_free().
+ */
+typedef struct AVVideotoolboxContext {
+    /**
+     * Videotoolbox decompression session object.
+     * Created and freed the caller.
+     */
+    VTDecompressionSessionRef session;
+
+    /**
+     * The output callback that must be passed to the session.
+     * Set by av_videottoolbox_default_init()
+     */
+    VTDecompressionOutputCallback output_callback;
+
+    /**
+     * CVPixelBuffer Format Type that Videotoolbox will use for decoded frames.
+     * set by the caller. If this is set to 0, then no specific format is
+     * requested from the decoder, and its native format is output.
+     */
+    OSType cv_pix_fmt_type;
+
+    /**
+     * CoreMedia Format Description that Videotoolbox will use to create the decompression session.
+     * Set by the caller.
+     */
+    CMVideoFormatDescriptionRef cm_fmt_desc;
+
+    /**
+     * CoreMedia codec type that Videotoolbox will use to create the decompression session.
+     * Set by the caller.
+     */
+    int cm_codec_type;
+} AVVideotoolboxContext;
+
+/**
+ * Allocate and initialize a Videotoolbox context.
+ *
+ * This function should be called from the get_format() callback when the caller
+ * selects the AV_PIX_FMT_VIDETOOLBOX format. The caller must then create
+ * the decoder object (using the output callback provided by libavcodec) that
+ * will be used for Videotoolbox-accelerated decoding.
+ *
+ * When decoding with Videotoolbox is finished, the caller must destroy the decoder
+ * object and free the Videotoolbox context using av_free().
+ *
+ * @return the newly allocated context or NULL on failure
+ */
+AVVideotoolboxContext *av_videotoolbox_alloc_context(void);
+
+/**
+ * This is a convenience function that creates and sets up the Videotoolbox context using
+ * an internal implementation.
+ *
+ * @param avctx the corresponding codec context
+ *
+ * @return >= 0 on success, a negative AVERROR code on failure
+ */
+int av_videotoolbox_default_init(AVCodecContext *avctx);
+
+/**
+ * This is a convenience function that creates and sets up the Videotoolbox context using
+ * an internal implementation.
+ *
+ * @param avctx the corresponding codec context
+ * @param vtctx the Videotoolbox context to use
+ *
+ * @return >= 0 on success, a negative AVERROR code on failure
+ */
+int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext *vtctx);
+
+/**
+ * This function must be called to free the Videotoolbox context initialized with
+ * av_videotoolbox_default_init().
+ *
+ * @param avctx the corresponding codec context
+ */
+void av_videotoolbox_default_free(AVCodecContext *avctx);
+
+/**
+ * @}
+ */
+
+#endif /* AVCODEC_VIDEOTOOLBOX_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vorbis_parser.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vorbis_parser.h
new file mode 100644
index 0000000..789932a
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/vorbis_parser.h
@@ -0,0 +1,74 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * A public API for Vorbis parsing
+ *
+ * Determines the duration for each packet.
+ */
+
+#ifndef AVCODEC_VORBIS_PARSER_H
+#define AVCODEC_VORBIS_PARSER_H
+
+#include <stdint.h>
+
+typedef struct AVVorbisParseContext AVVorbisParseContext;
+
+/**
+ * Allocate and initialize the Vorbis parser using headers in the extradata.
+ */
+AVVorbisParseContext *av_vorbis_parse_init(const uint8_t *extradata,
+                                           int extradata_size);
+
+/**
+ * Free the parser and everything associated with it.
+ */
+void av_vorbis_parse_free(AVVorbisParseContext **s);
+
+#define VORBIS_FLAG_HEADER  0x00000001
+#define VORBIS_FLAG_COMMENT 0x00000002
+#define VORBIS_FLAG_SETUP   0x00000004
+
+/**
+ * Get the duration for a Vorbis packet.
+ *
+ * If @p flags is @c NULL,
+ * special frames are considered invalid.
+ *
+ * @param s        Vorbis parser context
+ * @param buf      buffer containing a Vorbis frame
+ * @param buf_size size of the buffer
+ * @param flags    flags for special frames
+ */
+int av_vorbis_parse_frame_flags(AVVorbisParseContext *s, const uint8_t *buf,
+                                int buf_size, int *flags);
+
+/**
+ * Get the duration for a Vorbis packet.
+ *
+ * @param s        Vorbis parser context
+ * @param buf      buffer containing a Vorbis frame
+ * @param buf_size size of the buffer
+ */
+int av_vorbis_parse_frame(AVVorbisParseContext *s, const uint8_t *buf,
+                          int buf_size);
+
+void av_vorbis_parse_reset(AVVorbisParseContext *s);
+
+#endif /* AVCODEC_VORBIS_PARSER_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/xvmc.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/xvmc.h
new file mode 100644
index 0000000..465ee78
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavcodec/xvmc.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2003 Ivan Kalvachev
+ *
+ * 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_XVMC_H
+#define AVCODEC_XVMC_H
+
+/**
+ * @file
+ * @ingroup lavc_codec_hwaccel_xvmc
+ * Public libavcodec XvMC header.
+ */
+
+#include <X11/extensions/XvMC.h>
+
+#include "libavutil/attributes.h"
+#include "version.h"
+#include "avcodec.h"
+
+/**
+ * @defgroup lavc_codec_hwaccel_xvmc XvMC
+ * @ingroup lavc_codec_hwaccel
+ *
+ * @{
+ */
+
+#define AV_XVMC_ID                    0x1DC711C0  /**< special value to ensure that regular pixel routines haven't corrupted the struct
+                                                       the number is 1337 speak for the letters IDCT MCo (motion compensation) */
+
+struct attribute_deprecated xvmc_pix_fmt {
+    /** The field contains the special constant value AV_XVMC_ID.
+        It is used as a test that the application correctly uses the API,
+        and that there is no corruption caused by pixel routines.
+        - application - set during initialization
+        - libavcodec  - unchanged
+    */
+    int             xvmc_id;
+
+    /** Pointer to the block array allocated by XvMCCreateBlocks().
+        The array has to be freed by XvMCDestroyBlocks().
+        Each group of 64 values represents one data block of differential
+        pixel information (in MoCo mode) or coefficients for IDCT.
+        - application - set the pointer during initialization
+        - libavcodec  - fills coefficients/pixel data into the array
+    */
+    short*          data_blocks;
+
+    /** Pointer to the macroblock description array allocated by
+        XvMCCreateMacroBlocks() and freed by XvMCDestroyMacroBlocks().
+        - application - set the pointer during initialization
+        - libavcodec  - fills description data into the array
+    */
+    XvMCMacroBlock* mv_blocks;
+
+    /** Number of macroblock descriptions that can be stored in the mv_blocks
+        array.
+        - application - set during initialization
+        - libavcodec  - unchanged
+    */
+    int             allocated_mv_blocks;
+
+    /** Number of blocks that can be stored at once in the data_blocks array.
+        - application - set during initialization
+        - libavcodec  - unchanged
+    */
+    int             allocated_data_blocks;
+
+    /** Indicate that the hardware would interpret data_blocks as IDCT
+        coefficients and perform IDCT on them.
+        - application - set during initialization
+        - libavcodec  - unchanged
+    */
+    int             idct;
+
+    /** In MoCo mode it indicates that intra macroblocks are assumed to be in
+        unsigned format; same as the XVMC_INTRA_UNSIGNED flag.
+        - application - set during initialization
+        - libavcodec  - unchanged
+    */
+    int             unsigned_intra;
+
+    /** Pointer to the surface allocated by XvMCCreateSurface().
+        It has to be freed by XvMCDestroySurface() on application exit.
+        It identifies the frame and its state on the video hardware.
+        - application - set during initialization
+        - libavcodec  - unchanged
+    */
+    XvMCSurface*    p_surface;
+
+/** Set by the decoder before calling ff_draw_horiz_band(),
+    needed by the XvMCRenderSurface function. */
+//@{
+    /** Pointer to the surface used as past reference
+        - application - unchanged
+        - libavcodec  - set
+    */
+    XvMCSurface*    p_past_surface;
+
+    /** Pointer to the surface used as future reference
+        - application - unchanged
+        - libavcodec  - set
+    */
+    XvMCSurface*    p_future_surface;
+
+    /** top/bottom field or frame
+        - application - unchanged
+        - libavcodec  - set
+    */
+    unsigned int    picture_structure;
+
+    /** XVMC_SECOND_FIELD - 1st or 2nd field in the sequence
+        - application - unchanged
+        - libavcodec  - set
+    */
+    unsigned int    flags;
+//}@
+
+    /** Number of macroblock descriptions in the mv_blocks array
+        that have already been passed to the hardware.
+        - application - zeroes it on get_buffer().
+                        A successful ff_draw_horiz_band() may increment it
+                        with filled_mb_block_num or zero both.
+        - libavcodec  - unchanged
+    */
+    int             start_mv_blocks_num;
+
+    /** Number of new macroblock descriptions in the mv_blocks array (after
+        start_mv_blocks_num) that are filled by libavcodec and have to be
+        passed to the hardware.
+        - application - zeroes it on get_buffer() or after successful
+                        ff_draw_horiz_band().
+        - libavcodec  - increment with one of each stored MB
+    */
+    int             filled_mv_blocks_num;
+
+    /** Number of the next free data block; one data block consists of
+        64 short values in the data_blocks array.
+        All blocks before this one have already been claimed by placing their
+        position into the corresponding block description structure field,
+        that are part of the mv_blocks array.
+        - application - zeroes it on get_buffer().
+                        A successful ff_draw_horiz_band() may zero it together
+                        with start_mb_blocks_num.
+        - libavcodec  - each decoded macroblock increases it by the number
+                        of coded blocks it contains.
+    */
+    int             next_free_data_block_num;
+};
+
+/**
+ * @}
+ */
+
+#endif /* AVCODEC_XVMC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avformat.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avformat.h
new file mode 100644
index 0000000..fdaffa5
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avformat.h
@@ -0,0 +1,3085 @@
+/*
+ * 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 AVFORMAT_AVFORMAT_H
+#define AVFORMAT_AVFORMAT_H
+
+/**
+ * @file
+ * @ingroup libavf
+ * Main libavformat public API header
+ */
+
+/**
+ * @defgroup libavf libavformat
+ * I/O and Muxing/Demuxing Library
+ *
+ * Libavformat (lavf) is a library for dealing with various media container
+ * formats. Its main two purposes are demuxing - i.e. splitting a media file
+ * into component streams, and the reverse process of muxing - writing supplied
+ * data in a specified container format. It also has an @ref lavf_io
+ * "I/O module" which supports a number of protocols for accessing the data (e.g.
+ * file, tcp, http and others). Before using lavf, you need to call
+ * av_register_all() to register all compiled muxers, demuxers and protocols.
+ * Unless you are absolutely sure you won't use libavformat's network
+ * capabilities, you should also call avformat_network_init().
+ *
+ * A supported input format is described by an AVInputFormat struct, conversely
+ * an output format is described by AVOutputFormat. You can iterate over all
+ * registered input/output formats using the av_iformat_next() /
+ * av_oformat_next() functions. The protocols layer is not part of the public
+ * API, so you can only get the names of supported protocols with the
+ * avio_enum_protocols() function.
+ *
+ * Main lavf structure used for both muxing and demuxing is AVFormatContext,
+ * which exports all information about the file being read or written. As with
+ * most Libavformat structures, its size is not part of public ABI, so it cannot be
+ * allocated on stack or directly with av_malloc(). To create an
+ * AVFormatContext, use avformat_alloc_context() (some functions, like
+ * avformat_open_input() might do that for you).
+ *
+ * Most importantly an AVFormatContext contains:
+ * @li the @ref AVFormatContext.iformat "input" or @ref AVFormatContext.oformat
+ * "output" format. It is either autodetected or set by user for input;
+ * always set by user for output.
+ * @li an @ref AVFormatContext.streams "array" of AVStreams, which describe all
+ * elementary streams stored in the file. AVStreams are typically referred to
+ * using their index in this array.
+ * @li an @ref AVFormatContext.pb "I/O context". It is either opened by lavf or
+ * set by user for input, always set by user for output (unless you are dealing
+ * with an AVFMT_NOFILE format).
+ *
+ * @section lavf_options Passing options to (de)muxers
+ * It is possible to configure lavf muxers and demuxers using the @ref avoptions
+ * mechanism. Generic (format-independent) libavformat options are provided by
+ * AVFormatContext, they can be examined from a user program by calling
+ * av_opt_next() / av_opt_find() on an allocated AVFormatContext (or its AVClass
+ * from avformat_get_class()). Private (format-specific) options are provided by
+ * AVFormatContext.priv_data if and only if AVInputFormat.priv_class /
+ * AVOutputFormat.priv_class of the corresponding format struct is non-NULL.
+ * Further options may be provided by the @ref AVFormatContext.pb "I/O context",
+ * if its AVClass is non-NULL, and the protocols layer. See the discussion on
+ * nesting in @ref avoptions documentation to learn how to access those.
+ *
+ * @section urls
+ * URL strings in libavformat are made of a scheme/protocol, a ':', and a
+ * scheme specific string. URLs without a scheme and ':' used for local files
+ * are supported but deprecated. "file:" should be used for local files.
+ *
+ * It is important that the scheme string is not taken from untrusted
+ * sources without checks.
+ *
+ * Note that some schemes/protocols are quite powerful, allowing access to
+ * both local and remote files, parts of them, concatenations of them, local
+ * audio and video devices and so on.
+ *
+ * @{
+ *
+ * @defgroup lavf_decoding Demuxing
+ * @{
+ * Demuxers read a media file and split it into chunks of data (@em packets). A
+ * @ref AVPacket "packet" contains one or more encoded frames which belongs to a
+ * single elementary stream. In the lavf API this process is represented by the
+ * avformat_open_input() function for opening a file, av_read_frame() for
+ * reading a single packet and finally avformat_close_input(), which does the
+ * cleanup.
+ *
+ * @section lavf_decoding_open Opening a media file
+ * The minimum information required to open a file is its URL, which
+ * is passed to avformat_open_input(), as in the following code:
+ * @code
+ * const char    *url = "file:in.mp3";
+ * AVFormatContext *s = NULL;
+ * int ret = avformat_open_input(&s, url, NULL, NULL);
+ * if (ret < 0)
+ *     abort();
+ * @endcode
+ * The above code attempts to allocate an AVFormatContext, open the
+ * specified file (autodetecting the format) and read the header, exporting the
+ * information stored there into s. Some formats do not have a header or do not
+ * store enough information there, so it is recommended that you call the
+ * avformat_find_stream_info() function which tries to read and decode a few
+ * frames to find missing information.
+ *
+ * In some cases you might want to preallocate an AVFormatContext yourself with
+ * avformat_alloc_context() and do some tweaking on it before passing it to
+ * avformat_open_input(). One such case is when you want to use custom functions
+ * for reading input data instead of lavf internal I/O layer.
+ * To do that, create your own AVIOContext with avio_alloc_context(), passing
+ * your reading callbacks to it. Then set the @em pb field of your
+ * AVFormatContext to newly created AVIOContext.
+ *
+ * Since the format of the opened file is in general not known until after
+ * avformat_open_input() has returned, it is not possible to set demuxer private
+ * options on a preallocated context. Instead, the options should be passed to
+ * avformat_open_input() wrapped in an AVDictionary:
+ * @code
+ * AVDictionary *options = NULL;
+ * av_dict_set(&options, "video_size", "640x480", 0);
+ * av_dict_set(&options, "pixel_format", "rgb24", 0);
+ *
+ * if (avformat_open_input(&s, url, NULL, &options) < 0)
+ *     abort();
+ * av_dict_free(&options);
+ * @endcode
+ * This code passes the private options 'video_size' and 'pixel_format' to the
+ * demuxer. They would be necessary for e.g. the rawvideo demuxer, since it
+ * cannot know how to interpret raw video data otherwise. If the format turns
+ * out to be something different than raw video, those options will not be
+ * recognized by the demuxer and therefore will not be applied. Such unrecognized
+ * options are then returned in the options dictionary (recognized options are
+ * consumed). The calling program can handle such unrecognized options as it
+ * wishes, e.g.
+ * @code
+ * AVDictionaryEntry *e;
+ * if (e = av_dict_get(options, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
+ *     fprintf(stderr, "Option %s not recognized by the demuxer.\n", e->key);
+ *     abort();
+ * }
+ * @endcode
+ *
+ * After you have finished reading the file, you must close it with
+ * avformat_close_input(). It will free everything associated with the file.
+ *
+ * @section lavf_decoding_read Reading from an opened file
+ * Reading data from an opened AVFormatContext is done by repeatedly calling
+ * av_read_frame() on it. Each call, if successful, will return an AVPacket
+ * containing encoded data for one AVStream, identified by
+ * AVPacket.stream_index. This packet may be passed straight into the libavcodec
+ * decoding functions avcodec_send_packet() or avcodec_decode_subtitle2() if the
+ * caller wishes to decode the data.
+ *
+ * AVPacket.pts, AVPacket.dts and AVPacket.duration timing information will be
+ * set if known. They may also be unset (i.e. AV_NOPTS_VALUE for
+ * pts/dts, 0 for duration) if the stream does not provide them. The timing
+ * information will be in AVStream.time_base units, i.e. it has to be
+ * multiplied by the timebase to convert them to seconds.
+ *
+ * If AVPacket.buf is set on the returned packet, then the packet is
+ * allocated dynamically and the user may keep it indefinitely.
+ * Otherwise, if AVPacket.buf is NULL, the packet data is backed by a
+ * static storage somewhere inside the demuxer and the packet is only valid
+ * until the next av_read_frame() call or closing the file. If the caller
+ * requires a longer lifetime, av_dup_packet() will make an av_malloc()ed copy
+ * of it.
+ * In both cases, the packet must be freed with av_packet_unref() when it is no
+ * longer needed.
+ *
+ * @section lavf_decoding_seek Seeking
+ * @}
+ *
+ * @defgroup lavf_encoding Muxing
+ * @{
+ * Muxers take encoded data in the form of @ref AVPacket "AVPackets" and write
+ * it into files or other output bytestreams in the specified container format.
+ *
+ * The main API functions for muxing are avformat_write_header() for writing the
+ * file header, av_write_frame() / av_interleaved_write_frame() for writing the
+ * packets and av_write_trailer() for finalizing the file.
+ *
+ * At the beginning of the muxing process, the caller must first call
+ * avformat_alloc_context() to create a muxing context. The caller then sets up
+ * the muxer by filling the various fields in this context:
+ *
+ * - The @ref AVFormatContext.oformat "oformat" field must be set to select the
+ *   muxer that will be used.
+ * - Unless the format is of the AVFMT_NOFILE type, the @ref AVFormatContext.pb
+ *   "pb" field must be set to an opened IO context, either returned from
+ *   avio_open2() or a custom one.
+ * - Unless the format is of the AVFMT_NOSTREAMS type, at least one stream must
+ *   be created with the avformat_new_stream() function. The caller should fill
+ *   the @ref AVStream.codecpar "stream codec parameters" information, such as the
+ *   codec @ref AVCodecParameters.codec_type "type", @ref AVCodecParameters.codec_id
+ *   "id" and other parameters (e.g. width / height, the pixel or sample format,
+ *   etc.) as known. The @ref AVStream.time_base "stream timebase" should
+ *   be set to the timebase that the caller desires to use for this stream (note
+ *   that the timebase actually used by the muxer can be different, as will be
+ *   described later).
+ * - It is advised to manually initialize only the relevant fields in
+ *   AVCodecParameters, rather than using @ref avcodec_parameters_copy() during
+ *   remuxing: there is no guarantee that the codec context values remain valid
+ *   for both input and output format contexts.
+ * - The caller may fill in additional information, such as @ref
+ *   AVFormatContext.metadata "global" or @ref AVStream.metadata "per-stream"
+ *   metadata, @ref AVFormatContext.chapters "chapters", @ref
+ *   AVFormatContext.programs "programs", etc. as described in the
+ *   AVFormatContext documentation. Whether such information will actually be
+ *   stored in the output depends on what the container format and the muxer
+ *   support.
+ *
+ * When the muxing context is fully set up, the caller must call
+ * avformat_write_header() to initialize the muxer internals and write the file
+ * header. Whether anything actually is written to the IO context at this step
+ * depends on the muxer, but this function must always be called. Any muxer
+ * private options must be passed in the options parameter to this function.
+ *
+ * The data is then sent to the muxer by repeatedly calling av_write_frame() or
+ * av_interleaved_write_frame() (consult those functions' documentation for
+ * discussion on the difference between them; only one of them may be used with
+ * a single muxing context, they should not be mixed). Do note that the timing
+ * information on the packets sent to the muxer must be in the corresponding
+ * AVStream's timebase. That timebase is set by the muxer (in the
+ * avformat_write_header() step) and may be different from the timebase
+ * requested by the caller.
+ *
+ * Once all the data has been written, the caller must call av_write_trailer()
+ * to flush any buffered packets and finalize the output file, then close the IO
+ * context (if any) and finally free the muxing context with
+ * avformat_free_context().
+ * @}
+ *
+ * @defgroup lavf_io I/O Read/Write
+ * @{
+ * @section lavf_io_dirlist Directory listing
+ * The directory listing API makes it possible to list files on remote servers.
+ *
+ * Some of possible use cases:
+ * - an "open file" dialog to choose files from a remote location,
+ * - a recursive media finder providing a player with an ability to play all
+ * files from a given directory.
+ *
+ * @subsection lavf_io_dirlist_open Opening a directory
+ * At first, a directory needs to be opened by calling avio_open_dir()
+ * supplied with a URL and, optionally, ::AVDictionary containing
+ * protocol-specific parameters. The function returns zero or positive
+ * integer and allocates AVIODirContext on success.
+ *
+ * @code
+ * AVIODirContext *ctx = NULL;
+ * if (avio_open_dir(&ctx, "smb://example.com/some_dir", NULL) < 0) {
+ *     fprintf(stderr, "Cannot open directory.\n");
+ *     abort();
+ * }
+ * @endcode
+ *
+ * This code tries to open a sample directory using smb protocol without
+ * any additional parameters.
+ *
+ * @subsection lavf_io_dirlist_read Reading entries
+ * Each directory's entry (i.e. file, another directory, anything else
+ * within ::AVIODirEntryType) is represented by AVIODirEntry.
+ * Reading consecutive entries from an opened AVIODirContext is done by
+ * repeatedly calling avio_read_dir() on it. Each call returns zero or
+ * positive integer if successful. Reading can be stopped right after the
+ * NULL entry has been read -- it means there are no entries left to be
+ * read. The following code reads all entries from a directory associated
+ * with ctx and prints their names to standard output.
+ * @code
+ * AVIODirEntry *entry = NULL;
+ * for (;;) {
+ *     if (avio_read_dir(ctx, &entry) < 0) {
+ *         fprintf(stderr, "Cannot list directory.\n");
+ *         abort();
+ *     }
+ *     if (!entry)
+ *         break;
+ *     printf("%s\n", entry->name);
+ *     avio_free_directory_entry(&entry);
+ * }
+ * @endcode
+ * @}
+ *
+ * @defgroup lavf_codec Demuxers
+ * @{
+ * @defgroup lavf_codec_native Native Demuxers
+ * @{
+ * @}
+ * @defgroup lavf_codec_wrappers External library wrappers
+ * @{
+ * @}
+ * @}
+ * @defgroup lavf_protos I/O Protocols
+ * @{
+ * @}
+ * @defgroup lavf_internal Internal
+ * @{
+ * @}
+ * @}
+ */
+
+#include <time.h>
+#include <stdio.h>  /* FILE */
+#include "libavcodec/avcodec.h"
+#include "libavutil/dict.h"
+#include "libavutil/log.h"
+
+#include "avio.h"
+#include "libavformat/version.h"
+
+struct AVFormatContext;
+
+struct AVDeviceInfoList;
+struct AVDeviceCapabilitiesQuery;
+
+/**
+ * @defgroup metadata_api Public Metadata API
+ * @{
+ * @ingroup libavf
+ * The metadata API allows libavformat to export metadata tags to a client
+ * application when demuxing. Conversely it allows a client application to
+ * set metadata when muxing.
+ *
+ * Metadata is exported or set as pairs of key/value strings in the 'metadata'
+ * fields of the AVFormatContext, AVStream, AVChapter and AVProgram structs
+ * using the @ref lavu_dict "AVDictionary" API. Like all strings in FFmpeg,
+ * metadata is assumed to be UTF-8 encoded Unicode. Note that metadata
+ * exported by demuxers isn't checked to be valid UTF-8 in most cases.
+ *
+ * Important concepts to keep in mind:
+ * -  Keys are unique; there can never be 2 tags with the same key. This is
+ *    also meant semantically, i.e., a demuxer should not knowingly produce
+ *    several keys that are literally different but semantically identical.
+ *    E.g., key=Author5, key=Author6. In this example, all authors must be
+ *    placed in the same tag.
+ * -  Metadata is flat, not hierarchical; there are no subtags. If you
+ *    want to store, e.g., the email address of the child of producer Alice
+ *    and actor Bob, that could have key=alice_and_bobs_childs_email_address.
+ * -  Several modifiers can be applied to the tag name. This is done by
+ *    appending a dash character ('-') and the modifier name in the order
+ *    they appear in the list below -- e.g. foo-eng-sort, not foo-sort-eng.
+ *    -  language -- a tag whose value is localized for a particular language
+ *       is appended with the ISO 639-2/B 3-letter language code.
+ *       For example: Author-ger=Michael, Author-eng=Mike
+ *       The original/default language is in the unqualified "Author" tag.
+ *       A demuxer should set a default if it sets any translated tag.
+ *    -  sorting  -- a modified version of a tag that should be used for
+ *       sorting will have '-sort' appended. E.g. artist="The Beatles",
+ *       artist-sort="Beatles, The".
+ * - Some protocols and demuxers support metadata updates. After a successful
+ *   call to av_read_packet(), AVFormatContext.event_flags or AVStream.event_flags
+ *   will be updated to indicate if metadata changed. In order to detect metadata
+ *   changes on a stream, you need to loop through all streams in the AVFormatContext
+ *   and check their individual event_flags.
+ *
+ * -  Demuxers attempt to export metadata in a generic format, however tags
+ *    with no generic equivalents are left as they are stored in the container.
+ *    Follows a list of generic tag names:
+ *
+ @verbatim
+ album        -- name of the set this work belongs to
+ album_artist -- main creator of the set/album, if different from artist.
+                 e.g. "Various Artists" for compilation albums.
+ artist       -- main creator of the work
+ comment      -- any additional description of the file.
+ composer     -- who composed the work, if different from artist.
+ copyright    -- name of copyright holder.
+ creation_time-- date when the file was created, preferably in ISO 8601.
+ date         -- date when the work was created, preferably in ISO 8601.
+ disc         -- number of a subset, e.g. disc in a multi-disc collection.
+ encoder      -- name/settings of the software/hardware that produced the file.
+ encoded_by   -- person/group who created the file.
+ filename     -- original name of the file.
+ genre        -- <self-evident>.
+ language     -- main language in which the work is performed, preferably
+                 in ISO 639-2 format. Multiple languages can be specified by
+                 separating them with commas.
+ performer    -- artist who performed the work, if different from artist.
+                 E.g for "Also sprach Zarathustra", artist would be "Richard
+                 Strauss" and performer "London Philharmonic Orchestra".
+ publisher    -- name of the label/publisher.
+ service_name     -- name of the service in broadcasting (channel name).
+ service_provider -- name of the service provider in broadcasting.
+ title        -- name of the work.
+ track        -- number of this work in the set, can be in form current/total.
+ variant_bitrate -- the total bitrate of the bitrate variant that the current stream is part of
+ @endverbatim
+ *
+ * Look in the examples section for an application example how to use the Metadata API.
+ *
+ * @}
+ */
+
+/* packet functions */
+
+
+/**
+ * Allocate and read the payload of a packet and initialize its
+ * fields with default values.
+ *
+ * @param s    associated IO context
+ * @param pkt packet
+ * @param size desired payload size
+ * @return >0 (read size) if OK, AVERROR_xxx otherwise
+ */
+int av_get_packet(AVIOContext *s, AVPacket *pkt, int size);
+
+
+/**
+ * Read data and append it to the current content of the AVPacket.
+ * If pkt->size is 0 this is identical to av_get_packet.
+ * Note that this uses av_grow_packet and thus involves a realloc
+ * which is inefficient. Thus this function should only be used
+ * when there is no reasonable way to know (an upper bound of)
+ * the final size.
+ *
+ * @param s    associated IO context
+ * @param pkt packet
+ * @param size amount of data to read
+ * @return >0 (read size) if OK, AVERROR_xxx otherwise, previous data
+ *         will not be lost even if an error occurs.
+ */
+int av_append_packet(AVIOContext *s, AVPacket *pkt, int size);
+
+/*************************************************/
+/* input/output formats */
+
+struct AVCodecTag;
+
+/**
+ * This structure contains the data a format has to probe a file.
+ */
+typedef struct AVProbeData {
+    const char *filename;
+    unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */
+    int buf_size;       /**< Size of buf except extra allocated bytes */
+    const char *mime_type; /**< mime_type, when known. */
+} AVProbeData;
+
+#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4)
+#define AVPROBE_SCORE_STREAM_RETRY (AVPROBE_SCORE_MAX/4-1)
+
+#define AVPROBE_SCORE_EXTENSION  50 ///< score for file extension
+#define AVPROBE_SCORE_MIME       75 ///< score for file mime type
+#define AVPROBE_SCORE_MAX       100 ///< maximum score
+
+#define AVPROBE_PADDING_SIZE 32             ///< extra allocated bytes at the end of the probe buffer
+
+/// Demuxer will use avio_open, no opened file should be provided by the caller.
+#define AVFMT_NOFILE        0x0001
+#define AVFMT_NEEDNUMBER    0x0002 /**< Needs '%d' in filename. */
+#define AVFMT_SHOW_IDS      0x0008 /**< Show format stream IDs numbers. */
+#define AVFMT_GLOBALHEADER  0x0040 /**< Format wants global header. */
+#define AVFMT_NOTIMESTAMPS  0x0080 /**< Format does not need / have any timestamps. */
+#define AVFMT_GENERIC_INDEX 0x0100 /**< Use generic index building code. */
+#define AVFMT_TS_DISCONT    0x0200 /**< Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps */
+#define AVFMT_VARIABLE_FPS  0x0400 /**< Format allows variable fps. */
+#define AVFMT_NODIMENSIONS  0x0800 /**< Format does not need width/height */
+#define AVFMT_NOSTREAMS     0x1000 /**< Format does not require any streams */
+#define AVFMT_NOBINSEARCH   0x2000 /**< Format does not allow to fall back on binary search via read_timestamp */
+#define AVFMT_NOGENSEARCH   0x4000 /**< Format does not allow to fall back on generic search */
+#define AVFMT_NO_BYTE_SEEK  0x8000 /**< Format does not allow seeking by bytes */
+#define AVFMT_ALLOW_FLUSH  0x10000 /**< Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function. */
+#define AVFMT_TS_NONSTRICT 0x20000 /**< Format does not require strictly
+                                        increasing timestamps, but they must
+                                        still be monotonic */
+#define AVFMT_TS_NEGATIVE  0x40000 /**< Format allows muxing negative
+                                        timestamps. If not set the timestamp
+                                        will be shifted in av_write_frame and
+                                        av_interleaved_write_frame so they
+                                        start from 0.
+                                        The user or muxer can override this through
+                                        AVFormatContext.avoid_negative_ts
+                                        */
+
+#define AVFMT_SEEK_TO_PTS   0x4000000 /**< Seeking is based on PTS */
+
+/**
+ * @addtogroup lavf_encoding
+ * @{
+ */
+typedef struct AVOutputFormat {
+    const char *name;
+    /**
+     * Descriptive name for the format, meant to be more human-readable
+     * than name. You should use the NULL_IF_CONFIG_SMALL() macro
+     * to define it.
+     */
+    const char *long_name;
+    const char *mime_type;
+    const char *extensions; /**< comma-separated filename extensions */
+    /* output support */
+    enum AVCodecID audio_codec;    /**< default audio codec */
+    enum AVCodecID video_codec;    /**< default video codec */
+    enum AVCodecID subtitle_codec; /**< default subtitle codec */
+    /**
+     * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER,
+     * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS,
+     * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH,
+     * AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE
+     */
+    int flags;
+
+    /**
+     * List of supported codec_id-codec_tag pairs, ordered by "better
+     * choice first". The arrays are all terminated by AV_CODEC_ID_NONE.
+     */
+    const struct AVCodecTag * const *codec_tag;
+
+
+    const AVClass *priv_class; ///< AVClass for the private context
+
+    /*****************************************************************
+     * No fields below this line are part of the public API. They
+     * may not be used outside of libavformat and can be changed and
+     * removed at will.
+     * New public fields should be added right above.
+     *****************************************************************
+     */
+    struct AVOutputFormat *next;
+    /**
+     * size of private data so that it can be allocated in the wrapper
+     */
+    int priv_data_size;
+
+    int (*write_header)(struct AVFormatContext *);
+    /**
+     * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags,
+     * pkt can be NULL in order to flush data buffered in the muxer.
+     * When flushing, return 0 if there still is more data to flush,
+     * or 1 if everything was flushed and there is no more buffered
+     * data.
+     */
+    int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
+    int (*write_trailer)(struct AVFormatContext *);
+    /**
+     * Currently only used to set pixel format if not YUV420P.
+     */
+    int (*interleave_packet)(struct AVFormatContext *, AVPacket *out,
+                             AVPacket *in, int flush);
+    /**
+     * Test if the given codec can be stored in this container.
+     *
+     * @return 1 if the codec is supported, 0 if it is not.
+     *         A negative number if unknown.
+     *         MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC
+     */
+    int (*query_codec)(enum AVCodecID id, int std_compliance);
+
+    void (*get_output_timestamp)(struct AVFormatContext *s, int stream,
+                                 int64_t *dts, int64_t *wall);
+    /**
+     * Allows sending messages from application to device.
+     */
+    int (*control_message)(struct AVFormatContext *s, int type,
+                           void *data, size_t data_size);
+
+    /**
+     * Write an uncoded AVFrame.
+     *
+     * See av_write_uncoded_frame() for details.
+     *
+     * The library will free *frame afterwards, but the muxer can prevent it
+     * by setting the pointer to NULL.
+     */
+    int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index,
+                               AVFrame **frame, unsigned flags);
+    /**
+     * Returns device list with it properties.
+     * @see avdevice_list_devices() for more details.
+     */
+    int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
+    /**
+     * Initialize device capabilities submodule.
+     * @see avdevice_capabilities_create() for more details.
+     */
+    int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
+    /**
+     * Free device capabilities submodule.
+     * @see avdevice_capabilities_free() for more details.
+     */
+    int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
+    enum AVCodecID data_codec; /**< default data codec */
+    /**
+     * Initialize format. May allocate data here, and set any AVFormatContext or
+     * AVStream parameters that need to be set before packets are sent.
+     * This method must not write output.
+     *
+     * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure
+     *
+     * Any allocations made here must be freed in deinit().
+     */
+    int (*init)(struct AVFormatContext *);
+    /**
+     * Deinitialize format. If present, this is called whenever the muxer is being
+     * destroyed, regardless of whether or not the header has been written.
+     *
+     * If a trailer is being written, this is called after write_trailer().
+     *
+     * This is called if init() fails as well.
+     */
+    void (*deinit)(struct AVFormatContext *);
+    /**
+     * Set up any necessary bitstream filtering and extract any extra data needed
+     * for the global header.
+     * Return 0 if more packets from this stream must be checked; 1 if not.
+     */
+    int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);
+} AVOutputFormat;
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup lavf_decoding
+ * @{
+ */
+typedef struct AVInputFormat {
+    /**
+     * A comma separated list of short names for the format. New names
+     * may be appended with a minor bump.
+     */
+    const char *name;
+
+    /**
+     * Descriptive name for the format, meant to be more human-readable
+     * than name. You should use the NULL_IF_CONFIG_SMALL() macro
+     * to define it.
+     */
+    const char *long_name;
+
+    /**
+     * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,
+     * AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,
+     * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.
+     */
+    int flags;
+
+    /**
+     * If extensions are defined, then no probe is done. You should
+     * usually not use extension format guessing because it is not
+     * reliable enough
+     */
+    const char *extensions;
+
+    const struct AVCodecTag * const *codec_tag;
+
+    const AVClass *priv_class; ///< AVClass for the private context
+
+    /**
+     * Comma-separated list of mime types.
+     * It is used check for matching mime types while probing.
+     * @see av_probe_input_format2
+     */
+    const char *mime_type;
+
+    /*****************************************************************
+     * No fields below this line are part of the public API. They
+     * may not be used outside of libavformat and can be changed and
+     * removed at will.
+     * New public fields should be added right above.
+     *****************************************************************
+     */
+    struct AVInputFormat *next;
+
+    /**
+     * Raw demuxers store their codec ID here.
+     */
+    int raw_codec_id;
+
+    /**
+     * Size of private data so that it can be allocated in the wrapper.
+     */
+    int priv_data_size;
+
+    /**
+     * Tell if a given file has a chance of being parsed as this format.
+     * The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes
+     * big so you do not have to check for that unless you need more.
+     */
+    int (*read_probe)(AVProbeData *);
+
+    /**
+     * Read the format header and initialize the AVFormatContext
+     * structure. Return 0 if OK. 'avformat_new_stream' should be
+     * called to create new streams.
+     */
+    int (*read_header)(struct AVFormatContext *);
+
+    /**
+     * Read one packet and put it in 'pkt'. pts and flags are also
+     * set. 'avformat_new_stream' can be called only if the flag
+     * AVFMTCTX_NOHEADER is used and only in the calling thread (not in a
+     * background thread).
+     * @return 0 on success, < 0 on error.
+     *         When returning an error, pkt must not have been allocated
+     *         or must be freed before returning
+     */
+    int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
+
+    /**
+     * Close the stream. The AVFormatContext and AVStreams are not
+     * freed by this function
+     */
+    int (*read_close)(struct AVFormatContext *);
+
+    /**
+     * Seek to a given timestamp relative to the frames in
+     * stream component stream_index.
+     * @param stream_index Must not be -1.
+     * @param flags Selects which direction should be preferred if no exact
+     *              match is available.
+     * @return >= 0 on success (but not necessarily the new offset)
+     */
+    int (*read_seek)(struct AVFormatContext *,
+                     int stream_index, int64_t timestamp, int flags);
+
+    /**
+     * Get the next timestamp in stream[stream_index].time_base units.
+     * @return the timestamp or AV_NOPTS_VALUE if an error occurred
+     */
+    int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index,
+                              int64_t *pos, int64_t pos_limit);
+
+    /**
+     * Start/resume playing - only meaningful if using a network-based format
+     * (RTSP).
+     */
+    int (*read_play)(struct AVFormatContext *);
+
+    /**
+     * Pause playing - only meaningful if using a network-based format
+     * (RTSP).
+     */
+    int (*read_pause)(struct AVFormatContext *);
+
+    /**
+     * Seek to timestamp ts.
+     * Seeking will be done so that the point from which all active streams
+     * can be presented successfully will be closest to ts and within min/max_ts.
+     * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL.
+     */
+    int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
+
+    /**
+     * Returns device list with it properties.
+     * @see avdevice_list_devices() for more details.
+     */
+    int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
+
+    /**
+     * Initialize device capabilities submodule.
+     * @see avdevice_capabilities_create() for more details.
+     */
+    int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
+
+    /**
+     * Free device capabilities submodule.
+     * @see avdevice_capabilities_free() for more details.
+     */
+    int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
+} AVInputFormat;
+/**
+ * @}
+ */
+
+enum AVStreamParseType {
+    AVSTREAM_PARSE_NONE,
+    AVSTREAM_PARSE_FULL,       /**< full parsing and repack */
+    AVSTREAM_PARSE_HEADERS,    /**< Only parse headers, do not repack. */
+    AVSTREAM_PARSE_TIMESTAMPS, /**< full parsing and interpolation of timestamps for frames not starting on a packet boundary */
+    AVSTREAM_PARSE_FULL_ONCE,  /**< full parsing and repack of the first frame only, only implemented for H.264 currently */
+    AVSTREAM_PARSE_FULL_RAW,   /**< full parsing and repack with timestamp and position generation by parser for raw
+                                    this assumes that each packet in the file contains no demuxer level headers and
+                                    just codec level data, otherwise position generation would fail */
+};
+
+typedef struct AVIndexEntry {
+    int64_t pos;
+    int64_t timestamp;        /**<
+                               * Timestamp in AVStream.time_base units, preferably the time from which on correctly decoded frames are available
+                               * when seeking to this entry. That means preferable PTS on keyframe based formats.
+                               * But demuxers can choose to store a different timestamp, if it is more convenient for the implementation or nothing better
+                               * is known
+                               */
+#define AVINDEX_KEYFRAME 0x0001
+#define AVINDEX_DISCARD_FRAME  0x0002    /**
+                                          * Flag is used to indicate which frame should be discarded after decoding.
+                                          */
+    int flags:2;
+    int size:30; //Yeah, trying to keep the size of this small to reduce memory requirements (it is 24 vs. 32 bytes due to possible 8-byte alignment).
+    int min_distance;         /**< Minimum distance between this and the previous keyframe, used to avoid unneeded searching. */
+} AVIndexEntry;
+
+#define AV_DISPOSITION_DEFAULT   0x0001
+#define AV_DISPOSITION_DUB       0x0002
+#define AV_DISPOSITION_ORIGINAL  0x0004
+#define AV_DISPOSITION_COMMENT   0x0008
+#define AV_DISPOSITION_LYRICS    0x0010
+#define AV_DISPOSITION_KARAOKE   0x0020
+
+/**
+ * Track should be used during playback by default.
+ * Useful for subtitle track that should be displayed
+ * even when user did not explicitly ask for subtitles.
+ */
+#define AV_DISPOSITION_FORCED    0x0040
+#define AV_DISPOSITION_HEARING_IMPAIRED  0x0080  /**< stream for hearing impaired audiences */
+#define AV_DISPOSITION_VISUAL_IMPAIRED   0x0100  /**< stream for visual impaired audiences */
+#define AV_DISPOSITION_CLEAN_EFFECTS     0x0200  /**< stream without voice */
+/**
+ * The stream is stored in the file as an attached picture/"cover art" (e.g.
+ * APIC frame in ID3v2). The first (usually only) packet associated with it
+ * will be returned among the first few packets read from the file unless
+ * seeking takes place. It can also be accessed at any time in
+ * AVStream.attached_pic.
+ */
+#define AV_DISPOSITION_ATTACHED_PIC      0x0400
+/**
+ * The stream is sparse, and contains thumbnail images, often corresponding
+ * to chapter markers. Only ever used with AV_DISPOSITION_ATTACHED_PIC.
+ */
+#define AV_DISPOSITION_TIMED_THUMBNAILS  0x0800
+
+typedef struct AVStreamInternal AVStreamInternal;
+
+/**
+ * To specify text track kind (different from subtitles default).
+ */
+#define AV_DISPOSITION_CAPTIONS     0x10000
+#define AV_DISPOSITION_DESCRIPTIONS 0x20000
+#define AV_DISPOSITION_METADATA     0x40000
+#define AV_DISPOSITION_DEPENDENT    0x80000 ///< dependent audio stream (mix_type=0 in mpegts)
+#define AV_DISPOSITION_STILL_IMAGE 0x100000 ///< still images in video stream (still_picture_flag=1 in mpegts)
+
+/**
+ * Options for behavior on timestamp wrap detection.
+ */
+#define AV_PTS_WRAP_IGNORE      0   ///< ignore the wrap
+#define AV_PTS_WRAP_ADD_OFFSET  1   ///< add the format specific offset on wrap detection
+#define AV_PTS_WRAP_SUB_OFFSET  -1  ///< subtract the format specific offset on wrap detection
+
+/**
+ * Stream 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.
+ * sizeof(AVStream) must not be used outside libav*.
+ */
+typedef struct AVStream {
+    int index;    /**< stream index in AVFormatContext */
+    /**
+     * Format-specific stream ID.
+     * decoding: set by libavformat
+     * encoding: set by the user, replaced by libavformat if left unset
+     */
+    int id;
+#if FF_API_LAVF_AVCTX
+    /**
+     * @deprecated use the codecpar struct instead
+     */
+    attribute_deprecated
+    AVCodecContext *codec;
+#endif
+    void *priv_data;
+
+    /**
+     * This is the fundamental unit of time (in seconds) in terms
+     * of which frame timestamps are represented.
+     *
+     * decoding: set by libavformat
+     * encoding: May be set by the caller before avformat_write_header() to
+     *           provide a hint to the muxer about the desired timebase. In
+     *           avformat_write_header(), the muxer will overwrite this field
+     *           with the timebase that will actually be used for the timestamps
+     *           written into the file (which may or may not be related to the
+     *           user-provided one, depending on the format).
+     */
+    AVRational time_base;
+
+    /**
+     * Decoding: pts of the first frame of the stream in presentation order, in stream time base.
+     * Only set this if you are absolutely 100% sure that the value you set
+     * it to really is the pts of the first frame.
+     * This may be undefined (AV_NOPTS_VALUE).
+     * @note The ASF header does NOT contain a correct start_time the ASF
+     * demuxer must NOT set this.
+     */
+    int64_t start_time;
+
+    /**
+     * Decoding: duration of the stream, in stream time base.
+     * If a source file does not specify a duration, but does specify
+     * a bitrate, this value will be estimated from bitrate and file size.
+     *
+     * Encoding: May be set by the caller before avformat_write_header() to
+     * provide a hint to the muxer about the estimated duration.
+     */
+    int64_t duration;
+
+    int64_t nb_frames;                 ///< number of frames in this stream if known or 0
+
+    int disposition; /**< AV_DISPOSITION_* bit field */
+
+    enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed.
+
+    /**
+     * sample aspect ratio (0 if unknown)
+     * - encoding: Set by user.
+     * - decoding: Set by libavformat.
+     */
+    AVRational sample_aspect_ratio;
+
+    AVDictionary *metadata;
+
+    /**
+     * Average framerate
+     *
+     * - demuxing: May be set by libavformat when creating the stream or in
+     *             avformat_find_stream_info().
+     * - muxing: May be set by the caller before avformat_write_header().
+     */
+    AVRational avg_frame_rate;
+
+    /**
+     * For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet
+     * will contain the attached picture.
+     *
+     * decoding: set by libavformat, must not be modified by the caller.
+     * encoding: unused
+     */
+    AVPacket attached_pic;
+
+    /**
+     * An array of side data that applies to the whole stream (i.e. the
+     * container does not allow it to change between packets).
+     *
+     * There may be no overlap between the side data in this array and side data
+     * in the packets. I.e. a given side data is either exported by the muxer
+     * (demuxing) / set by the caller (muxing) in this array, then it never
+     * appears in the packets, or the side data is exported / sent through
+     * the packets (always in the first packet where the value becomes known or
+     * changes), then it does not appear in this array.
+     *
+     * - demuxing: Set by libavformat when the stream is created.
+     * - muxing: May be set by the caller before avformat_write_header().
+     *
+     * Freed by libavformat in avformat_free_context().
+     *
+     * @see av_format_inject_global_side_data()
+     */
+    AVPacketSideData *side_data;
+    /**
+     * The number of elements in the AVStream.side_data array.
+     */
+    int            nb_side_data;
+
+    /**
+     * Flags for the user to detect events happening on the stream. Flags must
+     * be cleared by the user once the event has been handled.
+     * A combination of AVSTREAM_EVENT_FLAG_*.
+     */
+    int event_flags;
+#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.
+
+    /**
+     * Real base framerate of the stream.
+     * This is the lowest framerate with which all timestamps can be
+     * represented accurately (it is the least common multiple of all
+     * framerates in the stream). Note, this value is just a guess!
+     * For example, if the time base is 1/90000 and all frames have either
+     * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
+     */
+    AVRational r_frame_rate;
+
+#if FF_API_LAVF_FFSERVER
+    /**
+     * String containing pairs of key and values describing recommended encoder configuration.
+     * Pairs are separated by ','.
+     * Keys are separated from values by '='.
+     *
+     * @deprecated unused
+     */
+    attribute_deprecated
+    char *recommended_encoder_configuration;
+#endif
+
+    /**
+     * Codec parameters associated with this stream. Allocated and freed by
+     * libavformat in avformat_new_stream() and avformat_free_context()
+     * respectively.
+     *
+     * - demuxing: filled by libavformat on stream creation or in
+     *             avformat_find_stream_info()
+     * - muxing: filled by the caller before avformat_write_header()
+     */
+    AVCodecParameters *codecpar;
+
+    /*****************************************************************
+     * All fields below this line are not part of the public API. They
+     * may not be used outside of libavformat and can be changed and
+     * removed at will.
+     * Internal note: be aware that physically removing these fields
+     * will break ABI. Replace removed fields with dummy fields, and
+     * add new fields to AVStreamInternal.
+     *****************************************************************
+     */
+
+#define MAX_STD_TIMEBASES (30*12+30+3+6)
+    /**
+     * Stream information used internally by avformat_find_stream_info()
+     */
+    struct {
+        int64_t last_dts;
+        int64_t duration_gcd;
+        int duration_count;
+        int64_t rfps_duration_sum;
+        double (*duration_error)[2][MAX_STD_TIMEBASES];
+        int64_t codec_info_duration;
+        int64_t codec_info_duration_fields;
+        int frame_delay_evidence;
+
+        /**
+         * 0  -> decoder has not been searched for yet.
+         * >0 -> decoder found
+         * <0 -> decoder with codec_id == -found_decoder has not been found
+         */
+        int found_decoder;
+
+        int64_t last_duration;
+
+        /**
+         * Those are used for average framerate estimation.
+         */
+        int64_t fps_first_dts;
+        int     fps_first_dts_idx;
+        int64_t fps_last_dts;
+        int     fps_last_dts_idx;
+
+    } *info;
+
+    int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */
+
+    // Timestamp generation support:
+    /**
+     * Timestamp corresponding to the last dts sync point.
+     *
+     * Initialized when AVCodecParserContext.dts_sync_point >= 0 and
+     * a DTS is received from the underlying container. Otherwise set to
+     * AV_NOPTS_VALUE by default.
+     */
+    int64_t first_dts;
+    int64_t cur_dts;
+    int64_t last_IP_pts;
+    int last_IP_duration;
+
+    /**
+     * Number of packets to buffer for codec probing
+     */
+    int probe_packets;
+
+    /**
+     * Number of frames that have been demuxed during avformat_find_stream_info()
+     */
+    int codec_info_nb_frames;
+
+    /* av_read_frame() support */
+    enum AVStreamParseType need_parsing;
+    struct AVCodecParserContext *parser;
+
+    /**
+     * last packet in packet_buffer for this stream when muxing.
+     */
+    struct AVPacketList *last_in_packet_buffer;
+    AVProbeData probe_data;
+#define MAX_REORDER_DELAY 16
+    int64_t pts_buffer[MAX_REORDER_DELAY+1];
+
+    AVIndexEntry *index_entries; /**< Only used if the format does not
+                                    support seeking natively. */
+    int nb_index_entries;
+    unsigned int index_entries_allocated_size;
+
+    /**
+     * Stream Identifier
+     * This is the MPEG-TS stream identifier +1
+     * 0 means unknown
+     */
+    int stream_identifier;
+
+    /**
+     * Details of the MPEG-TS program which created this stream.
+     */
+    int program_num;
+    int pmt_version;
+    int pmt_stream_idx;
+
+    int64_t interleaver_chunk_size;
+    int64_t interleaver_chunk_duration;
+
+    /**
+     * stream probing state
+     * -1   -> probing finished
+     *  0   -> no probing requested
+     * rest -> perform probing with request_probe being the minimum score to accept.
+     * NOT PART OF PUBLIC API
+     */
+    int request_probe;
+    /**
+     * Indicates that everything up to the next keyframe
+     * should be discarded.
+     */
+    int skip_to_keyframe;
+
+    /**
+     * Number of samples to skip at the start of the frame decoded from the next packet.
+     */
+    int skip_samples;
+
+    /**
+     * If not 0, the number of samples that should be skipped from the start of
+     * the stream (the samples are removed from packets with pts==0, which also
+     * assumes negative timestamps do not happen).
+     * Intended for use with formats such as mp3 with ad-hoc gapless audio
+     * support.
+     */
+    int64_t start_skip_samples;
+
+    /**
+     * If not 0, the first audio sample that should be discarded from the stream.
+     * This is broken by design (needs global sample count), but can't be
+     * avoided for broken by design formats such as mp3 with ad-hoc gapless
+     * audio support.
+     */
+    int64_t first_discard_sample;
+
+    /**
+     * The sample after last sample that is intended to be discarded after
+     * first_discard_sample. Works on frame boundaries only. Used to prevent
+     * early EOF if the gapless info is broken (considered concatenated mp3s).
+     */
+    int64_t last_discard_sample;
+
+    /**
+     * Number of internally decoded frames, used internally in libavformat, do not access
+     * its lifetime differs from info which is why it is not in that structure.
+     */
+    int nb_decoded_frames;
+
+    /**
+     * Timestamp offset added to timestamps before muxing
+     * NOT PART OF PUBLIC API
+     */
+    int64_t mux_ts_offset;
+
+    /**
+     * Internal data to check for wrapping of the time stamp
+     */
+    int64_t pts_wrap_reference;
+
+    /**
+     * Options for behavior, when a wrap is detected.
+     *
+     * Defined by AV_PTS_WRAP_ values.
+     *
+     * If correction is enabled, there are two possibilities:
+     * If the first time stamp is near the wrap point, the wrap offset
+     * will be subtracted, which will create negative time stamps.
+     * Otherwise the offset will be added.
+     */
+    int pts_wrap_behavior;
+
+    /**
+     * Internal data to prevent doing update_initial_durations() twice
+     */
+    int update_initial_durations_done;
+
+    /**
+     * Internal data to generate dts from pts
+     */
+    int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
+    uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];
+
+    /**
+     * Internal data to analyze DTS and detect faulty mpeg streams
+     */
+    int64_t last_dts_for_order_check;
+    uint8_t dts_ordered;
+    uint8_t dts_misordered;
+
+    /**
+     * Internal data to inject global side data
+     */
+    int inject_global_side_data;
+
+    /**
+     * display aspect ratio (0 if unknown)
+     * - encoding: unused
+     * - decoding: Set by libavformat to calculate sample_aspect_ratio internally
+     */
+    AVRational display_aspect_ratio;
+
+    /**
+     * An opaque field for libavformat internal usage.
+     * Must not be accessed in any way by callers.
+     */
+    AVStreamInternal *internal;
+} AVStream;
+
+#if FF_API_FORMAT_GET_SET
+/**
+ * Accessors for some AVStream fields. These used to be provided for ABI
+ * compatibility, and do not need to be used anymore.
+ */
+attribute_deprecated
+AVRational av_stream_get_r_frame_rate(const AVStream *s);
+attribute_deprecated
+void       av_stream_set_r_frame_rate(AVStream *s, AVRational r);
+#if FF_API_LAVF_FFSERVER
+attribute_deprecated
+char* av_stream_get_recommended_encoder_configuration(const AVStream *s);
+attribute_deprecated
+void  av_stream_set_recommended_encoder_configuration(AVStream *s, char *configuration);
+#endif
+#endif
+
+struct AVCodecParserContext *av_stream_get_parser(const AVStream *s);
+
+/**
+ * Returns the pts of the last muxed packet + its duration
+ *
+ * the retuned value is undefined when used with a demuxer.
+ */
+int64_t    av_stream_get_end_pts(const AVStream *st);
+
+#define AV_PROGRAM_RUNNING 1
+
+/**
+ * New fields can be added to the end with minor version bumps.
+ * Removal, reordering and changes to existing fields require a major
+ * version bump.
+ * sizeof(AVProgram) must not be used outside libav*.
+ */
+typedef struct AVProgram {
+    int            id;
+    int            flags;
+    enum AVDiscard discard;        ///< selects which program to discard and which to feed to the caller
+    unsigned int   *stream_index;
+    unsigned int   nb_stream_indexes;
+    AVDictionary *metadata;
+
+    int program_num;
+    int pmt_pid;
+    int pcr_pid;
+    int pmt_version;
+
+    /*****************************************************************
+     * All fields below this line are not part of the public API. They
+     * may not be used outside of libavformat and can be changed and
+     * removed at will.
+     * New public fields should be added right above.
+     *****************************************************************
+     */
+    int64_t start_time;
+    int64_t end_time;
+
+    int64_t pts_wrap_reference;    ///< reference dts for wrap detection
+    int pts_wrap_behavior;         ///< behavior on wrap detection
+} AVProgram;
+
+#define AVFMTCTX_NOHEADER      0x0001 /**< signal that no header is present
+                                         (streams are added dynamically) */
+#define AVFMTCTX_UNSEEKABLE    0x0002 /**< signal that the stream is definitely
+                                         not seekable, and attempts to call the
+                                         seek function will fail. For some
+                                         network protocols (e.g. HLS), this can
+                                         change dynamically at runtime. */
+
+typedef struct AVChapter {
+    int id;                 ///< unique ID to identify the chapter
+    AVRational time_base;   ///< time base in which the start/end timestamps are specified
+    int64_t start, end;     ///< chapter start/end time in time_base units
+    AVDictionary *metadata;
+} AVChapter;
+
+
+/**
+ * Callback used by devices to communicate with application.
+ */
+typedef int (*av_format_control_message)(struct AVFormatContext *s, int type,
+                                         void *data, size_t data_size);
+
+typedef int (*AVOpenCallback)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
+                              const AVIOInterruptCB *int_cb, AVDictionary **options);
+
+/**
+ * The duration of a video can be estimated through various ways, and this enum can be used
+ * to know how the duration was estimated.
+ */
+enum AVDurationEstimationMethod {
+    AVFMT_DURATION_FROM_PTS,    ///< Duration accurately estimated from PTSes
+    AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration
+    AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate)
+};
+
+typedef struct AVFormatInternal AVFormatInternal;
+
+/**
+ * Format I/O context.
+ * New fields can be added to the end with minor version bumps.
+ * Removal, reordering and changes to existing fields require a major
+ * version bump.
+ * sizeof(AVFormatContext) must not be used outside libav*, use
+ * avformat_alloc_context() to create an AVFormatContext.
+ *
+ * Fields can be accessed through AVOptions (av_opt*),
+ * the name string used matches the associated command line parameter name and
+ * can be found in libavformat/options_table.h.
+ * The AVOption/command line parameter names differ in some cases from the C
+ * structure field names for historic reasons or brevity.
+ */
+typedef struct AVFormatContext {
+    /**
+     * A class for logging and @ref avoptions. Set by avformat_alloc_context().
+     * Exports (de)muxer private options if they exist.
+     */
+    const AVClass *av_class;
+
+    /**
+     * The input container format.
+     *
+     * Demuxing only, set by avformat_open_input().
+     */
+    struct AVInputFormat *iformat;
+
+    /**
+     * The output container format.
+     *
+     * Muxing only, must be set by the caller before avformat_write_header().
+     */
+    struct AVOutputFormat *oformat;
+
+    /**
+     * Format private data. This is an AVOptions-enabled struct
+     * if and only if iformat/oformat.priv_class is not NULL.
+     *
+     * - muxing: set by avformat_write_header()
+     * - demuxing: set by avformat_open_input()
+     */
+    void *priv_data;
+
+    /**
+     * I/O context.
+     *
+     * - demuxing: either set by the user before avformat_open_input() (then
+     *             the user must close it manually) or set by avformat_open_input().
+     * - muxing: set by the user before avformat_write_header(). The caller must
+     *           take care of closing / freeing the IO context.
+     *
+     * Do NOT set this field if AVFMT_NOFILE flag is set in
+     * iformat/oformat.flags. In such a case, the (de)muxer will handle
+     * I/O in some other way and this field will be NULL.
+     */
+    AVIOContext *pb;
+
+    /* stream info */
+    /**
+     * Flags signalling stream properties. A combination of AVFMTCTX_*.
+     * Set by libavformat.
+     */
+    int ctx_flags;
+
+    /**
+     * Number of elements in AVFormatContext.streams.
+     *
+     * Set by avformat_new_stream(), must not be modified by any other code.
+     */
+    unsigned int nb_streams;
+    /**
+     * A list of all streams in the file. New streams are created with
+     * avformat_new_stream().
+     *
+     * - demuxing: streams are created by libavformat in avformat_open_input().
+     *             If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also
+     *             appear in av_read_frame().
+     * - muxing: streams are created by the user before avformat_write_header().
+     *
+     * Freed by libavformat in avformat_free_context().
+     */
+    AVStream **streams;
+
+#if FF_API_FORMAT_FILENAME
+    /**
+     * input or output filename
+     *
+     * - demuxing: set by avformat_open_input()
+     * - muxing: may be set by the caller before avformat_write_header()
+     *
+     * @deprecated Use url instead.
+     */
+    attribute_deprecated
+    char filename[1024];
+#endif
+
+    /**
+     * input or output URL. Unlike the old filename field, this field has no
+     * length restriction.
+     *
+     * - demuxing: set by avformat_open_input(), initialized to an empty
+     *             string if url parameter was NULL in avformat_open_input().
+     * - muxing: may be set by the caller before calling avformat_write_header()
+     *           (or avformat_init_output() if that is called first) to a string
+     *           which is freeable by av_free(). Set to an empty string if it
+     *           was NULL in avformat_init_output().
+     *
+     * Freed by libavformat in avformat_free_context().
+     */
+    char *url;
+
+    /**
+     * Position of the first frame of the component, in
+     * AV_TIME_BASE fractional seconds. NEVER set this value directly:
+     * It is deduced from the AVStream values.
+     *
+     * Demuxing only, set by libavformat.
+     */
+    int64_t start_time;
+
+    /**
+     * Duration of the stream, in AV_TIME_BASE fractional
+     * seconds. Only set this value if you know none of the individual stream
+     * durations and also do not set any of them. This is deduced from the
+     * AVStream values if not set.
+     *
+     * Demuxing only, set by libavformat.
+     */
+    int64_t duration;
+
+    /**
+     * Total stream bitrate in bit/s, 0 if not
+     * available. Never set it directly if the file_size and the
+     * duration are known as FFmpeg can compute it automatically.
+     */
+    int64_t bit_rate;
+
+    unsigned int packet_size;
+    int max_delay;
+
+    /**
+     * Flags modifying the (de)muxer behaviour. A combination of AVFMT_FLAG_*.
+     * Set by the user before avformat_open_input() / avformat_write_header().
+     */
+    int flags;
+#define AVFMT_FLAG_GENPTS       0x0001 ///< Generate missing pts even if it requires parsing future frames.
+#define AVFMT_FLAG_IGNIDX       0x0002 ///< Ignore index.
+#define AVFMT_FLAG_NONBLOCK     0x0004 ///< Do not block when reading packets from input.
+#define AVFMT_FLAG_IGNDTS       0x0008 ///< Ignore DTS on frames that contain both DTS & PTS
+#define AVFMT_FLAG_NOFILLIN     0x0010 ///< Do not infer any values from other values, just return what is stored in the container
+#define AVFMT_FLAG_NOPARSE      0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled
+#define AVFMT_FLAG_NOBUFFER     0x0040 ///< Do not buffer frames when possible
+#define AVFMT_FLAG_CUSTOM_IO    0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it.
+#define AVFMT_FLAG_DISCARD_CORRUPT  0x0100 ///< Discard frames marked corrupted
+#define AVFMT_FLAG_FLUSH_PACKETS    0x0200 ///< Flush the AVIOContext every packet.
+/**
+ * When muxing, try to avoid writing any random/volatile data to the output.
+ * This includes any random IDs, real-time timestamps/dates, muxer version, etc.
+ *
+ * This flag is mainly intended for testing.
+ */
+#define AVFMT_FLAG_BITEXACT         0x0400
+#if FF_API_LAVF_MP4A_LATM
+#define AVFMT_FLAG_MP4A_LATM    0x8000 ///< Deprecated, does nothing.
+#endif
+#define AVFMT_FLAG_SORT_DTS    0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down)
+#define AVFMT_FLAG_PRIV_OPT    0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted)
+#if FF_API_LAVF_KEEPSIDE_FLAG
+#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Deprecated, does nothing.
+#endif
+#define AVFMT_FLAG_FAST_SEEK   0x80000 ///< Enable fast, but inaccurate seeks for some formats
+#define AVFMT_FLAG_SHORTEST   0x100000 ///< Stop muxing when the shortest stream stops.
+#define AVFMT_FLAG_AUTO_BSF   0x200000 ///< Add bitstream filters as requested by the muxer
+
+    /**
+     * Maximum size of the data read from input for determining
+     * the input container format.
+     * Demuxing only, set by the caller before avformat_open_input().
+     */
+    int64_t probesize;
+
+    /**
+     * Maximum duration (in AV_TIME_BASE units) of the data read
+     * from input in avformat_find_stream_info().
+     * Demuxing only, set by the caller before avformat_find_stream_info().
+     * Can be set to 0 to let avformat choose using a heuristic.
+     */
+    int64_t max_analyze_duration;
+
+    const uint8_t *key;
+    int keylen;
+
+    unsigned int nb_programs;
+    AVProgram **programs;
+
+    /**
+     * Forced video codec_id.
+     * Demuxing: Set by user.
+     */
+    enum AVCodecID video_codec_id;
+
+    /**
+     * Forced audio codec_id.
+     * Demuxing: Set by user.
+     */
+    enum AVCodecID audio_codec_id;
+
+    /**
+     * Forced subtitle codec_id.
+     * Demuxing: Set by user.
+     */
+    enum AVCodecID subtitle_codec_id;
+
+    /**
+     * Maximum amount of memory in bytes to use for the index of each stream.
+     * If the index exceeds this size, entries will be discarded as
+     * needed to maintain a smaller size. This can lead to slower or less
+     * accurate seeking (depends on demuxer).
+     * Demuxers for which a full in-memory index is mandatory will ignore
+     * this.
+     * - muxing: unused
+     * - demuxing: set by user
+     */
+    unsigned int max_index_size;
+
+    /**
+     * Maximum amount of memory in bytes to use for buffering frames
+     * obtained from realtime capture devices.
+     */
+    unsigned int max_picture_buffer;
+
+    /**
+     * Number of chapters in AVChapter array.
+     * When muxing, chapters are normally written in the file header,
+     * so nb_chapters should normally be initialized before write_header
+     * is called. Some muxers (e.g. mov and mkv) can also write chapters
+     * in the trailer.  To write chapters in the trailer, nb_chapters
+     * must be zero when write_header is called and non-zero when
+     * write_trailer is called.
+     * - muxing: set by user
+     * - demuxing: set by libavformat
+     */
+    unsigned int nb_chapters;
+    AVChapter **chapters;
+
+    /**
+     * Metadata that applies to the whole file.
+     *
+     * - demuxing: set by libavformat in avformat_open_input()
+     * - muxing: may be set by the caller before avformat_write_header()
+     *
+     * Freed by libavformat in avformat_free_context().
+     */
+    AVDictionary *metadata;
+
+    /**
+     * Start time of the stream in real world time, in microseconds
+     * since the Unix epoch (00:00 1st January 1970). That is, pts=0 in the
+     * stream was captured at this real world time.
+     * - muxing: Set by the caller before avformat_write_header(). If set to
+     *           either 0 or AV_NOPTS_VALUE, then the current wall-time will
+     *           be used.
+     * - demuxing: Set by libavformat. AV_NOPTS_VALUE if unknown. Note that
+     *             the value may become known after some number of frames
+     *             have been received.
+     */
+    int64_t start_time_realtime;
+
+    /**
+     * The number of frames used for determining the framerate in
+     * avformat_find_stream_info().
+     * Demuxing only, set by the caller before avformat_find_stream_info().
+     */
+    int fps_probe_size;
+
+    /**
+     * Error recognition; higher values will detect more errors but may
+     * misdetect some more or less valid parts as errors.
+     * Demuxing only, set by the caller before avformat_open_input().
+     */
+    int error_recognition;
+
+    /**
+     * Custom interrupt callbacks for the I/O layer.
+     *
+     * demuxing: set by the user before avformat_open_input().
+     * muxing: set by the user before avformat_write_header()
+     * (mainly useful for AVFMT_NOFILE formats). The callback
+     * should also be passed to avio_open2() if it's used to
+     * open the file.
+     */
+    AVIOInterruptCB interrupt_callback;
+
+    /**
+     * Flags to enable debugging.
+     */
+    int debug;
+#define FF_FDEBUG_TS        0x0001
+
+    /**
+     * Maximum buffering duration for interleaving.
+     *
+     * To ensure all the streams are interleaved correctly,
+     * av_interleaved_write_frame() will wait until it has at least one packet
+     * for each stream before actually writing any packets to the output file.
+     * When some streams are "sparse" (i.e. there are large gaps between
+     * successive packets), this can result in excessive buffering.
+     *
+     * This field specifies the maximum difference between the timestamps of the
+     * first and the last packet in the muxing queue, above which libavformat
+     * will output a packet regardless of whether it has queued a packet for all
+     * the streams.
+     *
+     * Muxing only, set by the caller before avformat_write_header().
+     */
+    int64_t max_interleave_delta;
+
+    /**
+     * Allow non-standard and experimental extension
+     * @see AVCodecContext.strict_std_compliance
+     */
+    int strict_std_compliance;
+
+    /**
+     * Flags for the user to detect events happening on the file. Flags must
+     * be cleared by the user once the event has been handled.
+     * A combination of AVFMT_EVENT_FLAG_*.
+     */
+    int event_flags;
+#define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.
+
+    /**
+     * Maximum number of packets to read while waiting for the first timestamp.
+     * Decoding only.
+     */
+    int max_ts_probe;
+
+    /**
+     * Avoid negative timestamps during muxing.
+     * Any value of the AVFMT_AVOID_NEG_TS_* constants.
+     * Note, this only works when using av_interleaved_write_frame. (interleave_packet_per_dts is in use)
+     * - muxing: Set by user
+     * - demuxing: unused
+     */
+    int avoid_negative_ts;
+#define AVFMT_AVOID_NEG_TS_AUTO             -1 ///< Enabled when required by target format
+#define AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 1 ///< Shift timestamps so they are non negative
+#define AVFMT_AVOID_NEG_TS_MAKE_ZERO         2 ///< Shift timestamps so that they start at 0
+
+    /**
+     * Transport stream id.
+     * This will be moved into demuxer private options. Thus no API/ABI compatibility
+     */
+    int ts_id;
+
+    /**
+     * Audio preload in microseconds.
+     * Note, not all formats support this and unpredictable things may happen if it is used when not supported.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    int audio_preload;
+
+    /**
+     * Max chunk time in microseconds.
+     * Note, not all formats support this and unpredictable things may happen if it is used when not supported.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    int max_chunk_duration;
+
+    /**
+     * Max chunk size in bytes
+     * Note, not all formats support this and unpredictable things may happen if it is used when not supported.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    int max_chunk_size;
+
+    /**
+     * forces the use of wallclock timestamps as pts/dts of packets
+     * This has undefined results in the presence of B frames.
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    int use_wallclock_as_timestamps;
+
+    /**
+     * avio flags, used to force AVIO_FLAG_DIRECT.
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    int avio_flags;
+
+    /**
+     * The duration field can be estimated through various ways, and this field can be used
+     * to know how the duration was estimated.
+     * - encoding: unused
+     * - decoding: Read by user
+     */
+    enum AVDurationEstimationMethod duration_estimation_method;
+
+    /**
+     * Skip initial bytes when opening stream
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    int64_t skip_initial_bytes;
+
+    /**
+     * Correct single timestamp overflows
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    unsigned int correct_ts_overflow;
+
+    /**
+     * Force seeking to any (also non key) frames.
+     * - encoding: unused
+     * - decoding: Set by user
+     */
+    int seek2any;
+
+    /**
+     * Flush the I/O context after each packet.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    int flush_packets;
+
+    /**
+     * format probing score.
+     * The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes
+     * the format.
+     * - encoding: unused
+     * - decoding: set by avformat, read by user
+     */
+    int probe_score;
+
+    /**
+     * number of bytes to read maximally to identify format.
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    int format_probesize;
+
+    /**
+     * ',' separated list of allowed decoders.
+     * If NULL then all are allowed
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    char *codec_whitelist;
+
+    /**
+     * ',' separated list of allowed demuxers.
+     * If NULL then all are allowed
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    char *format_whitelist;
+
+    /**
+     * An opaque field for libavformat internal usage.
+     * Must not be accessed in any way by callers.
+     */
+    AVFormatInternal *internal;
+
+    /**
+     * IO repositioned flag.
+     * This is set by avformat when the underlaying IO context read pointer
+     * is repositioned, for example when doing byte based seeking.
+     * Demuxers can use the flag to detect such changes.
+     */
+    int io_repositioned;
+
+    /**
+     * Forced video codec.
+     * This allows forcing a specific decoder, even when there are multiple with
+     * the same codec_id.
+     * Demuxing: Set by user
+     */
+    AVCodec *video_codec;
+
+    /**
+     * Forced audio codec.
+     * This allows forcing a specific decoder, even when there are multiple with
+     * the same codec_id.
+     * Demuxing: Set by user
+     */
+    AVCodec *audio_codec;
+
+    /**
+     * Forced subtitle codec.
+     * This allows forcing a specific decoder, even when there are multiple with
+     * the same codec_id.
+     * Demuxing: Set by user
+     */
+    AVCodec *subtitle_codec;
+
+    /**
+     * Forced data codec.
+     * This allows forcing a specific decoder, even when there are multiple with
+     * the same codec_id.
+     * Demuxing: Set by user
+     */
+    AVCodec *data_codec;
+
+    /**
+     * Number of bytes to be written as padding in a metadata header.
+     * Demuxing: Unused.
+     * Muxing: Set by user via av_format_set_metadata_header_padding.
+     */
+    int metadata_header_padding;
+
+    /**
+     * User data.
+     * This is a place for some private data of the user.
+     */
+    void *opaque;
+
+    /**
+     * Callback used by devices to communicate with application.
+     */
+    av_format_control_message control_message_cb;
+
+    /**
+     * Output timestamp offset, in microseconds.
+     * Muxing: set by user
+     */
+    int64_t output_ts_offset;
+
+    /**
+     * dump format separator.
+     * can be ", " or "\n      " or anything else
+     * - muxing: Set by user.
+     * - demuxing: Set by user.
+     */
+    uint8_t *dump_separator;
+
+    /**
+     * Forced Data codec_id.
+     * Demuxing: Set by user.
+     */
+    enum AVCodecID data_codec_id;
+
+#if FF_API_OLD_OPEN_CALLBACKS
+    /**
+     * Called to open further IO contexts when needed for demuxing.
+     *
+     * This can be set by the user application to perform security checks on
+     * the URLs before opening them.
+     * The function should behave like avio_open2(), AVFormatContext is provided
+     * as contextual information and to reach AVFormatContext.opaque.
+     *
+     * If NULL then some simple checks are used together with avio_open2().
+     *
+     * Must not be accessed directly from outside avformat.
+     * @See av_format_set_open_cb()
+     *
+     * Demuxing: Set by user.
+     *
+     * @deprecated Use io_open and io_close.
+     */
+    attribute_deprecated
+    int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
+#endif
+
+    /**
+     * ',' separated list of allowed protocols.
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    char *protocol_whitelist;
+
+    /**
+     * A callback for opening new IO streams.
+     *
+     * Whenever a muxer or a demuxer needs to open an IO stream (typically from
+     * avformat_open_input() for demuxers, but for certain formats can happen at
+     * other times as well), it will call this callback to obtain an IO context.
+     *
+     * @param s the format context
+     * @param pb on success, the newly opened IO context should be returned here
+     * @param url the url to open
+     * @param flags a combination of AVIO_FLAG_*
+     * @param options a dictionary of additional options, with the same
+     *                semantics as in avio_open2()
+     * @return 0 on success, a negative AVERROR code on failure
+     *
+     * @note Certain muxers and demuxers do nesting, i.e. they open one or more
+     * additional internal format contexts. Thus the AVFormatContext pointer
+     * passed to this callback may be different from the one facing the caller.
+     * It will, however, have the same 'opaque' field.
+     */
+    int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,
+                   int flags, AVDictionary **options);
+
+    /**
+     * A callback for closing the streams opened with AVFormatContext.io_open().
+     */
+    void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);
+
+    /**
+     * ',' separated list of disallowed protocols.
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    char *protocol_blacklist;
+
+    /**
+     * The maximum number of streams.
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    int max_streams;
+
+    /**
+     * Skip duration calcuation in estimate_timings_from_pts.
+     * - encoding: unused
+     * - decoding: set by user
+     */
+    int skip_estimate_duration_from_pts;
+} AVFormatContext;
+
+#if FF_API_FORMAT_GET_SET
+/**
+ * Accessors for some AVFormatContext fields. These used to be provided for ABI
+ * compatibility, and do not need to be used anymore.
+ */
+attribute_deprecated
+int av_format_get_probe_score(const AVFormatContext *s);
+attribute_deprecated
+AVCodec * av_format_get_video_codec(const AVFormatContext *s);
+attribute_deprecated
+void      av_format_set_video_codec(AVFormatContext *s, AVCodec *c);
+attribute_deprecated
+AVCodec * av_format_get_audio_codec(const AVFormatContext *s);
+attribute_deprecated
+void      av_format_set_audio_codec(AVFormatContext *s, AVCodec *c);
+attribute_deprecated
+AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s);
+attribute_deprecated
+void      av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c);
+attribute_deprecated
+AVCodec * av_format_get_data_codec(const AVFormatContext *s);
+attribute_deprecated
+void      av_format_set_data_codec(AVFormatContext *s, AVCodec *c);
+attribute_deprecated
+int       av_format_get_metadata_header_padding(const AVFormatContext *s);
+attribute_deprecated
+void      av_format_set_metadata_header_padding(AVFormatContext *s, int c);
+attribute_deprecated
+void *    av_format_get_opaque(const AVFormatContext *s);
+attribute_deprecated
+void      av_format_set_opaque(AVFormatContext *s, void *opaque);
+attribute_deprecated
+av_format_control_message av_format_get_control_message_cb(const AVFormatContext *s);
+attribute_deprecated
+void      av_format_set_control_message_cb(AVFormatContext *s, av_format_control_message callback);
+#if FF_API_OLD_OPEN_CALLBACKS
+attribute_deprecated AVOpenCallback av_format_get_open_cb(const AVFormatContext *s);
+attribute_deprecated void av_format_set_open_cb(AVFormatContext *s, AVOpenCallback callback);
+#endif
+#endif
+
+/**
+ * This function will cause global side data to be injected in the next packet
+ * of each stream as well as after any subsequent seek.
+ */
+void av_format_inject_global_side_data(AVFormatContext *s);
+
+/**
+ * Returns the method used to set ctx->duration.
+ *
+ * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE.
+ */
+enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx);
+
+typedef struct AVPacketList {
+    AVPacket pkt;
+    struct AVPacketList *next;
+} AVPacketList;
+
+
+/**
+ * @defgroup lavf_core Core functions
+ * @ingroup libavf
+ *
+ * Functions for querying libavformat capabilities, allocating core structures,
+ * etc.
+ * @{
+ */
+
+/**
+ * Return the LIBAVFORMAT_VERSION_INT constant.
+ */
+unsigned avformat_version(void);
+
+/**
+ * Return the libavformat build-time configuration.
+ */
+const char *avformat_configuration(void);
+
+/**
+ * Return the libavformat license.
+ */
+const char *avformat_license(void);
+
+#if FF_API_NEXT
+/**
+ * Initialize libavformat and register all the muxers, demuxers and
+ * protocols. If you do not call this function, then you can select
+ * exactly which formats you want to support.
+ *
+ * @see av_register_input_format()
+ * @see av_register_output_format()
+ */
+attribute_deprecated
+void av_register_all(void);
+
+attribute_deprecated
+void av_register_input_format(AVInputFormat *format);
+attribute_deprecated
+void av_register_output_format(AVOutputFormat *format);
+#endif
+
+/**
+ * Do global initialization of network libraries. This is optional,
+ * and not recommended anymore.
+ *
+ * This functions only exists to work around thread-safety issues
+ * with older GnuTLS or OpenSSL libraries. If libavformat is linked
+ * to newer versions of those libraries, or if you do not use them,
+ * calling this function is unnecessary. Otherwise, you need to call
+ * this function before any other threads using them are started.
+ *
+ * This function will be deprecated once support for older GnuTLS and
+ * OpenSSL libraries is removed, and this function has no purpose
+ * anymore.
+ */
+int avformat_network_init(void);
+
+/**
+ * Undo the initialization done by avformat_network_init. Call it only
+ * once for each time you called avformat_network_init.
+ */
+int avformat_network_deinit(void);
+
+#if FF_API_NEXT
+/**
+ * If f is NULL, returns the first registered input format,
+ * if f is non-NULL, returns the next registered input format after f
+ * or NULL if f is the last one.
+ */
+attribute_deprecated
+AVInputFormat  *av_iformat_next(const AVInputFormat  *f);
+
+/**
+ * If f is NULL, returns the first registered output format,
+ * if f is non-NULL, returns the next registered output format after f
+ * or NULL if f is the last one.
+ */
+attribute_deprecated
+AVOutputFormat *av_oformat_next(const AVOutputFormat *f);
+#endif
+
+/**
+ * Iterate over all registered muxers.
+ *
+ * @param opaque a pointer where libavformat will store the iteration state. Must
+ *               point to NULL to start the iteration.
+ *
+ * @return the next registered muxer or NULL when the iteration is
+ *         finished
+ */
+const AVOutputFormat *av_muxer_iterate(void **opaque);
+
+/**
+ * Iterate over all registered demuxers.
+ *
+ * @param opaque a pointer where libavformat will store the iteration state. Must
+ *               point to NULL to start the iteration.
+ *
+ * @return the next registered demuxer or NULL when the iteration is
+ *         finished
+ */
+const AVInputFormat *av_demuxer_iterate(void **opaque);
+
+/**
+ * Allocate an AVFormatContext.
+ * avformat_free_context() can be used to free the context and everything
+ * allocated by the framework within it.
+ */
+AVFormatContext *avformat_alloc_context(void);
+
+/**
+ * Free an AVFormatContext and all its streams.
+ * @param s context to free
+ */
+void avformat_free_context(AVFormatContext *s);
+
+/**
+ * Get the AVClass for AVFormatContext. It can be used in combination with
+ * AV_OPT_SEARCH_FAKE_OBJ for examining options.
+ *
+ * @see av_opt_find().
+ */
+const AVClass *avformat_get_class(void);
+
+/**
+ * Add a new stream to a media file.
+ *
+ * When demuxing, it is called by the demuxer in read_header(). If the
+ * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also
+ * be called in read_packet().
+ *
+ * When muxing, should be called by the user before avformat_write_header().
+ *
+ * User is required to call avcodec_close() and avformat_free_context() to
+ * clean up the allocation by avformat_new_stream().
+ *
+ * @param s media file handle
+ * @param c If non-NULL, the AVCodecContext corresponding to the new stream
+ * will be initialized to use this codec. This is needed for e.g. codec-specific
+ * defaults to be set, so codec should be provided if it is known.
+ *
+ * @return newly created stream or NULL on error.
+ */
+AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);
+
+/**
+ * Wrap an existing array as stream side data.
+ *
+ * @param st stream
+ * @param type side information type
+ * @param data the side data array. It must be allocated with the av_malloc()
+ *             family of functions. The ownership of the data is transferred to
+ *             st.
+ * @param size side information size
+ * @return zero on success, a negative AVERROR code on failure. On failure,
+ *         the stream is unchanged and the data remains owned by the caller.
+ */
+int av_stream_add_side_data(AVStream *st, enum AVPacketSideDataType type,
+                            uint8_t *data, size_t size);
+
+/**
+ * Allocate new information from stream.
+ *
+ * @param stream stream
+ * @param type desired side information type
+ * @param size side information size
+ * @return pointer to fresh allocated data or NULL otherwise
+ */
+uint8_t *av_stream_new_side_data(AVStream *stream,
+                                 enum AVPacketSideDataType type, int size);
+/**
+ * Get side information from stream.
+ *
+ * @param stream stream
+ * @param type desired side information type
+ * @param size pointer for side information size to store (optional)
+ * @return pointer to data if present or NULL otherwise
+ */
+uint8_t *av_stream_get_side_data(const AVStream *stream,
+                                 enum AVPacketSideDataType type, int *size);
+
+AVProgram *av_new_program(AVFormatContext *s, int id);
+
+/**
+ * @}
+ */
+
+
+/**
+ * Allocate an AVFormatContext for an output format.
+ * avformat_free_context() can be used to free the context and
+ * everything allocated by the framework within it.
+ *
+ * @param *ctx is set to the created format context, or to NULL in
+ * case of failure
+ * @param oformat format to use for allocating the context, if NULL
+ * format_name and filename are used instead
+ * @param format_name the name of output format to use for allocating the
+ * context, if NULL filename is used instead
+ * @param filename the name of the filename to use for allocating the
+ * context, may be NULL
+ * @return >= 0 in case of success, a negative AVERROR code in case of
+ * failure
+ */
+int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat,
+                                   const char *format_name, const char *filename);
+
+/**
+ * @addtogroup lavf_decoding
+ * @{
+ */
+
+/**
+ * Find AVInputFormat based on the short name of the input format.
+ */
+AVInputFormat *av_find_input_format(const char *short_name);
+
+/**
+ * Guess the file format.
+ *
+ * @param pd        data to be probed
+ * @param is_opened Whether the file is already opened; determines whether
+ *                  demuxers with or without AVFMT_NOFILE are probed.
+ */
+AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened);
+
+/**
+ * Guess the file format.
+ *
+ * @param pd        data to be probed
+ * @param is_opened Whether the file is already opened; determines whether
+ *                  demuxers with or without AVFMT_NOFILE are probed.
+ * @param score_max A probe score larger that this is required to accept a
+ *                  detection, the variable is set to the actual detection
+ *                  score afterwards.
+ *                  If the score is <= AVPROBE_SCORE_MAX / 4 it is recommended
+ *                  to retry with a larger probe buffer.
+ */
+AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max);
+
+/**
+ * Guess the file format.
+ *
+ * @param is_opened Whether the file is already opened; determines whether
+ *                  demuxers with or without AVFMT_NOFILE are probed.
+ * @param score_ret The score of the best detection.
+ */
+AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret);
+
+/**
+ * Probe a bytestream to determine the input format. Each time a probe returns
+ * with a score that is too low, the probe buffer size is increased and another
+ * attempt is made. When the maximum probe size is reached, the input format
+ * with the highest score is returned.
+ *
+ * @param pb the bytestream to probe
+ * @param fmt the input format is put here
+ * @param url the url of the stream
+ * @param logctx the log context
+ * @param offset the offset within the bytestream to probe from
+ * @param max_probe_size the maximum probe buffer size (zero for default)
+ * @return the score in case of success, a negative value corresponding to an
+ *         the maximal score is AVPROBE_SCORE_MAX
+ * AVERROR code otherwise
+ */
+int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
+                           const char *url, void *logctx,
+                           unsigned int offset, unsigned int max_probe_size);
+
+/**
+ * Like av_probe_input_buffer2() but returns 0 on success
+ */
+int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt,
+                          const char *url, void *logctx,
+                          unsigned int offset, unsigned int max_probe_size);
+
+/**
+ * Open an input stream and read the header. The codecs are not opened.
+ * The stream must be closed with avformat_close_input().
+ *
+ * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
+ *           May be a pointer to NULL, in which case an AVFormatContext is allocated by this
+ *           function and written into ps.
+ *           Note that a user-supplied AVFormatContext will be freed on failure.
+ * @param url URL of the stream to open.
+ * @param fmt If non-NULL, this parameter forces a specific input format.
+ *            Otherwise the format is autodetected.
+ * @param options  A dictionary filled with AVFormatContext and demuxer-private options.
+ *                 On return this parameter will be destroyed and replaced with a dict containing
+ *                 options that were not found. May be NULL.
+ *
+ * @return 0 on success, a negative AVERROR on failure.
+ *
+ * @note If you want to use custom IO, preallocate the format context and set its pb field.
+ */
+int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
+
+attribute_deprecated
+int av_demuxer_open(AVFormatContext *ic);
+
+/**
+ * Read packets of a media file to get stream information. This
+ * is useful for file formats with no headers such as MPEG. This
+ * function also computes the real framerate in case of MPEG-2 repeat
+ * frame mode.
+ * The logical file position is not changed by this function;
+ * examined packets may be buffered for later processing.
+ *
+ * @param ic media file handle
+ * @param options  If non-NULL, an ic.nb_streams long array of pointers to
+ *                 dictionaries, where i-th member contains options for
+ *                 codec corresponding to i-th stream.
+ *                 On return each dictionary will be filled with options that were not found.
+ * @return >=0 if OK, AVERROR_xxx on error
+ *
+ * @note this function isn't guaranteed to open all the codecs, so
+ *       options being non-empty at return is a perfectly normal behavior.
+ *
+ * @todo Let the user decide somehow what information is needed so that
+ *       we do not waste time getting stuff the user does not need.
+ */
+int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
+
+/**
+ * Find the programs which belong to a given stream.
+ *
+ * @param ic    media file handle
+ * @param last  the last found program, the search will start after this
+ *              program, or from the beginning if it is NULL
+ * @param s     stream index
+ * @return the next program which belongs to s, NULL if no program is found or
+ *         the last program is not among the programs of ic.
+ */
+AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s);
+
+void av_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx);
+
+/**
+ * Find the "best" stream in the file.
+ * The best stream is determined according to various heuristics as the most
+ * likely to be what the user expects.
+ * If the decoder parameter is non-NULL, av_find_best_stream will find the
+ * default decoder for the stream's codec; streams for which no decoder can
+ * be found are ignored.
+ *
+ * @param ic                media file handle
+ * @param type              stream type: video, audio, subtitles, etc.
+ * @param wanted_stream_nb  user-requested stream number,
+ *                          or -1 for automatic selection
+ * @param related_stream    try to find a stream related (eg. in the same
+ *                          program) to this one, or -1 if none
+ * @param decoder_ret       if non-NULL, returns the decoder for the
+ *                          selected stream
+ * @param flags             flags; none are currently defined
+ * @return  the non-negative stream number in case of success,
+ *          AVERROR_STREAM_NOT_FOUND if no stream with the requested type
+ *          could be found,
+ *          AVERROR_DECODER_NOT_FOUND if streams were found but no decoder
+ * @note  If av_find_best_stream returns successfully and decoder_ret is not
+ *        NULL, then *decoder_ret is guaranteed to be set to a valid AVCodec.
+ */
+int av_find_best_stream(AVFormatContext *ic,
+                        enum AVMediaType type,
+                        int wanted_stream_nb,
+                        int related_stream,
+                        AVCodec **decoder_ret,
+                        int flags);
+
+/**
+ * Return the next frame of a stream.
+ * This function returns what is stored in the file, and does not validate
+ * that what is there are valid frames for the decoder. It will split what is
+ * stored in the file into frames and return one for each call. It will not
+ * omit invalid data between valid frames so as to give the decoder the maximum
+ * information possible for decoding.
+ *
+ * If pkt->buf is NULL, then the packet is valid until the next
+ * av_read_frame() or until avformat_close_input(). Otherwise the packet
+ * is valid indefinitely. In both cases the packet must be freed with
+ * av_packet_unref when it is no longer needed. For video, the packet contains
+ * exactly one frame. For audio, it contains an integer number of frames if each
+ * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
+ * have a variable size (e.g. MPEG audio), then it contains one frame.
+ *
+ * pkt->pts, pkt->dts and pkt->duration are always set to correct
+ * values in AVStream.time_base units (and guessed if the format cannot
+ * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
+ * has B-frames, so it is better to rely on pkt->dts if you do not
+ * decompress the payload.
+ *
+ * @return 0 if OK, < 0 on error or end of file
+ */
+int av_read_frame(AVFormatContext *s, AVPacket *pkt);
+
+/**
+ * Seek to the keyframe at timestamp.
+ * 'timestamp' in 'stream_index'.
+ *
+ * @param s media file handle
+ * @param stream_index If stream_index is (-1), a default
+ * stream is selected, and timestamp is automatically converted
+ * from AV_TIME_BASE units to the stream specific time_base.
+ * @param timestamp Timestamp in AVStream.time_base units
+ *        or, if no stream is specified, in AV_TIME_BASE units.
+ * @param flags flags which select direction and seeking mode
+ * @return >= 0 on success
+ */
+int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
+                  int flags);
+
+/**
+ * Seek to timestamp ts.
+ * Seeking will be done so that the point from which all active streams
+ * can be presented successfully will be closest to ts and within min/max_ts.
+ * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL.
+ *
+ * If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in bytes and
+ * are the file position (this may not be supported by all demuxers).
+ * If flags contain AVSEEK_FLAG_FRAME, then all timestamps are in frames
+ * in the stream with stream_index (this may not be supported by all demuxers).
+ * Otherwise all timestamps are in units of the stream selected by stream_index
+ * or if stream_index is -1, in AV_TIME_BASE units.
+ * If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as
+ * keyframes (this may not be supported by all demuxers).
+ * If flags contain AVSEEK_FLAG_BACKWARD, it is ignored.
+ *
+ * @param s media file handle
+ * @param stream_index index of the stream which is used as time base reference
+ * @param min_ts smallest acceptable timestamp
+ * @param ts target timestamp
+ * @param max_ts largest acceptable timestamp
+ * @param flags flags
+ * @return >=0 on success, error code otherwise
+ *
+ * @note This is part of the new seek API which is still under construction.
+ *       Thus do not use this yet. It may change at any time, do not expect
+ *       ABI compatibility yet!
+ */
+int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
+
+/**
+ * Discard all internally buffered data. This can be useful when dealing with
+ * discontinuities in the byte stream. Generally works only with formats that
+ * can resync. This includes headerless formats like MPEG-TS/TS but should also
+ * work with NUT, Ogg and in a limited way AVI for example.
+ *
+ * The set of streams, the detected duration, stream parameters and codecs do
+ * not change when calling this function. If you want a complete reset, it's
+ * better to open a new AVFormatContext.
+ *
+ * This does not flush the AVIOContext (s->pb). If necessary, call
+ * avio_flush(s->pb) before calling this function.
+ *
+ * @param s media file handle
+ * @return >=0 on success, error code otherwise
+ */
+int avformat_flush(AVFormatContext *s);
+
+/**
+ * Start playing a network-based stream (e.g. RTSP stream) at the
+ * current position.
+ */
+int av_read_play(AVFormatContext *s);
+
+/**
+ * Pause a network-based stream (e.g. RTSP stream).
+ *
+ * Use av_read_play() to resume it.
+ */
+int av_read_pause(AVFormatContext *s);
+
+/**
+ * Close an opened input AVFormatContext. Free it and all its contents
+ * and set *s to NULL.
+ */
+void avformat_close_input(AVFormatContext **s);
+/**
+ * @}
+ */
+
+#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward
+#define AVSEEK_FLAG_BYTE     2 ///< seeking based on position in bytes
+#define AVSEEK_FLAG_ANY      4 ///< seek to any frame, even non-keyframes
+#define AVSEEK_FLAG_FRAME    8 ///< seeking based on frame number
+
+/**
+ * @addtogroup lavf_encoding
+ * @{
+ */
+
+#define AVSTREAM_INIT_IN_WRITE_HEADER 0 ///< stream parameters initialized in avformat_write_header
+#define AVSTREAM_INIT_IN_INIT_OUTPUT  1 ///< stream parameters initialized in avformat_init_output
+
+/**
+ * Allocate the stream private data and write the stream header to
+ * an output media file.
+ *
+ * @param s Media file handle, must be allocated with avformat_alloc_context().
+ *          Its oformat field must be set to the desired output format;
+ *          Its pb field must be set to an already opened AVIOContext.
+ * @param options  An AVDictionary filled with AVFormatContext and muxer-private options.
+ *                 On return this parameter will be destroyed and replaced with a dict containing
+ *                 options that were not found. May be NULL.
+ *
+ * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec had not already been fully initialized in avformat_init,
+ *         AVSTREAM_INIT_IN_INIT_OUTPUT  on success if the codec had already been fully initialized in avformat_init,
+ *         negative AVERROR on failure.
+ *
+ * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_init_output.
+ */
+av_warn_unused_result
+int avformat_write_header(AVFormatContext *s, AVDictionary **options);
+
+/**
+ * Allocate the stream private data and initialize the codec, but do not write the header.
+ * May optionally be used before avformat_write_header to initialize stream parameters
+ * before actually writing the header.
+ * If using this function, do not pass the same options to avformat_write_header.
+ *
+ * @param s Media file handle, must be allocated with avformat_alloc_context().
+ *          Its oformat field must be set to the desired output format;
+ *          Its pb field must be set to an already opened AVIOContext.
+ * @param options  An AVDictionary filled with AVFormatContext and muxer-private options.
+ *                 On return this parameter will be destroyed and replaced with a dict containing
+ *                 options that were not found. May be NULL.
+ *
+ * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec requires avformat_write_header to fully initialize,
+ *         AVSTREAM_INIT_IN_INIT_OUTPUT  on success if the codec has been fully initialized,
+ *         negative AVERROR on failure.
+ *
+ * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_write_header.
+ */
+av_warn_unused_result
+int avformat_init_output(AVFormatContext *s, AVDictionary **options);
+
+/**
+ * Write a packet to an output media file.
+ *
+ * This function passes the packet directly to the muxer, without any buffering
+ * or reordering. The caller is responsible for correctly interleaving the
+ * packets if the format requires it. Callers that want libavformat to handle
+ * the interleaving should call av_interleaved_write_frame() instead of this
+ * function.
+ *
+ * @param s media file handle
+ * @param pkt The packet containing the data to be written. Note that unlike
+ *            av_interleaved_write_frame(), this function does not take
+ *            ownership of the packet passed to it (though some muxers may make
+ *            an internal reference to the input packet).
+ *            <br>
+ *            This parameter can be NULL (at any time, not just at the end), in
+ *            order to immediately flush data buffered within the muxer, for
+ *            muxers that buffer up data internally before writing it to the
+ *            output.
+ *            <br>
+ *            Packet's @ref AVPacket.stream_index "stream_index" field must be
+ *            set to the index of the corresponding stream in @ref
+ *            AVFormatContext.streams "s->streams".
+ *            <br>
+ *            The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts")
+ *            must be set to correct values in the stream's timebase (unless the
+ *            output format is flagged with the AVFMT_NOTIMESTAMPS flag, then
+ *            they can be set to AV_NOPTS_VALUE).
+ *            The dts for subsequent packets passed to this function must be strictly
+ *            increasing when compared in their respective timebases (unless the
+ *            output format is flagged with the AVFMT_TS_NONSTRICT, then they
+ *            merely have to be nondecreasing).  @ref AVPacket.duration
+ *            "duration") should also be set if known.
+ * @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush
+ *
+ * @see av_interleaved_write_frame()
+ */
+int av_write_frame(AVFormatContext *s, AVPacket *pkt);
+
+/**
+ * Write a packet to an output media file ensuring correct interleaving.
+ *
+ * This function will buffer the packets internally as needed to make sure the
+ * packets in the output file are properly interleaved in the order of
+ * increasing dts. Callers doing their own interleaving should call
+ * av_write_frame() instead of this function.
+ *
+ * Using this function instead of av_write_frame() can give muxers advance
+ * knowledge of future packets, improving e.g. the behaviour of the mp4
+ * muxer for VFR content in fragmenting mode.
+ *
+ * @param s media file handle
+ * @param pkt The packet containing the data to be written.
+ *            <br>
+ *            If the packet is reference-counted, this function will take
+ *            ownership of this reference and unreference it later when it sees
+ *            fit.
+ *            The caller must not access the data through this reference after
+ *            this function returns. If the packet is not reference-counted,
+ *            libavformat will make a copy.
+ *            <br>
+ *            This parameter can be NULL (at any time, not just at the end), to
+ *            flush the interleaving queues.
+ *            <br>
+ *            Packet's @ref AVPacket.stream_index "stream_index" field must be
+ *            set to the index of the corresponding stream in @ref
+ *            AVFormatContext.streams "s->streams".
+ *            <br>
+ *            The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts")
+ *            must be set to correct values in the stream's timebase (unless the
+ *            output format is flagged with the AVFMT_NOTIMESTAMPS flag, then
+ *            they can be set to AV_NOPTS_VALUE).
+ *            The dts for subsequent packets in one stream must be strictly
+ *            increasing (unless the output format is flagged with the
+ *            AVFMT_TS_NONSTRICT, then they merely have to be nondecreasing).
+ *            @ref AVPacket.duration "duration") should also be set if known.
+ *
+ * @return 0 on success, a negative AVERROR on error. Libavformat will always
+ *         take care of freeing the packet, even if this function fails.
+ *
+ * @see av_write_frame(), AVFormatContext.max_interleave_delta
+ */
+int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
+
+/**
+ * Write an uncoded frame to an output media file.
+ *
+ * The frame must be correctly interleaved according to the container
+ * specification; if not, then av_interleaved_write_frame() must be used.
+ *
+ * See av_interleaved_write_frame() for details.
+ */
+int av_write_uncoded_frame(AVFormatContext *s, int stream_index,
+                           AVFrame *frame);
+
+/**
+ * Write an uncoded frame to an output media file.
+ *
+ * If the muxer supports it, this function makes it possible to write an AVFrame
+ * structure directly, without encoding it into a packet.
+ * It is mostly useful for devices and similar special muxers that use raw
+ * video or PCM data and will not serialize it into a byte stream.
+ *
+ * To test whether it is possible to use it with a given muxer and stream,
+ * use av_write_uncoded_frame_query().
+ *
+ * The caller gives up ownership of the frame and must not access it
+ * afterwards.
+ *
+ * @return  >=0 for success, a negative code on error
+ */
+int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index,
+                                       AVFrame *frame);
+
+/**
+ * Test whether a muxer supports uncoded frame.
+ *
+ * @return  >=0 if an uncoded frame can be written to that muxer and stream,
+ *          <0 if not
+ */
+int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index);
+
+/**
+ * Write the stream trailer to an output media file and free the
+ * file private data.
+ *
+ * May only be called after a successful call to avformat_write_header.
+ *
+ * @param s media file handle
+ * @return 0 if OK, AVERROR_xxx on error
+ */
+int av_write_trailer(AVFormatContext *s);
+
+/**
+ * Return the output format in the list of registered output formats
+ * which best matches the provided parameters, or return NULL if
+ * there is no match.
+ *
+ * @param short_name if non-NULL checks if short_name matches with the
+ * names of the registered formats
+ * @param filename if non-NULL checks if filename terminates with the
+ * extensions of the registered formats
+ * @param mime_type if non-NULL checks if mime_type matches with the
+ * MIME type of the registered formats
+ */
+AVOutputFormat *av_guess_format(const char *short_name,
+                                const char *filename,
+                                const char *mime_type);
+
+/**
+ * Guess the codec ID based upon muxer and filename.
+ */
+enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name,
+                            const char *filename, const char *mime_type,
+                            enum AVMediaType type);
+
+/**
+ * Get timing information for the data currently output.
+ * The exact meaning of "currently output" depends on the format.
+ * It is mostly relevant for devices that have an internal buffer and/or
+ * work in real time.
+ * @param s          media file handle
+ * @param stream     stream in the media file
+ * @param[out] dts   DTS of the last packet output for the stream, in stream
+ *                   time_base units
+ * @param[out] wall  absolute time when that packet whas output,
+ *                   in microsecond
+ * @return  0 if OK, AVERROR(ENOSYS) if the format does not support it
+ * Note: some formats or devices may not allow to measure dts and wall
+ * atomically.
+ */
+int av_get_output_timestamp(struct AVFormatContext *s, int stream,
+                            int64_t *dts, int64_t *wall);
+
+
+/**
+ * @}
+ */
+
+
+/**
+ * @defgroup lavf_misc Utility functions
+ * @ingroup libavf
+ * @{
+ *
+ * Miscellaneous utility functions related to both muxing and demuxing
+ * (or neither).
+ */
+
+/**
+ * Send a nice hexadecimal dump of a buffer to the specified file stream.
+ *
+ * @param f The file stream pointer where the dump should be sent to.
+ * @param buf buffer
+ * @param size buffer size
+ *
+ * @see av_hex_dump_log, av_pkt_dump2, av_pkt_dump_log2
+ */
+void av_hex_dump(FILE *f, const uint8_t *buf, int size);
+
+/**
+ * Send a nice hexadecimal dump of a buffer to the log.
+ *
+ * @param avcl A pointer to an arbitrary struct of which the first field is a
+ * pointer to an AVClass struct.
+ * @param level The importance level of the message, lower values signifying
+ * higher importance.
+ * @param buf buffer
+ * @param size buffer size
+ *
+ * @see av_hex_dump, av_pkt_dump2, av_pkt_dump_log2
+ */
+void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size);
+
+/**
+ * Send a nice dump of a packet to the specified file stream.
+ *
+ * @param f The file stream pointer where the dump should be sent to.
+ * @param pkt packet to dump
+ * @param dump_payload True if the payload must be displayed, too.
+ * @param st AVStream that the packet belongs to
+ */
+void av_pkt_dump2(FILE *f, const AVPacket *pkt, int dump_payload, const AVStream *st);
+
+
+/**
+ * Send a nice dump of a packet to the log.
+ *
+ * @param avcl A pointer to an arbitrary struct of which the first field is a
+ * pointer to an AVClass struct.
+ * @param level The importance level of the message, lower values signifying
+ * higher importance.
+ * @param pkt packet to dump
+ * @param dump_payload True if the payload must be displayed, too.
+ * @param st AVStream that the packet belongs to
+ */
+void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_payload,
+                      const AVStream *st);
+
+/**
+ * Get the AVCodecID for the given codec tag tag.
+ * If no codec id is found returns AV_CODEC_ID_NONE.
+ *
+ * @param tags list of supported codec_id-codec_tag pairs, as stored
+ * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag
+ * @param tag  codec tag to match to a codec ID
+ */
+enum AVCodecID av_codec_get_id(const struct AVCodecTag * const *tags, unsigned int tag);
+
+/**
+ * Get the codec tag for the given codec id id.
+ * If no codec tag is found returns 0.
+ *
+ * @param tags list of supported codec_id-codec_tag pairs, as stored
+ * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag
+ * @param id   codec ID to match to a codec tag
+ */
+unsigned int av_codec_get_tag(const struct AVCodecTag * const *tags, enum AVCodecID id);
+
+/**
+ * Get the codec tag for the given codec id.
+ *
+ * @param tags list of supported codec_id - codec_tag pairs, as stored
+ * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag
+ * @param id codec id that should be searched for in the list
+ * @param tag A pointer to the found tag
+ * @return 0 if id was not found in tags, > 0 if it was found
+ */
+int av_codec_get_tag2(const struct AVCodecTag * const *tags, enum AVCodecID id,
+                      unsigned int *tag);
+
+int av_find_default_stream_index(AVFormatContext *s);
+
+/**
+ * Get the index for a specific timestamp.
+ *
+ * @param st        stream that the timestamp belongs to
+ * @param timestamp timestamp to retrieve the index for
+ * @param flags if AVSEEK_FLAG_BACKWARD then the returned index will correspond
+ *                 to the timestamp which is <= the requested one, if backward
+ *                 is 0, then it will be >=
+ *              if AVSEEK_FLAG_ANY seek to any frame, only keyframes otherwise
+ * @return < 0 if no such timestamp could be found
+ */
+int av_index_search_timestamp(AVStream *st, int64_t timestamp, int flags);
+
+/**
+ * Add an index entry into a sorted list. Update the entry if the list
+ * already contains it.
+ *
+ * @param timestamp timestamp in the time base of the given stream
+ */
+int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp,
+                       int size, int distance, int flags);
+
+
+/**
+ * Split a URL string into components.
+ *
+ * The pointers to buffers for storing individual components may be null,
+ * in order to ignore that component. Buffers for components not found are
+ * set to empty strings. If the port is not found, it is set to a negative
+ * value.
+ *
+ * @param proto the buffer for the protocol
+ * @param proto_size the size of the proto buffer
+ * @param authorization the buffer for the authorization
+ * @param authorization_size the size of the authorization buffer
+ * @param hostname the buffer for the host name
+ * @param hostname_size the size of the hostname buffer
+ * @param port_ptr a pointer to store the port number in
+ * @param path the buffer for the path
+ * @param path_size the size of the path buffer
+ * @param url the URL to split
+ */
+void av_url_split(char *proto,         int proto_size,
+                  char *authorization, int authorization_size,
+                  char *hostname,      int hostname_size,
+                  int *port_ptr,
+                  char *path,          int path_size,
+                  const char *url);
+
+
+/**
+ * Print detailed information about the input or output format, such as
+ * duration, bitrate, streams, container, programs, metadata, side data,
+ * codec and time base.
+ *
+ * @param ic        the context to analyze
+ * @param index     index of the stream to dump information about
+ * @param url       the URL to print, such as source or destination file
+ * @param is_output Select whether the specified context is an input(0) or output(1)
+ */
+void av_dump_format(AVFormatContext *ic,
+                    int index,
+                    const char *url,
+                    int is_output);
+
+
+#define AV_FRAME_FILENAME_FLAGS_MULTIPLE 1 ///< Allow multiple %d
+
+/**
+ * Return in 'buf' the path with '%d' replaced by a number.
+ *
+ * Also handles the '%0nd' format where 'n' is the total number
+ * of digits and '%%'.
+ *
+ * @param buf destination buffer
+ * @param buf_size destination buffer size
+ * @param path numbered sequence string
+ * @param number frame number
+ * @param flags AV_FRAME_FILENAME_FLAGS_*
+ * @return 0 if OK, -1 on format error
+ */
+int av_get_frame_filename2(char *buf, int buf_size,
+                          const char *path, int number, int flags);
+
+int av_get_frame_filename(char *buf, int buf_size,
+                          const char *path, int number);
+
+/**
+ * Check whether filename actually is a numbered sequence generator.
+ *
+ * @param filename possible numbered sequence string
+ * @return 1 if a valid numbered sequence string, 0 otherwise
+ */
+int av_filename_number_test(const char *filename);
+
+/**
+ * Generate an SDP for an RTP session.
+ *
+ * Note, this overwrites the id values of AVStreams in the muxer contexts
+ * for getting unique dynamic payload types.
+ *
+ * @param ac array of AVFormatContexts describing the RTP streams. If the
+ *           array is composed by only one context, such context can contain
+ *           multiple AVStreams (one AVStream per RTP stream). Otherwise,
+ *           all the contexts in the array (an AVCodecContext per RTP stream)
+ *           must contain only one AVStream.
+ * @param n_files number of AVCodecContexts contained in ac
+ * @param buf buffer where the SDP will be stored (must be allocated by
+ *            the caller)
+ * @param size the size of the buffer
+ * @return 0 if OK, AVERROR_xxx on error
+ */
+int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size);
+
+/**
+ * Return a positive value if the given filename has one of the given
+ * extensions, 0 otherwise.
+ *
+ * @param filename   file name to check against the given extensions
+ * @param extensions a comma-separated list of filename extensions
+ */
+int av_match_ext(const char *filename, const char *extensions);
+
+/**
+ * Test if the given container can store a codec.
+ *
+ * @param ofmt           container to check for compatibility
+ * @param codec_id       codec to potentially store in container
+ * @param std_compliance standards compliance level, one of FF_COMPLIANCE_*
+ *
+ * @return 1 if codec with ID codec_id can be stored in ofmt, 0 if it cannot.
+ *         A negative number if this information is not available.
+ */
+int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id,
+                         int std_compliance);
+
+/**
+ * @defgroup riff_fourcc RIFF FourCCs
+ * @{
+ * Get the tables mapping RIFF FourCCs to libavcodec AVCodecIDs. The tables are
+ * meant to be passed to av_codec_get_id()/av_codec_get_tag() as in the
+ * following code:
+ * @code
+ * uint32_t tag = MKTAG('H', '2', '6', '4');
+ * const struct AVCodecTag *table[] = { avformat_get_riff_video_tags(), 0 };
+ * enum AVCodecID id = av_codec_get_id(table, tag);
+ * @endcode
+ */
+/**
+ * @return the table mapping RIFF FourCCs for video to libavcodec AVCodecID.
+ */
+const struct AVCodecTag *avformat_get_riff_video_tags(void);
+/**
+ * @return the table mapping RIFF FourCCs for audio to AVCodecID.
+ */
+const struct AVCodecTag *avformat_get_riff_audio_tags(void);
+/**
+ * @return the table mapping MOV FourCCs for video to libavcodec AVCodecID.
+ */
+const struct AVCodecTag *avformat_get_mov_video_tags(void);
+/**
+ * @return the table mapping MOV FourCCs for audio to AVCodecID.
+ */
+const struct AVCodecTag *avformat_get_mov_audio_tags(void);
+
+/**
+ * @}
+ */
+
+/**
+ * Guess the sample aspect ratio of a frame, based on both the stream and the
+ * frame aspect ratio.
+ *
+ * Since the frame aspect ratio is set by the codec but the stream aspect ratio
+ * is set by the demuxer, these two may not be equal. This function tries to
+ * return the value that you should use if you would like to display the frame.
+ *
+ * Basic logic is to use the stream aspect ratio if it is set to something sane
+ * otherwise use the frame aspect ratio. This way a container setting, which is
+ * usually easy to modify can override the coded value in the frames.
+ *
+ * @param format the format context which the stream is part of
+ * @param stream the stream which the frame is part of
+ * @param frame the frame with the aspect ratio to be determined
+ * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea
+ */
+AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame);
+
+/**
+ * Guess the frame rate, based on both the container and codec information.
+ *
+ * @param ctx the format context which the stream is part of
+ * @param stream the stream which the frame is part of
+ * @param frame the frame for which the frame rate should be determined, may be NULL
+ * @return the guessed (valid) frame rate, 0/1 if no idea
+ */
+AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame);
+
+/**
+ * Check if the stream st contained in s is matched by the stream specifier
+ * spec.
+ *
+ * See the "stream specifiers" chapter in the documentation for the syntax
+ * of spec.
+ *
+ * @return  >0 if st is matched by spec;
+ *          0  if st is not matched by spec;
+ *          AVERROR code if spec is invalid
+ *
+ * @note  A stream specifier can match several streams in the format.
+ */
+int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
+                                    const char *spec);
+
+int avformat_queue_attached_pictures(AVFormatContext *s);
+
+#if FF_API_OLD_BSF
+/**
+ * Apply a list of bitstream filters to a packet.
+ *
+ * @param codec AVCodecContext, usually from an AVStream
+ * @param pkt the packet to apply filters to. If, on success, the returned
+ *        packet has size == 0 and side_data_elems == 0, it indicates that
+ *        the packet should be dropped
+ * @param bsfc a NULL-terminated list of filters to apply
+ * @return  >=0 on success;
+ *          AVERROR code on failure
+ */
+attribute_deprecated
+int av_apply_bitstream_filters(AVCodecContext *codec, AVPacket *pkt,
+                               AVBitStreamFilterContext *bsfc);
+#endif
+
+enum AVTimebaseSource {
+    AVFMT_TBCF_AUTO = -1,
+    AVFMT_TBCF_DECODER,
+    AVFMT_TBCF_DEMUXER,
+#if FF_API_R_FRAME_RATE
+    AVFMT_TBCF_R_FRAMERATE,
+#endif
+};
+
+/**
+ * Transfer internal timing information from one stream to another.
+ *
+ * This function is useful when doing stream copy.
+ *
+ * @param ofmt     target output format for ost
+ * @param ost      output stream which needs timings copy and adjustments
+ * @param ist      reference input stream to copy timings from
+ * @param copy_tb  define from where the stream codec timebase needs to be imported
+ */
+int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt,
+                                                  AVStream *ost, const AVStream *ist,
+                                                  enum AVTimebaseSource copy_tb);
+
+/**
+ * Get the internal codec timebase from a stream.
+ *
+ * @param st  input stream to extract the timebase from
+ */
+AVRational av_stream_get_codec_timebase(const AVStream *st);
+
+/**
+ * @}
+ */
+
+#endif /* AVFORMAT_AVFORMAT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avio.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avio.h
new file mode 100644
index 0000000..75912ce
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/avio.h
@@ -0,0 +1,861 @@
+/*
+ * 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 AVFORMAT_AVIO_H
+#define AVFORMAT_AVIO_H
+
+/**
+ * @file
+ * @ingroup lavf_io
+ * Buffered I/O operations
+ */
+
+#include <stdint.h>
+
+#include "libavutil/common.h"
+#include "libavutil/dict.h"
+#include "libavutil/log.h"
+
+#include "libavformat/version.h"
+
+/**
+ * Seeking works like for a local file.
+ */
+#define AVIO_SEEKABLE_NORMAL (1 << 0)
+
+/**
+ * Seeking by timestamp with avio_seek_time() is possible.
+ */
+#define AVIO_SEEKABLE_TIME   (1 << 1)
+
+/**
+ * Callback for checking whether to abort blocking functions.
+ * AVERROR_EXIT is returned in this case by the interrupted
+ * function. During blocking operations, callback is called with
+ * opaque as parameter. If the callback returns 1, the
+ * blocking operation will be aborted.
+ *
+ * No members can be added to this struct without a major bump, if
+ * new elements have been added after this struct in AVFormatContext
+ * or AVIOContext.
+ */
+typedef struct AVIOInterruptCB {
+    int (*callback)(void*);
+    void *opaque;
+} AVIOInterruptCB;
+
+/**
+ * Directory entry types.
+ */
+enum AVIODirEntryType {
+    AVIO_ENTRY_UNKNOWN,
+    AVIO_ENTRY_BLOCK_DEVICE,
+    AVIO_ENTRY_CHARACTER_DEVICE,
+    AVIO_ENTRY_DIRECTORY,
+    AVIO_ENTRY_NAMED_PIPE,
+    AVIO_ENTRY_SYMBOLIC_LINK,
+    AVIO_ENTRY_SOCKET,
+    AVIO_ENTRY_FILE,
+    AVIO_ENTRY_SERVER,
+    AVIO_ENTRY_SHARE,
+    AVIO_ENTRY_WORKGROUP,
+};
+
+/**
+ * Describes single entry of the directory.
+ *
+ * Only name and type fields are guaranteed be set.
+ * Rest of fields are protocol or/and platform dependent and might be unknown.
+ */
+typedef struct AVIODirEntry {
+    char *name;                           /**< Filename */
+    int type;                             /**< Type of the entry */
+    int utf8;                             /**< Set to 1 when name is encoded with UTF-8, 0 otherwise.
+                                               Name can be encoded with UTF-8 even though 0 is set. */
+    int64_t size;                         /**< File size in bytes, -1 if unknown. */
+    int64_t modification_timestamp;       /**< Time of last modification in microseconds since unix
+                                               epoch, -1 if unknown. */
+    int64_t access_timestamp;             /**< Time of last access in microseconds since unix epoch,
+                                               -1 if unknown. */
+    int64_t status_change_timestamp;      /**< Time of last status change in microseconds since unix
+                                               epoch, -1 if unknown. */
+    int64_t user_id;                      /**< User ID of owner, -1 if unknown. */
+    int64_t group_id;                     /**< Group ID of owner, -1 if unknown. */
+    int64_t filemode;                     /**< Unix file mode, -1 if unknown. */
+} AVIODirEntry;
+
+typedef struct AVIODirContext {
+    struct URLContext *url_context;
+} AVIODirContext;
+
+/**
+ * Different data types that can be returned via the AVIO
+ * write_data_type callback.
+ */
+enum AVIODataMarkerType {
+    /**
+     * Header data; this needs to be present for the stream to be decodeable.
+     */
+    AVIO_DATA_MARKER_HEADER,
+    /**
+     * A point in the output bytestream where a decoder can start decoding
+     * (i.e. a keyframe). A demuxer/decoder given the data flagged with
+     * AVIO_DATA_MARKER_HEADER, followed by any AVIO_DATA_MARKER_SYNC_POINT,
+     * should give decodeable results.
+     */
+    AVIO_DATA_MARKER_SYNC_POINT,
+    /**
+     * A point in the output bytestream where a demuxer can start parsing
+     * (for non self synchronizing bytestream formats). That is, any
+     * non-keyframe packet start point.
+     */
+    AVIO_DATA_MARKER_BOUNDARY_POINT,
+    /**
+     * This is any, unlabelled data. It can either be a muxer not marking
+     * any positions at all, it can be an actual boundary/sync point
+     * that the muxer chooses not to mark, or a later part of a packet/fragment
+     * that is cut into multiple write callbacks due to limited IO buffer size.
+     */
+    AVIO_DATA_MARKER_UNKNOWN,
+    /**
+     * Trailer data, which doesn't contain actual content, but only for
+     * finalizing the output file.
+     */
+    AVIO_DATA_MARKER_TRAILER,
+    /**
+     * A point in the output bytestream where the underlying AVIOContext might
+     * flush the buffer depending on latency or buffering requirements. Typically
+     * means the end of a packet.
+     */
+    AVIO_DATA_MARKER_FLUSH_POINT,
+};
+
+/**
+ * Bytestream IO Context.
+ * New fields can be added to the end with minor version bumps.
+ * Removal, reordering and changes to existing fields require a major
+ * version bump.
+ * sizeof(AVIOContext) must not be used outside libav*.
+ *
+ * @note None of the function pointers in AVIOContext should be called
+ *       directly, they should only be set by the client application
+ *       when implementing custom I/O. Normally these are set to the
+ *       function pointers specified in avio_alloc_context()
+ */
+typedef struct AVIOContext {
+    /**
+     * A class for private options.
+     *
+     * If this AVIOContext is created by avio_open2(), av_class is set and
+     * passes the options down to protocols.
+     *
+     * If this AVIOContext is manually allocated, then av_class may be set by
+     * the caller.
+     *
+     * warning -- this field can be NULL, be sure to not pass this AVIOContext
+     * to any av_opt_* functions in that case.
+     */
+    const AVClass *av_class;
+
+    /*
+     * The following shows the relationship between buffer, buf_ptr,
+     * buf_ptr_max, buf_end, buf_size, and pos, when reading and when writing
+     * (since AVIOContext is used for both):
+     *
+     **********************************************************************************
+     *                                   READING
+     **********************************************************************************
+     *
+     *                            |              buffer_size              |
+     *                            |---------------------------------------|
+     *                            |                                       |
+     *
+     *                         buffer          buf_ptr       buf_end
+     *                            +---------------+-----------------------+
+     *                            |/ / / / / / / /|/ / / / / / /|         |
+     *  read buffer:              |/ / consumed / | to be read /|         |
+     *                            |/ / / / / / / /|/ / / / / / /|         |
+     *                            +---------------+-----------------------+
+     *
+     *                                                         pos
+     *              +-------------------------------------------+-----------------+
+     *  input file: |                                           |                 |
+     *              +-------------------------------------------+-----------------+
+     *
+     *
+     **********************************************************************************
+     *                                   WRITING
+     **********************************************************************************
+     *
+     *                             |          buffer_size                 |
+     *                             |--------------------------------------|
+     *                             |                                      |
+     *
+     *                                                buf_ptr_max
+     *                          buffer                 (buf_ptr)       buf_end
+     *                             +-----------------------+--------------+
+     *                             |/ / / / / / / / / / / /|              |
+     *  write buffer:              | / / to be flushed / / |              |
+     *                             |/ / / / / / / / / / / /|              |
+     *                             +-----------------------+--------------+
+     *                               buf_ptr can be in this
+     *                               due to a backward seek
+     *
+     *                            pos
+     *               +-------------+----------------------------------------------+
+     *  output file: |             |                                              |
+     *               +-------------+----------------------------------------------+
+     *
+     */
+    unsigned char *buffer;  /**< Start of the buffer. */
+    int buffer_size;        /**< Maximum buffer size */
+    unsigned char *buf_ptr; /**< Current position in the buffer */
+    unsigned char *buf_end; /**< End of the data, may be less than
+                                 buffer+buffer_size if the read function returned
+                                 less data than requested, e.g. for streams where
+                                 no more data has been received yet. */
+    void *opaque;           /**< A private pointer, passed to the read/write/seek/...
+                                 functions. */
+    int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
+    int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
+    int64_t (*seek)(void *opaque, int64_t offset, int whence);
+    int64_t pos;            /**< position in the file of the current buffer */
+    int eof_reached;        /**< true if eof reached */
+    int write_flag;         /**< true if open for writing */
+    int max_packet_size;
+    unsigned long checksum;
+    unsigned char *checksum_ptr;
+    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
+    int error;              /**< contains the error code or 0 if no error happened */
+    /**
+     * Pause or resume playback for network streaming protocols - e.g. MMS.
+     */
+    int (*read_pause)(void *opaque, int pause);
+    /**
+     * Seek to a given timestamp in stream with the specified stream_index.
+     * Needed for some network streaming protocols which don't support seeking
+     * to byte position.
+     */
+    int64_t (*read_seek)(void *opaque, int stream_index,
+                         int64_t timestamp, int flags);
+    /**
+     * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
+     */
+    int seekable;
+
+    /**
+     * max filesize, used to limit allocations
+     * This field is internal to libavformat and access from outside is not allowed.
+     */
+    int64_t maxsize;
+
+    /**
+     * avio_read and avio_write should if possible be satisfied directly
+     * instead of going through a buffer, and avio_seek will always
+     * call the underlying seek function directly.
+     */
+    int direct;
+
+    /**
+     * Bytes read statistic
+     * This field is internal to libavformat and access from outside is not allowed.
+     */
+    int64_t bytes_read;
+
+    /**
+     * seek statistic
+     * This field is internal to libavformat and access from outside is not allowed.
+     */
+    int seek_count;
+
+    /**
+     * writeout statistic
+     * This field is internal to libavformat and access from outside is not allowed.
+     */
+    int writeout_count;
+
+    /**
+     * Original buffer size
+     * used internally after probing and ensure seekback to reset the buffer size
+     * This field is internal to libavformat and access from outside is not allowed.
+     */
+    int orig_buffer_size;
+
+    /**
+     * Threshold to favor readahead over seek.
+     * This is current internal only, do not use from outside.
+     */
+    int short_seek_threshold;
+
+    /**
+     * ',' separated list of allowed protocols.
+     */
+    const char *protocol_whitelist;
+
+    /**
+     * ',' separated list of disallowed protocols.
+     */
+    const char *protocol_blacklist;
+
+    /**
+     * A callback that is used instead of write_packet.
+     */
+    int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,
+                           enum AVIODataMarkerType type, int64_t time);
+    /**
+     * If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT,
+     * but ignore them and treat them as AVIO_DATA_MARKER_UNKNOWN (to avoid needlessly
+     * small chunks of data returned from the callback).
+     */
+    int ignore_boundary_point;
+
+    /**
+     * Internal, not meant to be used from outside of AVIOContext.
+     */
+    enum AVIODataMarkerType current_type;
+    int64_t last_time;
+
+    /**
+     * A callback that is used instead of short_seek_threshold.
+     * This is current internal only, do not use from outside.
+     */
+    int (*short_seek_get)(void *opaque);
+
+    int64_t written;
+
+    /**
+     * Maximum reached position before a backward seek in the write buffer,
+     * used keeping track of already written data for a later flush.
+     */
+    unsigned char *buf_ptr_max;
+
+    /**
+     * Try to buffer at least this amount of data before flushing it
+     */
+    int min_packet_size;
+} AVIOContext;
+
+/**
+ * Return the name of the protocol that will handle the passed URL.
+ *
+ * NULL is returned if no protocol could be found for the given URL.
+ *
+ * @return Name of the protocol or NULL.
+ */
+const char *avio_find_protocol_name(const char *url);
+
+/**
+ * Return AVIO_FLAG_* access flags corresponding to the access permissions
+ * of the resource in url, or a negative value corresponding to an
+ * AVERROR code in case of failure. The returned access flags are
+ * masked by the value in flags.
+ *
+ * @note This function is intrinsically unsafe, in the sense that the
+ * checked resource may change its existence or permission status from
+ * one call to another. Thus you should not trust the returned value,
+ * unless you are sure that no other processes are accessing the
+ * checked resource.
+ */
+int avio_check(const char *url, int flags);
+
+/**
+ * Move or rename a resource.
+ *
+ * @note url_src and url_dst should share the same protocol and authority.
+ *
+ * @param url_src url to resource to be moved
+ * @param url_dst new url to resource if the operation succeeded
+ * @return >=0 on success or negative on error.
+ */
+int avpriv_io_move(const char *url_src, const char *url_dst);
+
+/**
+ * Delete a resource.
+ *
+ * @param url resource to be deleted.
+ * @return >=0 on success or negative on error.
+ */
+int avpriv_io_delete(const char *url);
+
+/**
+ * Open directory for reading.
+ *
+ * @param s       directory read context. Pointer to a NULL pointer must be passed.
+ * @param url     directory to be listed.
+ * @param options A dictionary filled with protocol-private options. On return
+ *                this parameter will be destroyed and replaced with a dictionary
+ *                containing options that were not found. May be NULL.
+ * @return >=0 on success or negative on error.
+ */
+int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options);
+
+/**
+ * Get next directory entry.
+ *
+ * Returned entry must be freed with avio_free_directory_entry(). In particular
+ * it may outlive AVIODirContext.
+ *
+ * @param s         directory read context.
+ * @param[out] next next entry or NULL when no more entries.
+ * @return >=0 on success or negative on error. End of list is not considered an
+ *             error.
+ */
+int avio_read_dir(AVIODirContext *s, AVIODirEntry **next);
+
+/**
+ * Close directory.
+ *
+ * @note Entries created using avio_read_dir() are not deleted and must be
+ * freeded with avio_free_directory_entry().
+ *
+ * @param s         directory read context.
+ * @return >=0 on success or negative on error.
+ */
+int avio_close_dir(AVIODirContext **s);
+
+/**
+ * Free entry allocated by avio_read_dir().
+ *
+ * @param entry entry to be freed.
+ */
+void avio_free_directory_entry(AVIODirEntry **entry);
+
+/**
+ * Allocate and initialize an AVIOContext for buffered I/O. It must be later
+ * freed with avio_context_free().
+ *
+ * @param buffer Memory block for input/output operations via AVIOContext.
+ *        The buffer must be allocated with av_malloc() and friends.
+ *        It may be freed and replaced with a new buffer by libavformat.
+ *        AVIOContext.buffer holds the buffer currently in use,
+ *        which must be later freed with av_free().
+ * @param buffer_size The buffer size is very important for performance.
+ *        For protocols with fixed blocksize it should be set to this blocksize.
+ *        For others a typical size is a cache page, e.g. 4kb.
+ * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
+ * @param opaque An opaque pointer to user-specific data.
+ * @param read_packet  A function for refilling the buffer, may be NULL.
+ *                     For stream protocols, must never return 0 but rather
+ *                     a proper AVERROR code.
+ * @param write_packet A function for writing the buffer contents, may be NULL.
+ *        The function may not change the input buffers content.
+ * @param seek A function for seeking to specified byte position, may be NULL.
+ *
+ * @return Allocated AVIOContext or NULL on failure.
+ */
+AVIOContext *avio_alloc_context(
+                  unsigned char *buffer,
+                  int buffer_size,
+                  int write_flag,
+                  void *opaque,
+                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
+                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
+                  int64_t (*seek)(void *opaque, int64_t offset, int whence));
+
+/**
+ * Free the supplied IO context and everything associated with it.
+ *
+ * @param s Double pointer to the IO context. This function will write NULL
+ * into s.
+ */
+void avio_context_free(AVIOContext **s);
+
+void avio_w8(AVIOContext *s, int b);
+void avio_write(AVIOContext *s, const unsigned char *buf, int size);
+void avio_wl64(AVIOContext *s, uint64_t val);
+void avio_wb64(AVIOContext *s, uint64_t val);
+void avio_wl32(AVIOContext *s, unsigned int val);
+void avio_wb32(AVIOContext *s, unsigned int val);
+void avio_wl24(AVIOContext *s, unsigned int val);
+void avio_wb24(AVIOContext *s, unsigned int val);
+void avio_wl16(AVIOContext *s, unsigned int val);
+void avio_wb16(AVIOContext *s, unsigned int val);
+
+/**
+ * Write a NULL-terminated string.
+ * @return number of bytes written.
+ */
+int avio_put_str(AVIOContext *s, const char *str);
+
+/**
+ * Convert an UTF-8 string to UTF-16LE and write it.
+ * @param s the AVIOContext
+ * @param str NULL-terminated UTF-8 string
+ *
+ * @return number of bytes written.
+ */
+int avio_put_str16le(AVIOContext *s, const char *str);
+
+/**
+ * Convert an UTF-8 string to UTF-16BE and write it.
+ * @param s the AVIOContext
+ * @param str NULL-terminated UTF-8 string
+ *
+ * @return number of bytes written.
+ */
+int avio_put_str16be(AVIOContext *s, const char *str);
+
+/**
+ * Mark the written bytestream as a specific type.
+ *
+ * Zero-length ranges are omitted from the output.
+ *
+ * @param time the stream time the current bytestream pos corresponds to
+ *             (in AV_TIME_BASE units), or AV_NOPTS_VALUE if unknown or not
+ *             applicable
+ * @param type the kind of data written starting at the current pos
+ */
+void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type);
+
+/**
+ * ORing this as the "whence" parameter to a seek function causes it to
+ * return the filesize without seeking anywhere. Supporting this is optional.
+ * If it is not supported then the seek function will return <0.
+ */
+#define AVSEEK_SIZE 0x10000
+
+/**
+ * Passing this flag as the "whence" parameter to a seek function causes it to
+ * seek by any means (like reopening and linear reading) or other normally unreasonable
+ * means that can be extremely slow.
+ * This may be ignored by the seek code.
+ */
+#define AVSEEK_FORCE 0x20000
+
+/**
+ * fseek() equivalent for AVIOContext.
+ * @return new position or AVERROR.
+ */
+int64_t avio_seek(AVIOContext *s, int64_t offset, int whence);
+
+/**
+ * Skip given number of bytes forward
+ * @return new position or AVERROR.
+ */
+int64_t avio_skip(AVIOContext *s, int64_t offset);
+
+/**
+ * ftell() equivalent for AVIOContext.
+ * @return position or AVERROR.
+ */
+static av_always_inline int64_t avio_tell(AVIOContext *s)
+{
+    return avio_seek(s, 0, SEEK_CUR);
+}
+
+/**
+ * Get the filesize.
+ * @return filesize or AVERROR
+ */
+int64_t avio_size(AVIOContext *s);
+
+/**
+ * feof() equivalent for AVIOContext.
+ * @return non zero if and only if end of file
+ */
+int avio_feof(AVIOContext *s);
+
+/** @warning Writes up to 4 KiB per call */
+int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3);
+
+/**
+ * Force flushing of buffered data.
+ *
+ * For write streams, force the buffered data to be immediately written to the output,
+ * without to wait to fill the internal buffer.
+ *
+ * For read streams, discard all currently buffered data, and advance the
+ * reported file position to that of the underlying stream. This does not
+ * read new data, and does not perform any seeks.
+ */
+void avio_flush(AVIOContext *s);
+
+/**
+ * Read size bytes from AVIOContext into buf.
+ * @return number of bytes read or AVERROR
+ */
+int avio_read(AVIOContext *s, unsigned char *buf, int size);
+
+/**
+ * Read size bytes from AVIOContext into buf. Unlike avio_read(), this is allowed
+ * to read fewer bytes than requested. The missing bytes can be read in the next
+ * call. This always tries to read at least 1 byte.
+ * Useful to reduce latency in certain cases.
+ * @return number of bytes read or AVERROR
+ */
+int avio_read_partial(AVIOContext *s, unsigned char *buf, int size);
+
+/**
+ * @name Functions for reading from AVIOContext
+ * @{
+ *
+ * @note return 0 if EOF, so you cannot use it if EOF handling is
+ *       necessary
+ */
+int          avio_r8  (AVIOContext *s);
+unsigned int avio_rl16(AVIOContext *s);
+unsigned int avio_rl24(AVIOContext *s);
+unsigned int avio_rl32(AVIOContext *s);
+uint64_t     avio_rl64(AVIOContext *s);
+unsigned int avio_rb16(AVIOContext *s);
+unsigned int avio_rb24(AVIOContext *s);
+unsigned int avio_rb32(AVIOContext *s);
+uint64_t     avio_rb64(AVIOContext *s);
+/**
+ * @}
+ */
+
+/**
+ * Read a string from pb into buf. The reading will terminate when either
+ * a NULL character was encountered, maxlen bytes have been read, or nothing
+ * more can be read from pb. The result is guaranteed to be NULL-terminated, it
+ * will be truncated if buf is too small.
+ * Note that the string is not interpreted or validated in any way, it
+ * might get truncated in the middle of a sequence for multi-byte encodings.
+ *
+ * @return number of bytes read (is always <= maxlen).
+ * If reading ends on EOF or error, the return value will be one more than
+ * bytes actually read.
+ */
+int avio_get_str(AVIOContext *pb, int maxlen, char *buf, int buflen);
+
+/**
+ * Read a UTF-16 string from pb and convert it to UTF-8.
+ * The reading will terminate when either a null or invalid character was
+ * encountered or maxlen bytes have been read.
+ * @return number of bytes read (is always <= maxlen)
+ */
+int avio_get_str16le(AVIOContext *pb, int maxlen, char *buf, int buflen);
+int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen);
+
+
+/**
+ * @name URL open modes
+ * The flags argument to avio_open must be one of the following
+ * constants, optionally ORed with other flags.
+ * @{
+ */
+#define AVIO_FLAG_READ  1                                      /**< read-only */
+#define AVIO_FLAG_WRITE 2                                      /**< write-only */
+#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE)  /**< read-write pseudo flag */
+/**
+ * @}
+ */
+
+/**
+ * Use non-blocking mode.
+ * If this flag is set, operations on the context will return
+ * AVERROR(EAGAIN) if they can not be performed immediately.
+ * If this flag is not set, operations on the context will never return
+ * AVERROR(EAGAIN).
+ * Note that this flag does not affect the opening/connecting of the
+ * context. Connecting a protocol will always block if necessary (e.g. on
+ * network protocols) but never hang (e.g. on busy devices).
+ * Warning: non-blocking protocols is work-in-progress; this flag may be
+ * silently ignored.
+ */
+#define AVIO_FLAG_NONBLOCK 8
+
+/**
+ * Use direct mode.
+ * avio_read and avio_write should if possible be satisfied directly
+ * instead of going through a buffer, and avio_seek will always
+ * call the underlying seek function directly.
+ */
+#define AVIO_FLAG_DIRECT 0x8000
+
+/**
+ * Create and initialize a AVIOContext for accessing the
+ * resource indicated by url.
+ * @note When the resource indicated by url has been opened in
+ * read+write mode, the AVIOContext can be used only for writing.
+ *
+ * @param s Used to return the pointer to the created AVIOContext.
+ * In case of failure the pointed to value is set to NULL.
+ * @param url resource to access
+ * @param flags flags which control how the resource indicated by url
+ * is to be opened
+ * @return >= 0 in case of success, a negative value corresponding to an
+ * AVERROR code in case of failure
+ */
+int avio_open(AVIOContext **s, const char *url, int flags);
+
+/**
+ * Create and initialize a AVIOContext for accessing the
+ * resource indicated by url.
+ * @note When the resource indicated by url has been opened in
+ * read+write mode, the AVIOContext can be used only for writing.
+ *
+ * @param s Used to return the pointer to the created AVIOContext.
+ * In case of failure the pointed to value is set to NULL.
+ * @param url resource to access
+ * @param flags flags which control how the resource indicated by url
+ * is to be opened
+ * @param int_cb an interrupt callback to be used at the protocols level
+ * @param options  A dictionary filled with protocol-private options. On return
+ * this parameter will be destroyed and replaced with a dict containing options
+ * that were not found. May be NULL.
+ * @return >= 0 in case of success, a negative value corresponding to an
+ * AVERROR code in case of failure
+ */
+int avio_open2(AVIOContext **s, const char *url, int flags,
+               const AVIOInterruptCB *int_cb, AVDictionary **options);
+
+/**
+ * Close the resource accessed by the AVIOContext s and free it.
+ * This function can only be used if s was opened by avio_open().
+ *
+ * The internal buffer is automatically flushed before closing the
+ * resource.
+ *
+ * @return 0 on success, an AVERROR < 0 on error.
+ * @see avio_closep
+ */
+int avio_close(AVIOContext *s);
+
+/**
+ * Close the resource accessed by the AVIOContext *s, free it
+ * and set the pointer pointing to it to NULL.
+ * This function can only be used if s was opened by avio_open().
+ *
+ * The internal buffer is automatically flushed before closing the
+ * resource.
+ *
+ * @return 0 on success, an AVERROR < 0 on error.
+ * @see avio_close
+ */
+int avio_closep(AVIOContext **s);
+
+
+/**
+ * Open a write only memory stream.
+ *
+ * @param s new IO context
+ * @return zero if no error.
+ */
+int avio_open_dyn_buf(AVIOContext **s);
+
+/**
+ * Return the written size and a pointer to the buffer.
+ * The AVIOContext stream is left intact.
+ * The buffer must NOT be freed.
+ * No padding is added to the buffer.
+ *
+ * @param s IO context
+ * @param pbuffer pointer to a byte buffer
+ * @return the length of the byte buffer
+ */
+int avio_get_dyn_buf(AVIOContext *s, uint8_t **pbuffer);
+
+/**
+ * Return the written size and a pointer to the buffer. The buffer
+ * must be freed with av_free().
+ * Padding of AV_INPUT_BUFFER_PADDING_SIZE is added to the buffer.
+ *
+ * @param s IO context
+ * @param pbuffer pointer to a byte buffer
+ * @return the length of the byte buffer
+ */
+int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer);
+
+/**
+ * Iterate through names of available protocols.
+ *
+ * @param opaque A private pointer representing current protocol.
+ *        It must be a pointer to NULL on first iteration and will
+ *        be updated by successive calls to avio_enum_protocols.
+ * @param output If set to 1, iterate over output protocols,
+ *               otherwise over input protocols.
+ *
+ * @return A static string containing the name of current protocol or NULL
+ */
+const char *avio_enum_protocols(void **opaque, int output);
+
+/**
+ * Pause and resume playing - only meaningful if using a network streaming
+ * protocol (e.g. MMS).
+ *
+ * @param h     IO context from which to call the read_pause function pointer
+ * @param pause 1 for pause, 0 for resume
+ */
+int     avio_pause(AVIOContext *h, int pause);
+
+/**
+ * Seek to a given timestamp relative to some component stream.
+ * Only meaningful if using a network streaming protocol (e.g. MMS.).
+ *
+ * @param h IO context from which to call the seek function pointers
+ * @param stream_index The stream index that the timestamp is relative to.
+ *        If stream_index is (-1) the timestamp should be in AV_TIME_BASE
+ *        units from the beginning of the presentation.
+ *        If a stream_index >= 0 is used and the protocol does not support
+ *        seeking based on component streams, the call will fail.
+ * @param timestamp timestamp in AVStream.time_base units
+ *        or if there is no stream specified then in AV_TIME_BASE units.
+ * @param flags Optional combination of AVSEEK_FLAG_BACKWARD, AVSEEK_FLAG_BYTE
+ *        and AVSEEK_FLAG_ANY. The protocol may silently ignore
+ *        AVSEEK_FLAG_BACKWARD and AVSEEK_FLAG_ANY, but AVSEEK_FLAG_BYTE will
+ *        fail if used and not supported.
+ * @return >= 0 on success
+ * @see AVInputFormat::read_seek
+ */
+int64_t avio_seek_time(AVIOContext *h, int stream_index,
+                       int64_t timestamp, int flags);
+
+/* Avoid a warning. The header can not be included because it breaks c++. */
+struct AVBPrint;
+
+/**
+ * Read contents of h into print buffer, up to max_size bytes, or up to EOF.
+ *
+ * @return 0 for success (max_size bytes read or EOF reached), negative error
+ * code otherwise
+ */
+int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size);
+
+/**
+ * Accept and allocate a client context on a server context.
+ * @param  s the server context
+ * @param  c the client context, must be unallocated
+ * @return   >= 0 on success or a negative value corresponding
+ *           to an AVERROR on failure
+ */
+int avio_accept(AVIOContext *s, AVIOContext **c);
+
+/**
+ * Perform one step of the protocol handshake to accept a new client.
+ * This function must be called on a client returned by avio_accept() before
+ * using it as a read/write context.
+ * It is separate from avio_accept() because it may block.
+ * A step of the handshake is defined by places where the application may
+ * decide to change the proceedings.
+ * For example, on a protocol with a request header and a reply header, each
+ * one can constitute a step because the application may use the parameters
+ * from the request to change parameters in the reply; or each individual
+ * chunk of the request can constitute a step.
+ * If the handshake is already finished, avio_handshake() does nothing and
+ * returns 0 immediately.
+ *
+ * @param  c the client context to perform the handshake on
+ * @return   0   on a complete and successful handshake
+ *           > 0 if the handshake progressed, but is not complete
+ *           < 0 for an AVERROR code
+ */
+int avio_handshake(AVIOContext *c);
+#endif /* AVFORMAT_AVIO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/version.h
new file mode 100644
index 0000000..ac14a55
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavformat/version.h
@@ -0,0 +1,111 @@
+/*
+ * Version macros.
+ *
+ * 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 AVFORMAT_VERSION_H
+#define AVFORMAT_VERSION_H
+
+/**
+ * @file
+ * @ingroup libavf
+ * Libavformat version macros
+ */
+
+#include "libavutil/version.h"
+
+// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
+// Also please add any ticket numbers that you believe might be affected here
+#define LIBAVFORMAT_VERSION_MAJOR  58
+#define LIBAVFORMAT_VERSION_MINOR  20
+#define LIBAVFORMAT_VERSION_MICRO 100
+
+#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
+                                               LIBAVFORMAT_VERSION_MINOR, \
+                                               LIBAVFORMAT_VERSION_MICRO)
+#define LIBAVFORMAT_VERSION     AV_VERSION(LIBAVFORMAT_VERSION_MAJOR,   \
+                                           LIBAVFORMAT_VERSION_MINOR,   \
+                                           LIBAVFORMAT_VERSION_MICRO)
+#define LIBAVFORMAT_BUILD       LIBAVFORMAT_VERSION_INT
+
+#define LIBAVFORMAT_IDENT       "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION)
+
+/**
+ * FF_API_* defines may be placed below to indicate public API that will be
+ * dropped at a future version bump. The defines themselves are not part of
+ * the public API and may change, break or disappear at any time.
+ *
+ * @note, when bumping the major version it is recommended to manually
+ * disable each FF_API_* in its own commit instead of disabling them all
+ * at once through the bump. This improves the git bisect-ability of the change.
+ *
+ */
+#ifndef FF_API_COMPUTE_PKT_FIELDS2
+#define FF_API_COMPUTE_PKT_FIELDS2      (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_OLD_OPEN_CALLBACKS
+#define FF_API_OLD_OPEN_CALLBACKS       (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_LAVF_AVCTX
+#define FF_API_LAVF_AVCTX               (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_HTTP_USER_AGENT
+#define FF_API_HTTP_USER_AGENT          (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_HLS_WRAP
+#define FF_API_HLS_WRAP                 (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_HLS_USE_LOCALTIME
+#define FF_API_HLS_USE_LOCALTIME        (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_LAVF_KEEPSIDE_FLAG
+#define FF_API_LAVF_KEEPSIDE_FLAG       (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_OLD_ROTATE_API
+#define FF_API_OLD_ROTATE_API           (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_FORMAT_GET_SET
+#define FF_API_FORMAT_GET_SET           (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_OLD_AVIO_EOF_0
+#define FF_API_OLD_AVIO_EOF_0           (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_LAVF_FFSERVER
+#define FF_API_LAVF_FFSERVER            (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_FORMAT_FILENAME
+#define FF_API_FORMAT_FILENAME          (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_OLD_RTSP_OPTIONS
+#define FF_API_OLD_RTSP_OPTIONS         (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_NEXT
+#define FF_API_NEXT                     (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_DASH_MIN_SEG_DURATION
+#define FF_API_DASH_MIN_SEG_DURATION    (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+#ifndef FF_API_LAVF_MP4A_LATM
+#define FF_API_LAVF_MP4A_LATM           (LIBAVFORMAT_VERSION_MAJOR < 59)
+#endif
+
+
+#ifndef FF_API_R_FRAME_RATE
+#define FF_API_R_FRAME_RATE            1
+#endif
+#endif /* AVFORMAT_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/avresample.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/avresample.h
new file mode 100644
index 0000000..5ac9adb
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/avresample.h
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * 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 AVRESAMPLE_AVRESAMPLE_H
+#define AVRESAMPLE_AVRESAMPLE_H
+
+/**
+ * @file
+ * @ingroup lavr
+ * external API header
+ */
+
+/**
+ * @defgroup lavr libavresample
+ * @{
+ *
+ * Libavresample (lavr) is a library that handles audio resampling, sample
+ * format conversion and mixing.
+ *
+ * Interaction with lavr is done through AVAudioResampleContext, which is
+ * allocated with avresample_alloc_context(). It is opaque, so all parameters
+ * must be set with the @ref avoptions API.
+ *
+ * For example the following code will setup conversion from planar float sample
+ * format to interleaved signed 16-bit integer, downsampling from 48kHz to
+ * 44.1kHz and downmixing from 5.1 channels to stereo (using the default mixing
+ * matrix):
+ * @code
+ * AVAudioResampleContext *avr = avresample_alloc_context();
+ * av_opt_set_int(avr, "in_channel_layout",  AV_CH_LAYOUT_5POINT1, 0);
+ * av_opt_set_int(avr, "out_channel_layout", AV_CH_LAYOUT_STEREO,  0);
+ * av_opt_set_int(avr, "in_sample_rate",     48000,                0);
+ * av_opt_set_int(avr, "out_sample_rate",    44100,                0);
+ * av_opt_set_int(avr, "in_sample_fmt",      AV_SAMPLE_FMT_FLTP,   0);
+ * av_opt_set_int(avr, "out_sample_fmt",     AV_SAMPLE_FMT_S16,    0);
+ * @endcode
+ *
+ * Once the context is initialized, it must be opened with avresample_open(). If
+ * you need to change the conversion parameters, you must close the context with
+ * avresample_close(), change the parameters as described above, then reopen it
+ * again.
+ *
+ * The conversion itself is done by repeatedly calling avresample_convert().
+ * Note that the samples may get buffered in two places in lavr. The first one
+ * is the output FIFO, where the samples end up if the output buffer is not
+ * large enough. The data stored in there may be retrieved at any time with
+ * avresample_read(). The second place is the resampling delay buffer,
+ * applicable only when resampling is done. The samples in it require more input
+ * before they can be processed. Their current amount is returned by
+ * avresample_get_delay(). At the end of conversion the resampling buffer can be
+ * flushed by calling avresample_convert() with NULL input.
+ *
+ * The following code demonstrates the conversion loop assuming the parameters
+ * from above and caller-defined functions get_input() and handle_output():
+ * @code
+ * uint8_t **input;
+ * int in_linesize, in_samples;
+ *
+ * while (get_input(&input, &in_linesize, &in_samples)) {
+ *     uint8_t *output
+ *     int out_linesize;
+ *     int out_samples = avresample_get_out_samples(avr, in_samples);
+ *
+ *     av_samples_alloc(&output, &out_linesize, 2, out_samples,
+ *                      AV_SAMPLE_FMT_S16, 0);
+ *     out_samples = avresample_convert(avr, &output, out_linesize, out_samples,
+ *                                      input, in_linesize, in_samples);
+ *     handle_output(output, out_linesize, out_samples);
+ *     av_freep(&output);
+ *  }
+ *  @endcode
+ *
+ *  When the conversion is finished and the FIFOs are flushed if required, the
+ *  conversion context and everything associated with it must be freed with
+ *  avresample_free().
+ */
+
+#include "libavutil/avutil.h"
+#include "libavutil/channel_layout.h"
+#include "libavutil/dict.h"
+#include "libavutil/frame.h"
+#include "libavutil/log.h"
+#include "libavutil/mathematics.h"
+
+#include "libavresample/version.h"
+
+#define AVRESAMPLE_MAX_CHANNELS 32
+
+typedef struct AVAudioResampleContext AVAudioResampleContext;
+
+/**
+ * @deprecated use libswresample
+ *
+ * Mixing Coefficient Types */
+enum attribute_deprecated AVMixCoeffType {
+    AV_MIX_COEFF_TYPE_Q8,   /** 16-bit 8.8 fixed-point                      */
+    AV_MIX_COEFF_TYPE_Q15,  /** 32-bit 17.15 fixed-point                    */
+    AV_MIX_COEFF_TYPE_FLT,  /** floating-point                              */
+    AV_MIX_COEFF_TYPE_NB,   /** Number of coeff types. Not part of ABI      */
+};
+
+/**
+ * @deprecated use libswresample
+ *
+ * Resampling Filter Types */
+enum attribute_deprecated AVResampleFilterType {
+    AV_RESAMPLE_FILTER_TYPE_CUBIC,              /**< Cubic */
+    AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL,   /**< Blackman Nuttall Windowed Sinc */
+    AV_RESAMPLE_FILTER_TYPE_KAISER,             /**< Kaiser Windowed Sinc */
+};
+
+/**
+ * @deprecated use libswresample
+ */
+enum attribute_deprecated AVResampleDitherMethod {
+    AV_RESAMPLE_DITHER_NONE,            /**< Do not use dithering */
+    AV_RESAMPLE_DITHER_RECTANGULAR,     /**< Rectangular Dither */
+    AV_RESAMPLE_DITHER_TRIANGULAR,      /**< Triangular Dither*/
+    AV_RESAMPLE_DITHER_TRIANGULAR_HP,   /**< Triangular Dither with High Pass */
+    AV_RESAMPLE_DITHER_TRIANGULAR_NS,   /**< Triangular Dither with Noise Shaping */
+    AV_RESAMPLE_DITHER_NB,              /**< Number of dither types. Not part of ABI. */
+};
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Return the LIBAVRESAMPLE_VERSION_INT constant.
+ */
+attribute_deprecated
+unsigned avresample_version(void);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Return the libavresample build-time configuration.
+ * @return  configure string
+ */
+attribute_deprecated
+const char *avresample_configuration(void);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Return the libavresample license.
+ */
+attribute_deprecated
+const char *avresample_license(void);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Get the AVClass for AVAudioResampleContext.
+ *
+ * Can be used in combination with AV_OPT_SEARCH_FAKE_OBJ for examining options
+ * without allocating a context.
+ *
+ * @see av_opt_find().
+ *
+ * @return AVClass for AVAudioResampleContext
+ */
+attribute_deprecated
+const AVClass *avresample_get_class(void);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Allocate AVAudioResampleContext and set options.
+ *
+ * @return  allocated audio resample context, or NULL on failure
+ */
+attribute_deprecated
+AVAudioResampleContext *avresample_alloc_context(void);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Initialize AVAudioResampleContext.
+ * @note The context must be configured using the AVOption API.
+ * @note The fields "in_channel_layout", "out_channel_layout",
+ *       "in_sample_rate", "out_sample_rate", "in_sample_fmt",
+ *       "out_sample_fmt" must be set.
+ *
+ * @see av_opt_set_int()
+ * @see av_opt_set_dict()
+ * @see av_get_default_channel_layout()
+ *
+ * @param avr  audio resample context
+ * @return     0 on success, negative AVERROR code on failure
+ */
+attribute_deprecated
+int avresample_open(AVAudioResampleContext *avr);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Check whether an AVAudioResampleContext is open or closed.
+ *
+ * @param avr AVAudioResampleContext to check
+ * @return 1 if avr is open, 0 if avr is closed.
+ */
+attribute_deprecated
+int avresample_is_open(AVAudioResampleContext *avr);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Close AVAudioResampleContext.
+ *
+ * This closes the context, but it does not change the parameters. The context
+ * can be reopened with avresample_open(). It does, however, clear the output
+ * FIFO and any remaining leftover samples in the resampling delay buffer. If
+ * there was a custom matrix being used, that is also cleared.
+ *
+ * @see avresample_convert()
+ * @see avresample_set_matrix()
+ *
+ * @param avr  audio resample context
+ */
+attribute_deprecated
+void avresample_close(AVAudioResampleContext *avr);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Free AVAudioResampleContext and associated AVOption values.
+ *
+ * This also calls avresample_close() before freeing.
+ *
+ * @param avr  audio resample context
+ */
+attribute_deprecated
+void avresample_free(AVAudioResampleContext **avr);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Generate a channel mixing matrix.
+ *
+ * This function is the one used internally by libavresample for building the
+ * default mixing matrix. It is made public just as a utility function for
+ * building custom matrices.
+ *
+ * @param in_layout           input channel layout
+ * @param out_layout          output channel layout
+ * @param center_mix_level    mix level for the center channel
+ * @param surround_mix_level  mix level for the surround channel(s)
+ * @param lfe_mix_level       mix level for the low-frequency effects channel
+ * @param normalize           if 1, coefficients will be normalized to prevent
+ *                            overflow. if 0, coefficients will not be
+ *                            normalized.
+ * @param[out] matrix         mixing coefficients; matrix[i + stride * o] is
+ *                            the weight of input channel i in output channel o.
+ * @param stride              distance between adjacent input channels in the
+ *                            matrix array
+ * @param matrix_encoding     matrixed stereo downmix mode (e.g. dplii)
+ * @return                    0 on success, negative AVERROR code on failure
+ */
+attribute_deprecated
+int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
+                            double center_mix_level, double surround_mix_level,
+                            double lfe_mix_level, int normalize, double *matrix,
+                            int stride, enum AVMatrixEncoding matrix_encoding);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Get the current channel mixing matrix.
+ *
+ * If no custom matrix has been previously set or the AVAudioResampleContext is
+ * not open, an error is returned.
+ *
+ * @param avr     audio resample context
+ * @param matrix  mixing coefficients; matrix[i + stride * o] is the weight of
+ *                input channel i in output channel o.
+ * @param stride  distance between adjacent input channels in the matrix array
+ * @return        0 on success, negative AVERROR code on failure
+ */
+attribute_deprecated
+int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix,
+                          int stride);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Set channel mixing matrix.
+ *
+ * Allows for setting a custom mixing matrix, overriding the default matrix
+ * generated internally during avresample_open(). This function can be called
+ * anytime on an allocated context, either before or after calling
+ * avresample_open(), as long as the channel layouts have been set.
+ * avresample_convert() always uses the current matrix.
+ * Calling avresample_close() on the context will clear the current matrix.
+ *
+ * @see avresample_close()
+ *
+ * @param avr     audio resample context
+ * @param matrix  mixing coefficients; matrix[i + stride * o] is the weight of
+ *                input channel i in output channel o.
+ * @param stride  distance between adjacent input channels in the matrix array
+ * @return        0 on success, negative AVERROR code on failure
+ */
+attribute_deprecated
+int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix,
+                          int stride);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Set a customized input channel mapping.
+ *
+ * This function can only be called when the allocated context is not open.
+ * Also, the input channel layout must have already been set.
+ *
+ * Calling avresample_close() on the context will clear the channel mapping.
+ *
+ * The map for each input channel specifies the channel index in the source to
+ * use for that particular channel, or -1 to mute the channel. Source channels
+ * can be duplicated by using the same index for multiple input channels.
+ *
+ * Examples:
+ *
+ * Reordering 5.1 AAC order (C,L,R,Ls,Rs,LFE) to FFmpeg order (L,R,C,LFE,Ls,Rs):
+ * { 1, 2, 0, 5, 3, 4 }
+ *
+ * Muting the 3rd channel in 4-channel input:
+ * { 0, 1, -1, 3 }
+ *
+ * Duplicating the left channel of stereo input:
+ * { 0, 0 }
+ *
+ * @param avr         audio resample context
+ * @param channel_map customized input channel mapping
+ * @return            0 on success, negative AVERROR code on failure
+ */
+attribute_deprecated
+int avresample_set_channel_mapping(AVAudioResampleContext *avr,
+                                   const int *channel_map);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Set compensation for resampling.
+ *
+ * This can be called anytime after avresample_open(). If resampling is not
+ * automatically enabled because of a sample rate conversion, the
+ * "force_resampling" option must have been set to 1 when opening the context
+ * in order to use resampling compensation.
+ *
+ * @param avr                    audio resample context
+ * @param sample_delta           compensation delta, in samples
+ * @param compensation_distance  compensation distance, in samples
+ * @return                       0 on success, negative AVERROR code on failure
+ */
+attribute_deprecated
+int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta,
+                                int compensation_distance);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Provide the upper bound on the number of samples the configured
+ * conversion would output.
+ *
+ * @param avr           audio resample context
+ * @param in_nb_samples number of input samples
+ *
+ * @return              number of samples or AVERROR(EINVAL) if the value
+ *                      would exceed INT_MAX
+ */
+attribute_deprecated
+int avresample_get_out_samples(AVAudioResampleContext *avr, int in_nb_samples);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Convert input samples and write them to the output FIFO.
+ *
+ * The upper bound on the number of output samples can be obtained through
+ * avresample_get_out_samples().
+ *
+ * The output data can be NULL or have fewer allocated samples than required.
+ * In this case, any remaining samples not written to the output will be added
+ * to an internal FIFO buffer, to be returned at the next call to this function
+ * or to avresample_read().
+ *
+ * If converting sample rate, there may be data remaining in the internal
+ * resampling delay buffer. avresample_get_delay() tells the number of remaining
+ * samples. To get this data as output, call avresample_convert() with NULL
+ * input.
+ *
+ * At the end of the conversion process, there may be data remaining in the
+ * internal FIFO buffer. avresample_available() tells the number of remaining
+ * samples. To get this data as output, either call avresample_convert() with
+ * NULL input or call avresample_read().
+ *
+ * @see avresample_get_out_samples()
+ * @see avresample_read()
+ * @see avresample_get_delay()
+ *
+ * @param avr             audio resample context
+ * @param output          output data pointers
+ * @param out_plane_size  output plane size, in bytes.
+ *                        This can be 0 if unknown, but that will lead to
+ *                        optimized functions not being used directly on the
+ *                        output, which could slow down some conversions.
+ * @param out_samples     maximum number of samples that the output buffer can hold
+ * @param input           input data pointers
+ * @param in_plane_size   input plane size, in bytes
+ *                        This can be 0 if unknown, but that will lead to
+ *                        optimized functions not being used directly on the
+ *                        input, which could slow down some conversions.
+ * @param in_samples      number of input samples to convert
+ * @return                number of samples written to the output buffer,
+ *                        not including converted samples added to the internal
+ *                        output FIFO
+ */
+attribute_deprecated
+int avresample_convert(AVAudioResampleContext *avr, uint8_t **output,
+                       int out_plane_size, int out_samples,
+                       uint8_t * const *input, int in_plane_size,
+                       int in_samples);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Return the number of samples currently in the resampling delay buffer.
+ *
+ * When resampling, there may be a delay between the input and output. Any
+ * unconverted samples in each call are stored internally in a delay buffer.
+ * This function allows the user to determine the current number of samples in
+ * the delay buffer, which can be useful for synchronization.
+ *
+ * @see avresample_convert()
+ *
+ * @param avr  audio resample context
+ * @return     number of samples currently in the resampling delay buffer
+ */
+attribute_deprecated
+int avresample_get_delay(AVAudioResampleContext *avr);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Return the number of available samples in the output FIFO.
+ *
+ * During conversion, if the user does not specify an output buffer or
+ * specifies an output buffer that is smaller than what is needed, remaining
+ * samples that are not written to the output are stored to an internal FIFO
+ * buffer. The samples in the FIFO can be read with avresample_read() or
+ * avresample_convert().
+ *
+ * @see avresample_read()
+ * @see avresample_convert()
+ *
+ * @param avr  audio resample context
+ * @return     number of samples available for reading
+ */
+attribute_deprecated
+int avresample_available(AVAudioResampleContext *avr);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Read samples from the output FIFO.
+ *
+ * During conversion, if the user does not specify an output buffer or
+ * specifies an output buffer that is smaller than what is needed, remaining
+ * samples that are not written to the output are stored to an internal FIFO
+ * buffer. This function can be used to read samples from that internal FIFO.
+ *
+ * @see avresample_available()
+ * @see avresample_convert()
+ *
+ * @param avr         audio resample context
+ * @param output      output data pointers. May be NULL, in which case
+ *                    nb_samples of data is discarded from output FIFO.
+ * @param nb_samples  number of samples to read from the FIFO
+ * @return            the number of samples written to output
+ */
+attribute_deprecated
+int avresample_read(AVAudioResampleContext *avr, uint8_t **output, int nb_samples);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Convert the samples in the input AVFrame and write them to the output AVFrame.
+ *
+ * Input and output AVFrames must have channel_layout, sample_rate and format set.
+ *
+ * The upper bound on the number of output samples is obtained through
+ * avresample_get_out_samples().
+ *
+ * If the output AVFrame does not have the data pointers allocated the nb_samples
+ * field will be set using avresample_get_out_samples() and av_frame_get_buffer()
+ * is called to allocate the frame.
+ *
+ * The output AVFrame can be NULL or have fewer allocated samples than required.
+ * In this case, any remaining samples not written to the output will be added
+ * to an internal FIFO buffer, to be returned at the next call to this function
+ * or to avresample_convert() or to avresample_read().
+ *
+ * If converting sample rate, there may be data remaining in the internal
+ * resampling delay buffer. avresample_get_delay() tells the number of
+ * remaining samples. To get this data as output, call this function or
+ * avresample_convert() with NULL input.
+ *
+ * At the end of the conversion process, there may be data remaining in the
+ * internal FIFO buffer. avresample_available() tells the number of remaining
+ * samples. To get this data as output, either call this function or
+ * avresample_convert() with NULL input or call avresample_read().
+ *
+ * If the AVAudioResampleContext configuration does not match the output and
+ * input AVFrame settings the conversion does not take place and depending on
+ * which AVFrame is not matching AVERROR_OUTPUT_CHANGED, AVERROR_INPUT_CHANGED
+ * or AVERROR_OUTPUT_CHANGED|AVERROR_INPUT_CHANGED is returned.
+ *
+ * @see avresample_get_out_samples()
+ * @see avresample_available()
+ * @see avresample_convert()
+ * @see avresample_read()
+ * @see avresample_get_delay()
+ *
+ * @param avr             audio resample context
+ * @param output          output AVFrame
+ * @param input           input AVFrame
+ * @return                0 on success, AVERROR on failure or nonmatching
+ *                        configuration.
+ */
+attribute_deprecated
+int avresample_convert_frame(AVAudioResampleContext *avr,
+                             AVFrame *output, AVFrame *input);
+
+/**
+ *
+ * @deprecated use libswresample
+ *
+ * Configure or reconfigure the AVAudioResampleContext using the information
+ * provided by the AVFrames.
+ *
+ * The original resampling context is reset even on failure.
+ * The function calls avresample_close() internally if the context is open.
+ *
+ * @see avresample_open();
+ * @see avresample_close();
+ *
+ * @param avr             audio resample context
+ * @param out             output AVFrame
+ * @param in              input AVFrame
+ * @return                0 on success, AVERROR on failure.
+ */
+attribute_deprecated
+int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame *in);
+
+/**
+ * @}
+ */
+
+#endif /* AVRESAMPLE_AVRESAMPLE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/version.h
new file mode 100644
index 0000000..d5d3ea8
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavresample/version.h
@@ -0,0 +1,50 @@
+/*
+ * 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 AVRESAMPLE_VERSION_H
+#define AVRESAMPLE_VERSION_H
+
+/**
+ * @file
+ * @ingroup lavr
+ * Libavresample version macros.
+ */
+
+#include "libavutil/version.h"
+
+#define LIBAVRESAMPLE_VERSION_MAJOR  4
+#define LIBAVRESAMPLE_VERSION_MINOR  0
+#define LIBAVRESAMPLE_VERSION_MICRO  0
+
+#define LIBAVRESAMPLE_VERSION_INT  AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \
+                                                  LIBAVRESAMPLE_VERSION_MINOR, \
+                                                  LIBAVRESAMPLE_VERSION_MICRO)
+#define LIBAVRESAMPLE_VERSION          AV_VERSION(LIBAVRESAMPLE_VERSION_MAJOR, \
+                                                  LIBAVRESAMPLE_VERSION_MINOR, \
+                                                  LIBAVRESAMPLE_VERSION_MICRO)
+#define LIBAVRESAMPLE_BUILD        LIBAVRESAMPLE_VERSION_INT
+
+#define LIBAVRESAMPLE_IDENT        "Lavr" AV_STRINGIFY(LIBAVRESAMPLE_VERSION)
+
+/**
+ * FF_API_* defines may be placed below to indicate public API that will be
+ * dropped at a future version bump. The defines themselves are not part of
+ * the public API and may change, break or disappear at any time.
+ */
+
+#endif /* AVRESAMPLE_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/adler32.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/adler32.h
new file mode 100644
index 0000000..a1f035b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/adler32.h
@@ -0,0 +1,60 @@
+/*
+ * copyright (c) 2006 Mans Rullgard
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_adler32
+ * Public header for Adler-32 hash function implementation.
+ */
+
+#ifndef AVUTIL_ADLER32_H
+#define AVUTIL_ADLER32_H
+
+#include <stdint.h>
+#include "attributes.h"
+
+/**
+ * @defgroup lavu_adler32 Adler-32
+ * @ingroup lavu_hash
+ * Adler-32 hash function implementation.
+ *
+ * @{
+ */
+
+/**
+ * Calculate the Adler32 checksum of a buffer.
+ *
+ * Passing the return value to a subsequent av_adler32_update() call
+ * allows the checksum of multiple buffers to be calculated as though
+ * they were concatenated.
+ *
+ * @param adler initial checksum value
+ * @param buf   pointer to input buffer
+ * @param len   size of input buffer
+ * @return      updated checksum
+ */
+unsigned long av_adler32_update(unsigned long adler, const uint8_t *buf,
+                                unsigned int len) av_pure;
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_ADLER32_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes.h
new file mode 100644
index 0000000..09efbda
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes.h
@@ -0,0 +1,65 @@
+/*
+ * copyright (c) 2007 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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 AVUTIL_AES_H
+#define AVUTIL_AES_H
+
+#include <stdint.h>
+
+#include "attributes.h"
+#include "version.h"
+
+/**
+ * @defgroup lavu_aes AES
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+extern const int av_aes_size;
+
+struct AVAES;
+
+/**
+ * Allocate an AVAES context.
+ */
+struct AVAES *av_aes_alloc(void);
+
+/**
+ * Initialize an AVAES context.
+ * @param key_bits 128, 192 or 256
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+int av_aes_init(struct AVAES *a, const uint8_t *key, int key_bits, int decrypt);
+
+/**
+ * Encrypt or decrypt a buffer using a previously initialized context.
+ * @param count number of 16 byte blocks
+ * @param dst destination array, can be equal to src
+ * @param src source array, can be equal to dst
+ * @param iv initialization vector for CBC mode, if NULL then ECB will be used
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_aes_crypt(struct AVAES *a, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_AES_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes_ctr.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes_ctr.h
new file mode 100644
index 0000000..e4aae12
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/aes_ctr.h
@@ -0,0 +1,88 @@
+/*
+ * AES-CTR cipher
+ * Copyright (c) 2015 Eran Kornblau <erankor at gmail dot com>
+ *
+ * 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 AVUTIL_AES_CTR_H
+#define AVUTIL_AES_CTR_H
+
+#include <stdint.h>
+
+#include "attributes.h"
+#include "version.h"
+
+#define AES_CTR_KEY_SIZE (16)
+#define AES_CTR_IV_SIZE (8)
+
+struct AVAESCTR;
+
+/**
+ * Allocate an AVAESCTR context.
+ */
+struct AVAESCTR *av_aes_ctr_alloc(void);
+
+/**
+ * Initialize an AVAESCTR context.
+ * @param key encryption key, must have a length of AES_CTR_KEY_SIZE
+ */
+int av_aes_ctr_init(struct AVAESCTR *a, const uint8_t *key);
+
+/**
+ * Release an AVAESCTR context.
+ */
+void av_aes_ctr_free(struct AVAESCTR *a);
+
+/**
+ * Process a buffer using a previously initialized context.
+ * @param dst destination array, can be equal to src
+ * @param src source array, can be equal to dst
+ * @param size the size of src and dst
+ */
+void av_aes_ctr_crypt(struct AVAESCTR *a, uint8_t *dst, const uint8_t *src, int size);
+
+/**
+ * Get the current iv
+ */
+const uint8_t* av_aes_ctr_get_iv(struct AVAESCTR *a);
+
+/**
+ * Generate a random iv
+ */
+void av_aes_ctr_set_random_iv(struct AVAESCTR *a);
+
+/**
+ * Forcefully change the 8-byte iv
+ */
+void av_aes_ctr_set_iv(struct AVAESCTR *a, const uint8_t* iv);
+
+/**
+ * Forcefully change the "full" 16-byte iv, including the counter
+ */
+void av_aes_ctr_set_full_iv(struct AVAESCTR *a, const uint8_t* iv);
+
+/**
+ * Increment the top 64 bit of the iv (performed after each frame)
+ */
+void av_aes_ctr_increment_iv(struct AVAESCTR *a);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_AES_CTR_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/attributes.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/attributes.h
new file mode 100644
index 0000000..ced108a
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/attributes.h
@@ -0,0 +1,167 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Macro definitions for various function/variable attributes
+ */
+
+#ifndef AVUTIL_ATTRIBUTES_H
+#define AVUTIL_ATTRIBUTES_H
+
+#ifdef __GNUC__
+#    define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
+#    define AV_GCC_VERSION_AT_MOST(x,y)  (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y))
+#else
+#    define AV_GCC_VERSION_AT_LEAST(x,y) 0
+#    define AV_GCC_VERSION_AT_MOST(x,y)  0
+#endif
+
+#ifndef av_always_inline
+#if AV_GCC_VERSION_AT_LEAST(3,1)
+#    define av_always_inline __attribute__((always_inline)) inline
+#elif defined(_MSC_VER)
+#    define av_always_inline __forceinline
+#else
+#    define av_always_inline inline
+#endif
+#endif
+
+#ifndef av_extern_inline
+#if defined(__ICL) && __ICL >= 1210 || defined(__GNUC_STDC_INLINE__)
+#    define av_extern_inline extern inline
+#else
+#    define av_extern_inline inline
+#endif
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(3,4)
+#    define av_warn_unused_result __attribute__((warn_unused_result))
+#else
+#    define av_warn_unused_result
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(3,1)
+#    define av_noinline __attribute__((noinline))
+#elif defined(_MSC_VER)
+#    define av_noinline __declspec(noinline)
+#else
+#    define av_noinline
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__)
+#    define av_pure __attribute__((pure))
+#else
+#    define av_pure
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__)
+#    define av_const __attribute__((const))
+#else
+#    define av_const
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(4,3) || defined(__clang__)
+#    define av_cold __attribute__((cold))
+#else
+#    define av_cold
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(4,1) && !defined(__llvm__)
+#    define av_flatten __attribute__((flatten))
+#else
+#    define av_flatten
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(3,1)
+#    define attribute_deprecated __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#    define attribute_deprecated __declspec(deprecated)
+#else
+#    define attribute_deprecated
+#endif
+
+/**
+ * Disable warnings about deprecated features
+ * This is useful for sections of code kept for backward compatibility and
+ * scheduled for removal.
+ */
+#ifndef AV_NOWARN_DEPRECATED
+#if AV_GCC_VERSION_AT_LEAST(4,6)
+#    define AV_NOWARN_DEPRECATED(code) \
+        _Pragma("GCC diagnostic push") \
+        _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
+        code \
+        _Pragma("GCC diagnostic pop")
+#elif defined(_MSC_VER)
+#    define AV_NOWARN_DEPRECATED(code) \
+        __pragma(warning(push)) \
+        __pragma(warning(disable : 4996)) \
+        code; \
+        __pragma(warning(pop))
+#else
+#    define AV_NOWARN_DEPRECATED(code) code
+#endif
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#    define av_unused __attribute__((unused))
+#else
+#    define av_unused
+#endif
+
+/**
+ * Mark a variable as used and prevent the compiler from optimizing it
+ * away.  This is useful for variables accessed only from inline
+ * assembler without the compiler being aware.
+ */
+#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__)
+#    define av_used __attribute__((used))
+#else
+#    define av_used
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(3,3) || defined(__clang__)
+#   define av_alias __attribute__((may_alias))
+#else
+#   define av_alias
+#endif
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER)
+#    define av_uninit(x) x=x
+#else
+#    define av_uninit(x) x
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#    define av_builtin_constant_p __builtin_constant_p
+#    define av_printf_format(fmtpos, attrpos) __attribute__((__format__(__printf__, fmtpos, attrpos)))
+#else
+#    define av_builtin_constant_p(x) 0
+#    define av_printf_format(fmtpos, attrpos)
+#endif
+
+#if AV_GCC_VERSION_AT_LEAST(2,5) || defined(__clang__)
+#    define av_noreturn __attribute__((noreturn))
+#else
+#    define av_noreturn
+#endif
+
+#endif /* AVUTIL_ATTRIBUTES_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/audio_fifo.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/audio_fifo.h
new file mode 100644
index 0000000..d8a9194
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/audio_fifo.h
@@ -0,0 +1,187 @@
+/*
+ * Audio FIFO
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Audio FIFO Buffer
+ */
+
+#ifndef AVUTIL_AUDIO_FIFO_H
+#define AVUTIL_AUDIO_FIFO_H
+
+#include "avutil.h"
+#include "fifo.h"
+#include "samplefmt.h"
+
+/**
+ * @addtogroup lavu_audio
+ * @{
+ *
+ * @defgroup lavu_audiofifo Audio FIFO Buffer
+ * @{
+ */
+
+/**
+ * Context for an Audio FIFO Buffer.
+ *
+ * - Operates at the sample level rather than the byte level.
+ * - Supports multiple channels with either planar or packed sample format.
+ * - Automatic reallocation when writing to a full buffer.
+ */
+typedef struct AVAudioFifo AVAudioFifo;
+
+/**
+ * Free an AVAudioFifo.
+ *
+ * @param af  AVAudioFifo to free
+ */
+void av_audio_fifo_free(AVAudioFifo *af);
+
+/**
+ * Allocate an AVAudioFifo.
+ *
+ * @param sample_fmt  sample format
+ * @param channels    number of channels
+ * @param nb_samples  initial allocation size, in samples
+ * @return            newly allocated AVAudioFifo, or NULL on error
+ */
+AVAudioFifo *av_audio_fifo_alloc(enum AVSampleFormat sample_fmt, int channels,
+                                 int nb_samples);
+
+/**
+ * Reallocate an AVAudioFifo.
+ *
+ * @param af          AVAudioFifo to reallocate
+ * @param nb_samples  new allocation size, in samples
+ * @return            0 if OK, or negative AVERROR code on failure
+ */
+av_warn_unused_result
+int av_audio_fifo_realloc(AVAudioFifo *af, int nb_samples);
+
+/**
+ * Write data to an AVAudioFifo.
+ *
+ * The AVAudioFifo will be reallocated automatically if the available space
+ * is less than nb_samples.
+ *
+ * @see enum AVSampleFormat
+ * The documentation for AVSampleFormat describes the data layout.
+ *
+ * @param af          AVAudioFifo to write to
+ * @param data        audio data plane pointers
+ * @param nb_samples  number of samples to write
+ * @return            number of samples actually written, or negative AVERROR
+ *                    code on failure. If successful, the number of samples
+ *                    actually written will always be nb_samples.
+ */
+int av_audio_fifo_write(AVAudioFifo *af, void **data, int nb_samples);
+
+/**
+ * Peek data from an AVAudioFifo.
+ *
+ * @see enum AVSampleFormat
+ * The documentation for AVSampleFormat describes the data layout.
+ *
+ * @param af          AVAudioFifo to read from
+ * @param data        audio data plane pointers
+ * @param nb_samples  number of samples to peek
+ * @return            number of samples actually peek, or negative AVERROR code
+ *                    on failure. The number of samples actually peek will not
+ *                    be greater than nb_samples, and will only be less than
+ *                    nb_samples if av_audio_fifo_size is less than nb_samples.
+ */
+int av_audio_fifo_peek(AVAudioFifo *af, void **data, int nb_samples);
+
+/**
+ * Peek data from an AVAudioFifo.
+ *
+ * @see enum AVSampleFormat
+ * The documentation for AVSampleFormat describes the data layout.
+ *
+ * @param af          AVAudioFifo to read from
+ * @param data        audio data plane pointers
+ * @param nb_samples  number of samples to peek
+ * @param offset      offset from current read position
+ * @return            number of samples actually peek, or negative AVERROR code
+ *                    on failure. The number of samples actually peek will not
+ *                    be greater than nb_samples, and will only be less than
+ *                    nb_samples if av_audio_fifo_size is less than nb_samples.
+ */
+int av_audio_fifo_peek_at(AVAudioFifo *af, void **data, int nb_samples, int offset);
+
+/**
+ * Read data from an AVAudioFifo.
+ *
+ * @see enum AVSampleFormat
+ * The documentation for AVSampleFormat describes the data layout.
+ *
+ * @param af          AVAudioFifo to read from
+ * @param data        audio data plane pointers
+ * @param nb_samples  number of samples to read
+ * @return            number of samples actually read, or negative AVERROR code
+ *                    on failure. The number of samples actually read will not
+ *                    be greater than nb_samples, and will only be less than
+ *                    nb_samples if av_audio_fifo_size is less than nb_samples.
+ */
+int av_audio_fifo_read(AVAudioFifo *af, void **data, int nb_samples);
+
+/**
+ * Drain data from an AVAudioFifo.
+ *
+ * Removes the data without reading it.
+ *
+ * @param af          AVAudioFifo to drain
+ * @param nb_samples  number of samples to drain
+ * @return            0 if OK, or negative AVERROR code on failure
+ */
+int av_audio_fifo_drain(AVAudioFifo *af, int nb_samples);
+
+/**
+ * Reset the AVAudioFifo buffer.
+ *
+ * This empties all data in the buffer.
+ *
+ * @param af  AVAudioFifo to reset
+ */
+void av_audio_fifo_reset(AVAudioFifo *af);
+
+/**
+ * Get the current number of samples in the AVAudioFifo available for reading.
+ *
+ * @param af  the AVAudioFifo to query
+ * @return    number of samples available for reading
+ */
+int av_audio_fifo_size(AVAudioFifo *af);
+
+/**
+ * Get the current number of samples in the AVAudioFifo available for writing.
+ *
+ * @param af  the AVAudioFifo to query
+ * @return    number of samples available for writing
+ */
+int av_audio_fifo_space(AVAudioFifo *af);
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_AUDIO_FIFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avassert.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avassert.h
new file mode 100644
index 0000000..9abeade
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avassert.h
@@ -0,0 +1,75 @@
+/*
+ * copyright (c) 2010 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * simple assert() macros that are a bit more flexible than ISO C assert().
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#ifndef AVUTIL_AVASSERT_H
+#define AVUTIL_AVASSERT_H
+
+#include <stdlib.h>
+#include "avutil.h"
+#include "log.h"
+
+/**
+ * assert() equivalent, that is always enabled.
+ */
+#define av_assert0(cond) do {                                           \
+    if (!(cond)) {                                                      \
+        av_log(NULL, AV_LOG_PANIC, "Assertion %s failed at %s:%d\n",    \
+               AV_STRINGIFY(cond), __FILE__, __LINE__);                 \
+        abort();                                                        \
+    }                                                                   \
+} while (0)
+
+
+/**
+ * assert() equivalent, that does not lie in speed critical code.
+ * These asserts() thus can be enabled without fearing speed loss.
+ */
+#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 0
+#define av_assert1(cond) av_assert0(cond)
+#else
+#define av_assert1(cond) ((void)0)
+#endif
+
+
+/**
+ * assert() equivalent, that does lie in speed critical code.
+ */
+#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
+#define av_assert2(cond) av_assert0(cond)
+#define av_assert2_fpu() av_assert0_fpu()
+#else
+#define av_assert2(cond) ((void)0)
+#define av_assert2_fpu() ((void)0)
+#endif
+
+/**
+ * Assert that floating point operations can be executed.
+ *
+ * This will av_assert0() that the cpu is not in MMX state on X86
+ */
+void av_assert0_fpu(void);
+
+#endif /* AVUTIL_AVASSERT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avconfig.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avconfig.h
new file mode 100644
index 0000000..c289fbb
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avconfig.h
@@ -0,0 +1,6 @@
+/* Generated by ffmpeg configure */
+#ifndef AVUTIL_AVCONFIG_H
+#define AVUTIL_AVCONFIG_H
+#define AV_HAVE_BIGENDIAN 0
+#define AV_HAVE_FAST_UNALIGNED 1
+#endif /* AVUTIL_AVCONFIG_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avstring.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avstring.h
new file mode 100644
index 0000000..04d2695
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avstring.h
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2007 Mans Rullgard
+ *
+ * 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 AVUTIL_AVSTRING_H
+#define AVUTIL_AVSTRING_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "attributes.h"
+
+/**
+ * @addtogroup lavu_string
+ * @{
+ */
+
+/**
+ * Return non-zero if pfx is a prefix of str. If it is, *ptr is set to
+ * the address of the first character in str after the prefix.
+ *
+ * @param str input string
+ * @param pfx prefix to test
+ * @param ptr updated if the prefix is matched inside str
+ * @return non-zero if the prefix matches, zero otherwise
+ */
+int av_strstart(const char *str, const char *pfx, const char **ptr);
+
+/**
+ * Return non-zero if pfx is a prefix of str independent of case. If
+ * it is, *ptr is set to the address of the first character in str
+ * after the prefix.
+ *
+ * @param str input string
+ * @param pfx prefix to test
+ * @param ptr updated if the prefix is matched inside str
+ * @return non-zero if the prefix matches, zero otherwise
+ */
+int av_stristart(const char *str, const char *pfx, const char **ptr);
+
+/**
+ * Locate the first case-independent occurrence in the string haystack
+ * of the string needle.  A zero-length string needle is considered to
+ * match at the start of haystack.
+ *
+ * This function is a case-insensitive version of the standard strstr().
+ *
+ * @param haystack string to search in
+ * @param needle   string to search for
+ * @return         pointer to the located match within haystack
+ *                 or a null pointer if no match
+ */
+char *av_stristr(const char *haystack, const char *needle);
+
+/**
+ * Locate the first occurrence of the string needle in the string haystack
+ * where not more than hay_length characters are searched. A zero-length
+ * string needle is considered to match at the start of haystack.
+ *
+ * This function is a length-limited version of the standard strstr().
+ *
+ * @param haystack   string to search in
+ * @param needle     string to search for
+ * @param hay_length length of string to search in
+ * @return           pointer to the located match within haystack
+ *                   or a null pointer if no match
+ */
+char *av_strnstr(const char *haystack, const char *needle, size_t hay_length);
+
+/**
+ * Copy the string src to dst, but no more than size - 1 bytes, and
+ * null-terminate dst.
+ *
+ * This function is the same as BSD strlcpy().
+ *
+ * @param dst destination buffer
+ * @param src source string
+ * @param size size of destination buffer
+ * @return the length of src
+ *
+ * @warning since the return value is the length of src, src absolutely
+ * _must_ be a properly 0-terminated string, otherwise this will read beyond
+ * the end of the buffer and possibly crash.
+ */
+size_t av_strlcpy(char *dst, const char *src, size_t size);
+
+/**
+ * Append the string src to the string dst, but to a total length of
+ * no more than size - 1 bytes, and null-terminate dst.
+ *
+ * This function is similar to BSD strlcat(), but differs when
+ * size <= strlen(dst).
+ *
+ * @param dst destination buffer
+ * @param src source string
+ * @param size size of destination buffer
+ * @return the total length of src and dst
+ *
+ * @warning since the return value use the length of src and dst, these
+ * absolutely _must_ be a properly 0-terminated strings, otherwise this
+ * will read beyond the end of the buffer and possibly crash.
+ */
+size_t av_strlcat(char *dst, const char *src, size_t size);
+
+/**
+ * Append output to a string, according to a format. Never write out of
+ * the destination buffer, and always put a terminating 0 within
+ * the buffer.
+ * @param dst destination buffer (string to which the output is
+ *  appended)
+ * @param size total size of the destination buffer
+ * @param fmt printf-compatible format string, specifying how the
+ *  following parameters are used
+ * @return the length of the string that would have been generated
+ *  if enough space had been available
+ */
+size_t av_strlcatf(char *dst, size_t size, const char *fmt, ...) av_printf_format(3, 4);
+
+/**
+ * Get the count of continuous non zero chars starting from the beginning.
+ *
+ * @param len maximum number of characters to check in the string, that
+ *            is the maximum value which is returned by the function
+ */
+static inline size_t av_strnlen(const char *s, size_t len)
+{
+    size_t i;
+    for (i = 0; i < len && s[i]; i++)
+        ;
+    return i;
+}
+
+/**
+ * Print arguments following specified format into a large enough auto
+ * allocated buffer. It is similar to GNU asprintf().
+ * @param fmt printf-compatible format string, specifying how the
+ *            following parameters are used.
+ * @return the allocated string
+ * @note You have to free the string yourself with av_free().
+ */
+char *av_asprintf(const char *fmt, ...) av_printf_format(1, 2);
+
+/**
+ * Convert a number to an av_malloced string.
+ */
+char *av_d2str(double d);
+
+/**
+ * Unescape the given string until a non escaped terminating char,
+ * and return the token corresponding to the unescaped string.
+ *
+ * The normal \ and ' escaping is supported. Leading and trailing
+ * whitespaces are removed, unless they are escaped with '\' or are
+ * enclosed between ''.
+ *
+ * @param buf the buffer to parse, buf will be updated to point to the
+ * terminating char
+ * @param term a 0-terminated list of terminating chars
+ * @return the malloced unescaped string, which must be av_freed by
+ * the user, NULL in case of allocation failure
+ */
+char *av_get_token(const char **buf, const char *term);
+
+/**
+ * Split the string into several tokens which can be accessed by
+ * successive calls to av_strtok().
+ *
+ * A token is defined as a sequence of characters not belonging to the
+ * set specified in delim.
+ *
+ * On the first call to av_strtok(), s should point to the string to
+ * parse, and the value of saveptr is ignored. In subsequent calls, s
+ * should be NULL, and saveptr should be unchanged since the previous
+ * call.
+ *
+ * This function is similar to strtok_r() defined in POSIX.1.
+ *
+ * @param s the string to parse, may be NULL
+ * @param delim 0-terminated list of token delimiters, must be non-NULL
+ * @param saveptr user-provided pointer which points to stored
+ * information necessary for av_strtok() to continue scanning the same
+ * string. saveptr is updated to point to the next character after the
+ * first delimiter found, or to NULL if the string was terminated
+ * @return the found token, or NULL when no token is found
+ */
+char *av_strtok(char *s, const char *delim, char **saveptr);
+
+/**
+ * Locale-independent conversion of ASCII isdigit.
+ */
+static inline av_const int av_isdigit(int c)
+{
+    return c >= '0' && c <= '9';
+}
+
+/**
+ * Locale-independent conversion of ASCII isgraph.
+ */
+static inline av_const int av_isgraph(int c)
+{
+    return c > 32 && c < 127;
+}
+
+/**
+ * Locale-independent conversion of ASCII isspace.
+ */
+static inline av_const int av_isspace(int c)
+{
+    return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' ||
+           c == '\v';
+}
+
+/**
+ * Locale-independent conversion of ASCII characters to uppercase.
+ */
+static inline av_const int av_toupper(int c)
+{
+    if (c >= 'a' && c <= 'z')
+        c ^= 0x20;
+    return c;
+}
+
+/**
+ * Locale-independent conversion of ASCII characters to lowercase.
+ */
+static inline av_const int av_tolower(int c)
+{
+    if (c >= 'A' && c <= 'Z')
+        c ^= 0x20;
+    return c;
+}
+
+/**
+ * Locale-independent conversion of ASCII isxdigit.
+ */
+static inline av_const int av_isxdigit(int c)
+{
+    c = av_tolower(c);
+    return av_isdigit(c) || (c >= 'a' && c <= 'f');
+}
+
+/**
+ * Locale-independent case-insensitive compare.
+ * @note This means only ASCII-range characters are case-insensitive
+ */
+int av_strcasecmp(const char *a, const char *b);
+
+/**
+ * Locale-independent case-insensitive compare.
+ * @note This means only ASCII-range characters are case-insensitive
+ */
+int av_strncasecmp(const char *a, const char *b, size_t n);
+
+/**
+ * Locale-independent strings replace.
+ * @note This means only ASCII-range characters are replace
+ */
+char *av_strireplace(const char *str, const char *from, const char *to);
+
+/**
+ * Thread safe basename.
+ * @param path the path, on DOS both \ and / are considered separators.
+ * @return pointer to the basename substring.
+ */
+const char *av_basename(const char *path);
+
+/**
+ * Thread safe dirname.
+ * @param path the path, on DOS both \ and / are considered separators.
+ * @return the path with the separator replaced by the string terminator or ".".
+ * @note the function may change the input string.
+ */
+const char *av_dirname(char *path);
+
+/**
+ * Match instances of a name in a comma-separated list of names.
+ * List entries are checked from the start to the end of the names list,
+ * the first match ends further processing. If an entry prefixed with '-'
+ * matches, then 0 is returned. The "ALL" list entry is considered to
+ * match all names.
+ *
+ * @param name  Name to look for.
+ * @param names List of names.
+ * @return 1 on match, 0 otherwise.
+ */
+int av_match_name(const char *name, const char *names);
+
+/**
+ * Append path component to the existing path.
+ * Path separator '/' is placed between when needed.
+ * Resulting string have to be freed with av_free().
+ * @param path      base path
+ * @param component component to be appended
+ * @return new path or NULL on error.
+ */
+char *av_append_path_component(const char *path, const char *component);
+
+enum AVEscapeMode {
+    AV_ESCAPE_MODE_AUTO,      ///< Use auto-selected escaping mode.
+    AV_ESCAPE_MODE_BACKSLASH, ///< Use backslash escaping.
+    AV_ESCAPE_MODE_QUOTE,     ///< Use single-quote escaping.
+};
+
+/**
+ * Consider spaces special and escape them even in the middle of the
+ * string.
+ *
+ * This is equivalent to adding the whitespace characters to the special
+ * characters lists, except it is guaranteed to use the exact same list
+ * of whitespace characters as the rest of libavutil.
+ */
+#define AV_ESCAPE_FLAG_WHITESPACE (1 << 0)
+
+/**
+ * Escape only specified special characters.
+ * Without this flag, escape also any characters that may be considered
+ * special by av_get_token(), such as the single quote.
+ */
+#define AV_ESCAPE_FLAG_STRICT (1 << 1)
+
+/**
+ * Escape string in src, and put the escaped string in an allocated
+ * string in *dst, which must be freed with av_free().
+ *
+ * @param dst           pointer where an allocated string is put
+ * @param src           string to escape, must be non-NULL
+ * @param special_chars string containing the special characters which
+ *                      need to be escaped, can be NULL
+ * @param mode          escape mode to employ, see AV_ESCAPE_MODE_* macros.
+ *                      Any unknown value for mode will be considered equivalent to
+ *                      AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
+ *                      notice.
+ * @param flags         flags which control how to escape, see AV_ESCAPE_FLAG_ macros
+ * @return the length of the allocated string, or a negative error code in case of error
+ * @see av_bprint_escape()
+ */
+av_warn_unused_result
+int av_escape(char **dst, const char *src, const char *special_chars,
+              enum AVEscapeMode mode, int flags);
+
+#define AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES          1 ///< accept codepoints over 0x10FFFF
+#define AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS             2 ///< accept non-characters - 0xFFFE and 0xFFFF
+#define AV_UTF8_FLAG_ACCEPT_SURROGATES                 4 ///< accept UTF-16 surrogates codes
+#define AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES 8 ///< exclude control codes not accepted by XML
+
+#define AV_UTF8_FLAG_ACCEPT_ALL \
+    AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES|AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS|AV_UTF8_FLAG_ACCEPT_SURROGATES
+
+/**
+ * Read and decode a single UTF-8 code point (character) from the
+ * buffer in *buf, and update *buf to point to the next byte to
+ * decode.
+ *
+ * In case of an invalid byte sequence, the pointer will be updated to
+ * the next byte after the invalid sequence and the function will
+ * return an error code.
+ *
+ * Depending on the specified flags, the function will also fail in
+ * case the decoded code point does not belong to a valid range.
+ *
+ * @note For speed-relevant code a carefully implemented use of
+ * GET_UTF8() may be preferred.
+ *
+ * @param codep   pointer used to return the parsed code in case of success.
+ *                The value in *codep is set even in case the range check fails.
+ * @param bufp    pointer to the address the first byte of the sequence
+ *                to decode, updated by the function to point to the
+ *                byte next after the decoded sequence
+ * @param buf_end pointer to the end of the buffer, points to the next
+ *                byte past the last in the buffer. This is used to
+ *                avoid buffer overreads (in case of an unfinished
+ *                UTF-8 sequence towards the end of the buffer).
+ * @param flags   a collection of AV_UTF8_FLAG_* flags
+ * @return >= 0 in case a sequence was successfully read, a negative
+ * value in case of invalid sequence
+ */
+av_warn_unused_result
+int av_utf8_decode(int32_t *codep, const uint8_t **bufp, const uint8_t *buf_end,
+                   unsigned int flags);
+
+/**
+ * Check if a name is in a list.
+ * @returns 0 if not found, or the 1 based index where it has been found in the
+ *            list.
+ */
+int av_match_list(const char *name, const char *list, char separator);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_AVSTRING_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avutil.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avutil.h
new file mode 100644
index 0000000..4d63315
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/avutil.h
@@ -0,0 +1,365 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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 AVUTIL_AVUTIL_H
+#define AVUTIL_AVUTIL_H
+
+/**
+ * @file
+ * @ingroup lavu
+ * Convenience header that includes @ref lavu "libavutil"'s core.
+ */
+
+/**
+ * @mainpage
+ *
+ * @section ffmpeg_intro Introduction
+ *
+ * This document describes the usage of the different libraries
+ * provided by FFmpeg.
+ *
+ * @li @ref libavc "libavcodec" encoding/decoding library
+ * @li @ref lavfi "libavfilter" graph-based frame editing library
+ * @li @ref libavf "libavformat" I/O and muxing/demuxing library
+ * @li @ref lavd "libavdevice" special devices muxing/demuxing library
+ * @li @ref lavu "libavutil" common utility library
+ * @li @ref lswr "libswresample" audio resampling, format conversion and mixing
+ * @li @ref lpp  "libpostproc" post processing library
+ * @li @ref libsws "libswscale" color conversion and scaling library
+ *
+ * @section ffmpeg_versioning Versioning and compatibility
+ *
+ * Each of the FFmpeg libraries contains a version.h header, which defines a
+ * major, minor and micro version number with the
+ * <em>LIBRARYNAME_VERSION_{MAJOR,MINOR,MICRO}</em> macros. The major version
+ * number is incremented with backward incompatible changes - e.g. removing
+ * parts of the public API, reordering public struct members, etc. The minor
+ * version number is incremented for backward compatible API changes or major
+ * new features - e.g. adding a new public function or a new decoder. The micro
+ * version number is incremented for smaller changes that a calling program
+ * might still want to check for - e.g. changing behavior in a previously
+ * unspecified situation.
+ *
+ * FFmpeg guarantees backward API and ABI compatibility for each library as long
+ * as its major version number is unchanged. This means that no public symbols
+ * will be removed or renamed. Types and names of the public struct members and
+ * values of public macros and enums will remain the same (unless they were
+ * explicitly declared as not part of the public API). Documented behavior will
+ * not change.
+ *
+ * In other words, any correct program that works with a given FFmpeg snapshot
+ * should work just as well without any changes with any later snapshot with the
+ * same major versions. This applies to both rebuilding the program against new
+ * FFmpeg versions or to replacing the dynamic FFmpeg libraries that a program
+ * links against.
+ *
+ * However, new public symbols may be added and new members may be appended to
+ * public structs whose size is not part of public ABI (most public structs in
+ * FFmpeg). New macros and enum values may be added. Behavior in undocumented
+ * situations may change slightly (and be documented). All those are accompanied
+ * by an entry in doc/APIchanges and incrementing either the minor or micro
+ * version number.
+ */
+
+/**
+ * @defgroup lavu libavutil
+ * Common code shared across all FFmpeg libraries.
+ *
+ * @note
+ * libavutil is designed to be modular. In most cases, in order to use the
+ * functions provided by one component of libavutil you must explicitly include
+ * the specific header containing that feature. If you are only using
+ * media-related components, you could simply include libavutil/avutil.h, which
+ * brings in most of the "core" components.
+ *
+ * @{
+ *
+ * @defgroup lavu_crypto Crypto and Hashing
+ *
+ * @{
+ * @}
+ *
+ * @defgroup lavu_math Mathematics
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_string String Manipulation
+ *
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_mem Memory Management
+ *
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_data Data Structures
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_video Video related
+ *
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_audio Audio related
+ *
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_error Error Codes
+ *
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_log Logging Facility
+ *
+ * @{
+ *
+ * @}
+ *
+ * @defgroup lavu_misc Other
+ *
+ * @{
+ *
+ * @defgroup preproc_misc Preprocessor String Macros
+ *
+ * @{
+ *
+ * @}
+ *
+ * @defgroup version_utils Library Version Macros
+ *
+ * @{
+ *
+ * @}
+ */
+
+
+/**
+ * @addtogroup lavu_ver
+ * @{
+ */
+
+/**
+ * Return the LIBAVUTIL_VERSION_INT constant.
+ */
+unsigned avutil_version(void);
+
+/**
+ * Return an informative version string. This usually is the actual release
+ * version number or a git commit description. This string has no fixed format
+ * and can change any time. It should never be parsed by code.
+ */
+const char *av_version_info(void);
+
+/**
+ * Return the libavutil build-time configuration.
+ */
+const char *avutil_configuration(void);
+
+/**
+ * Return the libavutil license.
+ */
+const char *avutil_license(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup lavu_media Media Type
+ * @brief Media Type
+ */
+
+enum AVMediaType {
+    AVMEDIA_TYPE_UNKNOWN = -1,  ///< Usually treated as AVMEDIA_TYPE_DATA
+    AVMEDIA_TYPE_VIDEO,
+    AVMEDIA_TYPE_AUDIO,
+    AVMEDIA_TYPE_DATA,          ///< Opaque data information usually continuous
+    AVMEDIA_TYPE_SUBTITLE,
+    AVMEDIA_TYPE_ATTACHMENT,    ///< Opaque data information usually sparse
+    AVMEDIA_TYPE_NB
+};
+
+/**
+ * Return a string describing the media_type enum, NULL if media_type
+ * is unknown.
+ */
+const char *av_get_media_type_string(enum AVMediaType media_type);
+
+/**
+ * @defgroup lavu_const Constants
+ * @{
+ *
+ * @defgroup lavu_enc Encoding specific
+ *
+ * @note those definition should move to avcodec
+ * @{
+ */
+
+#define FF_LAMBDA_SHIFT 7
+#define FF_LAMBDA_SCALE (1<<FF_LAMBDA_SHIFT)
+#define FF_QP2LAMBDA 118 ///< factor to convert from H.263 QP to lambda
+#define FF_LAMBDA_MAX (256*128-1)
+
+#define FF_QUALITY_SCALE FF_LAMBDA_SCALE //FIXME maybe remove
+
+/**
+ * @}
+ * @defgroup lavu_time Timestamp specific
+ *
+ * FFmpeg internal timebase and timestamp definitions
+ *
+ * @{
+ */
+
+/**
+ * @brief Undefined timestamp value
+ *
+ * Usually reported by demuxer that work on containers that do not provide
+ * either pts or dts.
+ */
+
+#define AV_NOPTS_VALUE          ((int64_t)UINT64_C(0x8000000000000000))
+
+/**
+ * Internal time base represented as integer
+ */
+
+#define AV_TIME_BASE            1000000
+
+/**
+ * Internal time base represented as fractional value
+ */
+
+#define AV_TIME_BASE_Q          (AVRational){1, AV_TIME_BASE}
+
+/**
+ * @}
+ * @}
+ * @defgroup lavu_picture Image related
+ *
+ * AVPicture types, pixel formats and basic image planes manipulation.
+ *
+ * @{
+ */
+
+enum AVPictureType {
+    AV_PICTURE_TYPE_NONE = 0, ///< Undefined
+    AV_PICTURE_TYPE_I,     ///< Intra
+    AV_PICTURE_TYPE_P,     ///< Predicted
+    AV_PICTURE_TYPE_B,     ///< Bi-dir predicted
+    AV_PICTURE_TYPE_S,     ///< S(GMC)-VOP MPEG-4
+    AV_PICTURE_TYPE_SI,    ///< Switching Intra
+    AV_PICTURE_TYPE_SP,    ///< Switching Predicted
+    AV_PICTURE_TYPE_BI,    ///< BI type
+};
+
+/**
+ * Return a single letter to describe the given picture type
+ * pict_type.
+ *
+ * @param[in] pict_type the picture type @return a single character
+ * representing the picture type, '?' if pict_type is unknown
+ */
+char av_get_picture_type_char(enum AVPictureType pict_type);
+
+/**
+ * @}
+ */
+
+#include "common.h"
+#include "error.h"
+#include "rational.h"
+#include "version.h"
+#include "macros.h"
+#include "mathematics.h"
+#include "log.h"
+#include "pixfmt.h"
+
+/**
+ * Return x default pointer in case p is NULL.
+ */
+static inline void *av_x_if_null(const void *p, const void *x)
+{
+    return (void *)(intptr_t)(p ? p : x);
+}
+
+/**
+ * Compute the length of an integer list.
+ *
+ * @param elsize  size in bytes of each list element (only 1, 2, 4 or 8)
+ * @param term    list terminator (usually 0 or -1)
+ * @param list    pointer to the list
+ * @return  length of the list, in elements, not counting the terminator
+ */
+unsigned av_int_list_length_for_size(unsigned elsize,
+                                     const void *list, uint64_t term) av_pure;
+
+/**
+ * Compute the length of an integer list.
+ *
+ * @param term  list terminator (usually 0 or -1)
+ * @param list  pointer to the list
+ * @return  length of the list, in elements, not counting the terminator
+ */
+#define av_int_list_length(list, term) \
+    av_int_list_length_for_size(sizeof(*(list)), list, term)
+
+/**
+ * Open a file using a UTF-8 filename.
+ * The API of this function matches POSIX fopen(), errors are returned through
+ * errno.
+ */
+FILE *av_fopen_utf8(const char *path, const char *mode);
+
+/**
+ * Return the fractional representation of the internal time base.
+ */
+AVRational av_get_time_base_q(void);
+
+#define AV_FOURCC_MAX_STRING_SIZE 32
+
+#define av_fourcc2str(fourcc) av_fourcc_make_string((char[AV_FOURCC_MAX_STRING_SIZE]){0}, fourcc)
+
+/**
+ * Fill the provided buffer with a string containing a FourCC (four-character
+ * code) representation.
+ *
+ * @param buf    a buffer with size in bytes of at least AV_FOURCC_MAX_STRING_SIZE
+ * @param fourcc the fourcc to represent
+ * @return the buffer in input
+ */
+char *av_fourcc_make_string(char *buf, uint32_t fourcc);
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_AVUTIL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/base64.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/base64.h
new file mode 100644
index 0000000..2954c12
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/base64.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
+ *
+ * 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 AVUTIL_BASE64_H
+#define AVUTIL_BASE64_H
+
+#include <stdint.h>
+
+/**
+ * @defgroup lavu_base64 Base64
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+/**
+ * Decode a base64-encoded string.
+ *
+ * @param out      buffer for decoded data
+ * @param in       null-terminated input string
+ * @param out_size size in bytes of the out buffer, must be at
+ *                 least 3/4 of the length of in, that is AV_BASE64_DECODE_SIZE(strlen(in))
+ * @return         number of bytes written, or a negative value in case of
+ *                 invalid input
+ */
+int av_base64_decode(uint8_t *out, const char *in, int out_size);
+
+/**
+ * Calculate the output size in bytes needed to decode a base64 string
+ * with length x to a data buffer.
+ */
+#define AV_BASE64_DECODE_SIZE(x) ((x) * 3LL / 4)
+
+/**
+ * Encode data to base64 and null-terminate.
+ *
+ * @param out      buffer for encoded data
+ * @param out_size size in bytes of the out buffer (including the
+ *                 null terminator), must be at least AV_BASE64_SIZE(in_size)
+ * @param in       input buffer containing the data to encode
+ * @param in_size  size in bytes of the in buffer
+ * @return         out or NULL in case of error
+ */
+char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size);
+
+/**
+ * Calculate the output size needed to base64-encode x bytes to a
+ * null-terminated string.
+ */
+#define AV_BASE64_SIZE(x)  (((x)+2) / 3 * 4 + 1)
+
+ /**
+  * @}
+  */
+
+#endif /* AVUTIL_BASE64_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/blowfish.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/blowfish.h
new file mode 100644
index 0000000..9e289a4
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/blowfish.h
@@ -0,0 +1,82 @@
+/*
+ * Blowfish algorithm
+ * Copyright (c) 2012 Samuel Pitoiset
+ *
+ * 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 AVUTIL_BLOWFISH_H
+#define AVUTIL_BLOWFISH_H
+
+#include <stdint.h>
+
+/**
+ * @defgroup lavu_blowfish Blowfish
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+#define AV_BF_ROUNDS 16
+
+typedef struct AVBlowfish {
+    uint32_t p[AV_BF_ROUNDS + 2];
+    uint32_t s[4][256];
+} AVBlowfish;
+
+/**
+ * Allocate an AVBlowfish context.
+ */
+AVBlowfish *av_blowfish_alloc(void);
+
+/**
+ * Initialize an AVBlowfish context.
+ *
+ * @param ctx an AVBlowfish context
+ * @param key a key
+ * @param key_len length of the key
+ */
+void av_blowfish_init(struct AVBlowfish *ctx, const uint8_t *key, int key_len);
+
+/**
+ * Encrypt or decrypt a buffer using a previously initialized context.
+ *
+ * @param ctx an AVBlowfish context
+ * @param xl left four bytes halves of input to be encrypted
+ * @param xr right four bytes halves of input to be encrypted
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_blowfish_crypt_ecb(struct AVBlowfish *ctx, uint32_t *xl, uint32_t *xr,
+                           int decrypt);
+
+/**
+ * Encrypt or decrypt a buffer using a previously initialized context.
+ *
+ * @param ctx an AVBlowfish context
+ * @param dst destination array, can be equal to src
+ * @param src source array, can be equal to dst
+ * @param count number of 8 byte blocks
+ * @param iv initialization vector for CBC mode, if NULL ECB will be used
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_blowfish_crypt(struct AVBlowfish *ctx, uint8_t *dst, const uint8_t *src,
+                       int count, uint8_t *iv, int decrypt);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_BLOWFISH_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bprint.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bprint.h
new file mode 100644
index 0000000..c09b1ac
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bprint.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2012 Nicolas George
+ *
+ * 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 AVUTIL_BPRINT_H
+#define AVUTIL_BPRINT_H
+
+#include <stdarg.h>
+
+#include "attributes.h"
+#include "avstring.h"
+
+/**
+ * Define a structure with extra padding to a fixed size
+ * This helps ensuring binary compatibility with future versions.
+ */
+
+#define FF_PAD_STRUCTURE(name, size, ...) \
+struct ff_pad_helper_##name { __VA_ARGS__ }; \
+typedef struct name { \
+    __VA_ARGS__ \
+    char reserved_padding[size - sizeof(struct ff_pad_helper_##name)]; \
+} name;
+
+/**
+ * Buffer to print data progressively
+ *
+ * The string buffer grows as necessary and is always 0-terminated.
+ * The content of the string is never accessed, and thus is
+ * encoding-agnostic and can even hold binary data.
+ *
+ * Small buffers are kept in the structure itself, and thus require no
+ * memory allocation at all (unless the contents of the buffer is needed
+ * after the structure goes out of scope). This is almost as lightweight as
+ * declaring a local "char buf[512]".
+ *
+ * The length of the string can go beyond the allocated size: the buffer is
+ * then truncated, but the functions still keep account of the actual total
+ * length.
+ *
+ * In other words, buf->len can be greater than buf->size and records the
+ * total length of what would have been to the buffer if there had been
+ * enough memory.
+ *
+ * Append operations do not need to be tested for failure: if a memory
+ * allocation fails, data stop being appended to the buffer, but the length
+ * is still updated. This situation can be tested with
+ * av_bprint_is_complete().
+ *
+ * The size_max field determines several possible behaviours:
+ *
+ * size_max = -1 (= UINT_MAX) or any large value will let the buffer be
+ * reallocated as necessary, with an amortized linear cost.
+ *
+ * size_max = 0 prevents writing anything to the buffer: only the total
+ * length is computed. The write operations can then possibly be repeated in
+ * a buffer with exactly the necessary size
+ * (using size_init = size_max = len + 1).
+ *
+ * size_max = 1 is automatically replaced by the exact size available in the
+ * structure itself, thus ensuring no dynamic memory allocation. The
+ * internal buffer is large enough to hold a reasonable paragraph of text,
+ * such as the current paragraph.
+ */
+
+FF_PAD_STRUCTURE(AVBPrint, 1024,
+    char *str;         /**< string so far */
+    unsigned len;      /**< length so far */
+    unsigned size;     /**< allocated memory */
+    unsigned size_max; /**< maximum allocated memory */
+    char reserved_internal_buffer[1];
+)
+
+/**
+ * Convenience macros for special values for av_bprint_init() size_max
+ * parameter.
+ */
+#define AV_BPRINT_SIZE_UNLIMITED  ((unsigned)-1)
+#define AV_BPRINT_SIZE_AUTOMATIC  1
+#define AV_BPRINT_SIZE_COUNT_ONLY 0
+
+/**
+ * Init a print buffer.
+ *
+ * @param buf        buffer to init
+ * @param size_init  initial size (including the final 0)
+ * @param size_max   maximum size;
+ *                   0 means do not write anything, just count the length;
+ *                   1 is replaced by the maximum value for automatic storage;
+ *                   any large value means that the internal buffer will be
+ *                   reallocated as needed up to that limit; -1 is converted to
+ *                   UINT_MAX, the largest limit possible.
+ *                   Check also AV_BPRINT_SIZE_* macros.
+ */
+void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max);
+
+/**
+ * Init a print buffer using a pre-existing buffer.
+ *
+ * The buffer will not be reallocated.
+ *
+ * @param buf     buffer structure to init
+ * @param buffer  byte buffer to use for the string data
+ * @param size    size of buffer
+ */
+void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size);
+
+/**
+ * Append a formatted string to a print buffer.
+ */
+void av_bprintf(AVBPrint *buf, const char *fmt, ...) av_printf_format(2, 3);
+
+/**
+ * Append a formatted string to a print buffer.
+ */
+void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg);
+
+/**
+ * Append char c n times to a print buffer.
+ */
+void av_bprint_chars(AVBPrint *buf, char c, unsigned n);
+
+/**
+ * Append data to a print buffer.
+ *
+ * param buf  bprint buffer to use
+ * param data pointer to data
+ * param size size of data
+ */
+void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size);
+
+struct tm;
+/**
+ * Append a formatted date and time to a print buffer.
+ *
+ * param buf  bprint buffer to use
+ * param fmt  date and time format string, see strftime()
+ * param tm   broken-down time structure to translate
+ *
+ * @note due to poor design of the standard strftime function, it may
+ * produce poor results if the format string expands to a very long text and
+ * the bprint buffer is near the limit stated by the size_max option.
+ */
+void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm);
+
+/**
+ * Allocate bytes in the buffer for external use.
+ *
+ * @param[in]  buf          buffer structure
+ * @param[in]  size         required size
+ * @param[out] mem          pointer to the memory area
+ * @param[out] actual_size  size of the memory area after allocation;
+ *                          can be larger or smaller than size
+ */
+void av_bprint_get_buffer(AVBPrint *buf, unsigned size,
+                          unsigned char **mem, unsigned *actual_size);
+
+/**
+ * Reset the string to "" but keep internal allocated data.
+ */
+void av_bprint_clear(AVBPrint *buf);
+
+/**
+ * Test if the print buffer is complete (not truncated).
+ *
+ * It may have been truncated due to a memory allocation failure
+ * or the size_max limit (compare size and size_max if necessary).
+ */
+static inline int av_bprint_is_complete(const AVBPrint *buf)
+{
+    return buf->len < buf->size;
+}
+
+/**
+ * Finalize a print buffer.
+ *
+ * The print buffer can no longer be used afterwards,
+ * but the len and size fields are still valid.
+ *
+ * @arg[out] ret_str  if not NULL, used to return a permanent copy of the
+ *                    buffer contents, or NULL if memory allocation fails;
+ *                    if NULL, the buffer is discarded and freed
+ * @return  0 for success or error code (probably AVERROR(ENOMEM))
+ */
+int av_bprint_finalize(AVBPrint *buf, char **ret_str);
+
+/**
+ * Escape the content in src and append it to dstbuf.
+ *
+ * @param dstbuf        already inited destination bprint buffer
+ * @param src           string containing the text to escape
+ * @param special_chars string containing the special characters which
+ *                      need to be escaped, can be NULL
+ * @param mode          escape mode to employ, see AV_ESCAPE_MODE_* macros.
+ *                      Any unknown value for mode will be considered equivalent to
+ *                      AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
+ *                      notice.
+ * @param flags         flags which control how to escape, see AV_ESCAPE_FLAG_* macros
+ */
+void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
+                      enum AVEscapeMode mode, int flags);
+
+#endif /* AVUTIL_BPRINT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bswap.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bswap.h
new file mode 100644
index 0000000..91cb795
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/bswap.h
@@ -0,0 +1,109 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * byte swapping routines
+ */
+
+#ifndef AVUTIL_BSWAP_H
+#define AVUTIL_BSWAP_H
+
+#include <stdint.h>
+#include "libavutil/avconfig.h"
+#include "attributes.h"
+
+#ifdef HAVE_AV_CONFIG_H
+
+#include "config.h"
+
+#if   ARCH_AARCH64
+#   include "aarch64/bswap.h"
+#elif ARCH_ARM
+#   include "arm/bswap.h"
+#elif ARCH_AVR32
+#   include "avr32/bswap.h"
+#elif ARCH_SH4
+#   include "sh4/bswap.h"
+#elif ARCH_X86
+#   include "x86/bswap.h"
+#endif
+
+#endif /* HAVE_AV_CONFIG_H */
+
+#define AV_BSWAP16C(x) (((x) << 8 & 0xff00)  | ((x) >> 8 & 0x00ff))
+#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16))
+#define AV_BSWAP64C(x) (AV_BSWAP32C(x) << 32 | AV_BSWAP32C((x) >> 32))
+
+#define AV_BSWAPC(s, x) AV_BSWAP##s##C(x)
+
+#ifndef av_bswap16
+static av_always_inline av_const uint16_t av_bswap16(uint16_t x)
+{
+    x= (x>>8) | (x<<8);
+    return x;
+}
+#endif
+
+#ifndef av_bswap32
+static av_always_inline av_const uint32_t av_bswap32(uint32_t x)
+{
+    return AV_BSWAP32C(x);
+}
+#endif
+
+#ifndef av_bswap64
+static inline uint64_t av_const av_bswap64(uint64_t x)
+{
+    return (uint64_t)av_bswap32(x) << 32 | av_bswap32(x >> 32);
+}
+#endif
+
+// be2ne ... big-endian to native-endian
+// le2ne ... little-endian to native-endian
+
+#if AV_HAVE_BIGENDIAN
+#define av_be2ne16(x) (x)
+#define av_be2ne32(x) (x)
+#define av_be2ne64(x) (x)
+#define av_le2ne16(x) av_bswap16(x)
+#define av_le2ne32(x) av_bswap32(x)
+#define av_le2ne64(x) av_bswap64(x)
+#define AV_BE2NEC(s, x) (x)
+#define AV_LE2NEC(s, x) AV_BSWAPC(s, x)
+#else
+#define av_be2ne16(x) av_bswap16(x)
+#define av_be2ne32(x) av_bswap32(x)
+#define av_be2ne64(x) av_bswap64(x)
+#define av_le2ne16(x) (x)
+#define av_le2ne32(x) (x)
+#define av_le2ne64(x) (x)
+#define AV_BE2NEC(s, x) AV_BSWAPC(s, x)
+#define AV_LE2NEC(s, x) (x)
+#endif
+
+#define AV_BE2NE16C(x) AV_BE2NEC(16, x)
+#define AV_BE2NE32C(x) AV_BE2NEC(32, x)
+#define AV_BE2NE64C(x) AV_BE2NEC(64, x)
+#define AV_LE2NE16C(x) AV_LE2NEC(16, x)
+#define AV_LE2NE32C(x) AV_LE2NEC(32, x)
+#define AV_LE2NE64C(x) AV_LE2NEC(64, x)
+
+#endif /* AVUTIL_BSWAP_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/buffer.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/buffer.h
new file mode 100644
index 0000000..73b6bd0
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/buffer.h
@@ -0,0 +1,291 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_buffer
+ * refcounted data buffer API
+ */
+
+#ifndef AVUTIL_BUFFER_H
+#define AVUTIL_BUFFER_H
+
+#include <stdint.h>
+
+/**
+ * @defgroup lavu_buffer AVBuffer
+ * @ingroup lavu_data
+ *
+ * @{
+ * AVBuffer is an API for reference-counted data buffers.
+ *
+ * There are two core objects in this API -- AVBuffer and AVBufferRef. AVBuffer
+ * represents the data buffer itself; it is opaque and not meant to be accessed
+ * by the caller directly, but only through AVBufferRef. However, the caller may
+ * e.g. compare two AVBuffer pointers to check whether two different references
+ * are describing the same data buffer. AVBufferRef represents a single
+ * reference to an AVBuffer and it is the object that may be manipulated by the
+ * caller directly.
+ *
+ * There are two functions provided for creating a new AVBuffer with a single
+ * reference -- av_buffer_alloc() to just allocate a new buffer, and
+ * av_buffer_create() to wrap an existing array in an AVBuffer. From an existing
+ * reference, additional references may be created with av_buffer_ref().
+ * Use av_buffer_unref() to free a reference (this will automatically free the
+ * data once all the references are freed).
+ *
+ * The convention throughout this API and the rest of FFmpeg is such that the
+ * buffer is considered writable if there exists only one reference to it (and
+ * it has not been marked as read-only). The av_buffer_is_writable() function is
+ * provided to check whether this is true and av_buffer_make_writable() will
+ * automatically create a new writable buffer when necessary.
+ * Of course nothing prevents the calling code from violating this convention,
+ * however that is safe only when all the existing references are under its
+ * control.
+ *
+ * @note Referencing and unreferencing the buffers is thread-safe and thus
+ * may be done from multiple threads simultaneously without any need for
+ * additional locking.
+ *
+ * @note Two different references to the same buffer can point to different
+ * parts of the buffer (i.e. their AVBufferRef.data will not be equal).
+ */
+
+/**
+ * A reference counted buffer type. It is opaque and is meant to be used through
+ * references (AVBufferRef).
+ */
+typedef struct AVBuffer AVBuffer;
+
+/**
+ * A reference to a data buffer.
+ *
+ * The size of this struct is not a part of the public ABI and it is not meant
+ * to be allocated directly.
+ */
+typedef struct AVBufferRef {
+    AVBuffer *buffer;
+
+    /**
+     * The data buffer. It is considered writable if and only if
+     * this is the only reference to the buffer, in which case
+     * av_buffer_is_writable() returns 1.
+     */
+    uint8_t *data;
+    /**
+     * Size of data in bytes.
+     */
+    int      size;
+} AVBufferRef;
+
+/**
+ * Allocate an AVBuffer of the given size using av_malloc().
+ *
+ * @return an AVBufferRef of given size or NULL when out of memory
+ */
+AVBufferRef *av_buffer_alloc(int size);
+
+/**
+ * Same as av_buffer_alloc(), except the returned buffer will be initialized
+ * to zero.
+ */
+AVBufferRef *av_buffer_allocz(int size);
+
+/**
+ * Always treat the buffer as read-only, even when it has only one
+ * reference.
+ */
+#define AV_BUFFER_FLAG_READONLY (1 << 0)
+
+/**
+ * Create an AVBuffer from an existing array.
+ *
+ * If this function is successful, data is owned by the AVBuffer. The caller may
+ * only access data through the returned AVBufferRef and references derived from
+ * it.
+ * If this function fails, data is left untouched.
+ * @param data   data array
+ * @param size   size of data in bytes
+ * @param free   a callback for freeing this buffer's data
+ * @param opaque parameter to be got for processing or passed to free
+ * @param flags  a combination of AV_BUFFER_FLAG_*
+ *
+ * @return an AVBufferRef referring to data on success, NULL on failure.
+ */
+AVBufferRef *av_buffer_create(uint8_t *data, int size,
+                              void (*free)(void *opaque, uint8_t *data),
+                              void *opaque, int flags);
+
+/**
+ * Default free callback, which calls av_free() on the buffer data.
+ * This function is meant to be passed to av_buffer_create(), not called
+ * directly.
+ */
+void av_buffer_default_free(void *opaque, uint8_t *data);
+
+/**
+ * Create a new reference to an AVBuffer.
+ *
+ * @return a new AVBufferRef referring to the same AVBuffer as buf or NULL on
+ * failure.
+ */
+AVBufferRef *av_buffer_ref(AVBufferRef *buf);
+
+/**
+ * Free a given reference and automatically free the buffer if there are no more
+ * references to it.
+ *
+ * @param buf the reference to be freed. The pointer is set to NULL on return.
+ */
+void av_buffer_unref(AVBufferRef **buf);
+
+/**
+ * @return 1 if the caller may write to the data referred to by buf (which is
+ * true if and only if buf is the only reference to the underlying AVBuffer).
+ * Return 0 otherwise.
+ * A positive answer is valid until av_buffer_ref() is called on buf.
+ */
+int av_buffer_is_writable(const AVBufferRef *buf);
+
+/**
+ * @return the opaque parameter set by av_buffer_create.
+ */
+void *av_buffer_get_opaque(const AVBufferRef *buf);
+
+int av_buffer_get_ref_count(const AVBufferRef *buf);
+
+/**
+ * Create a writable reference from a given buffer reference, avoiding data copy
+ * if possible.
+ *
+ * @param buf buffer reference to make writable. On success, buf is either left
+ *            untouched, or it is unreferenced and a new writable AVBufferRef is
+ *            written in its place. On failure, buf is left untouched.
+ * @return 0 on success, a negative AVERROR on failure.
+ */
+int av_buffer_make_writable(AVBufferRef **buf);
+
+/**
+ * Reallocate a given buffer.
+ *
+ * @param buf  a buffer reference to reallocate. On success, buf will be
+ *             unreferenced and a new reference with the required size will be
+ *             written in its place. On failure buf will be left untouched. *buf
+ *             may be NULL, then a new buffer is allocated.
+ * @param size required new buffer size.
+ * @return 0 on success, a negative AVERROR on failure.
+ *
+ * @note the buffer is actually reallocated with av_realloc() only if it was
+ * initially allocated through av_buffer_realloc(NULL) and there is only one
+ * reference to it (i.e. the one passed to this function). In all other cases
+ * a new buffer is allocated and the data is copied.
+ */
+int av_buffer_realloc(AVBufferRef **buf, int size);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lavu_bufferpool AVBufferPool
+ * @ingroup lavu_data
+ *
+ * @{
+ * AVBufferPool is an API for a lock-free thread-safe pool of AVBuffers.
+ *
+ * Frequently allocating and freeing large buffers may be slow. AVBufferPool is
+ * meant to solve this in cases when the caller needs a set of buffers of the
+ * same size (the most obvious use case being buffers for raw video or audio
+ * frames).
+ *
+ * At the beginning, the user must call av_buffer_pool_init() to create the
+ * buffer pool. Then whenever a buffer is needed, call av_buffer_pool_get() to
+ * get a reference to a new buffer, similar to av_buffer_alloc(). This new
+ * reference works in all aspects the same way as the one created by
+ * av_buffer_alloc(). However, when the last reference to this buffer is
+ * unreferenced, it is returned to the pool instead of being freed and will be
+ * reused for subsequent av_buffer_pool_get() calls.
+ *
+ * When the caller is done with the pool and no longer needs to allocate any new
+ * buffers, av_buffer_pool_uninit() must be called to mark the pool as freeable.
+ * Once all the buffers are released, it will automatically be freed.
+ *
+ * Allocating and releasing buffers with this API is thread-safe as long as
+ * either the default alloc callback is used, or the user-supplied one is
+ * thread-safe.
+ */
+
+/**
+ * The buffer pool. This structure is opaque and not meant to be accessed
+ * directly. It is allocated with av_buffer_pool_init() and freed with
+ * av_buffer_pool_uninit().
+ */
+typedef struct AVBufferPool AVBufferPool;
+
+/**
+ * Allocate and initialize a buffer pool.
+ *
+ * @param size size of each buffer in this pool
+ * @param alloc a function that will be used to allocate new buffers when the
+ * pool is empty. May be NULL, then the default allocator will be used
+ * (av_buffer_alloc()).
+ * @return newly created buffer pool on success, NULL on error.
+ */
+AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size));
+
+/**
+ * Allocate and initialize a buffer pool with a more complex allocator.
+ *
+ * @param size size of each buffer in this pool
+ * @param opaque arbitrary user data used by the allocator
+ * @param alloc a function that will be used to allocate new buffers when the
+ *              pool is empty.
+ * @param pool_free a function that will be called immediately before the pool
+ *                  is freed. I.e. after av_buffer_pool_uninit() is called
+ *                  by the caller and all the frames are returned to the pool
+ *                  and freed. It is intended to uninitialize the user opaque
+ *                  data.
+ * @return newly created buffer pool on success, NULL on error.
+ */
+AVBufferPool *av_buffer_pool_init2(int size, void *opaque,
+                                   AVBufferRef* (*alloc)(void *opaque, int size),
+                                   void (*pool_free)(void *opaque));
+
+/**
+ * Mark the pool as being available for freeing. It will actually be freed only
+ * once all the allocated buffers associated with the pool are released. Thus it
+ * is safe to call this function while some of the allocated buffers are still
+ * in use.
+ *
+ * @param pool pointer to the pool to be freed. It will be set to NULL.
+ */
+void av_buffer_pool_uninit(AVBufferPool **pool);
+
+/**
+ * Allocate a new AVBuffer, reusing an old buffer from the pool when available.
+ * This function may be called simultaneously from multiple threads.
+ *
+ * @return a reference to the new buffer on success, NULL on error.
+ */
+AVBufferRef *av_buffer_pool_get(AVBufferPool *pool);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_BUFFER_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/camellia.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/camellia.h
new file mode 100644
index 0000000..e674c9b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/camellia.h
@@ -0,0 +1,70 @@
+/*
+ * An implementation of the CAMELLIA algorithm as mentioned in RFC3713
+ * Copyright (c) 2014 Supraja Meedinti
+ *
+ * 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 AVUTIL_CAMELLIA_H
+#define AVUTIL_CAMELLIA_H
+
+#include <stdint.h>
+
+
+/**
+  * @file
+  * @brief Public header for libavutil CAMELLIA algorithm
+  * @defgroup lavu_camellia CAMELLIA
+  * @ingroup lavu_crypto
+  * @{
+  */
+
+extern const int av_camellia_size;
+
+struct AVCAMELLIA;
+
+/**
+  * Allocate an AVCAMELLIA context
+  * To free the struct: av_free(ptr)
+  */
+struct AVCAMELLIA *av_camellia_alloc(void);
+
+/**
+  * Initialize an AVCAMELLIA context.
+  *
+  * @param ctx an AVCAMELLIA context
+  * @param key a key of 16, 24, 32 bytes used for encryption/decryption
+  * @param key_bits number of keybits: possible are 128, 192, 256
+ */
+int av_camellia_init(struct AVCAMELLIA *ctx, const uint8_t *key, int key_bits);
+
+/**
+  * Encrypt or decrypt a buffer using a previously initialized context
+  *
+  * @param ctx an AVCAMELLIA context
+  * @param dst destination array, can be equal to src
+  * @param src source array, can be equal to dst
+  * @param count number of 16 byte blocks
+  * @paran iv initialization vector for CBC mode, NULL for ECB mode
+  * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_camellia_crypt(struct AVCAMELLIA *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t* iv, int decrypt);
+
+/**
+ * @}
+ */
+#endif /* AVUTIL_CAMELLIA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cast5.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cast5.h
new file mode 100644
index 0000000..ad5b347
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cast5.h
@@ -0,0 +1,80 @@
+/*
+ * An implementation of the CAST128 algorithm as mentioned in RFC2144
+ * Copyright (c) 2014 Supraja Meedinti
+ *
+ * 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 AVUTIL_CAST5_H
+#define AVUTIL_CAST5_H
+
+#include <stdint.h>
+
+
+/**
+  * @file
+  * @brief Public header for libavutil CAST5 algorithm
+  * @defgroup lavu_cast5 CAST5
+  * @ingroup lavu_crypto
+  * @{
+  */
+
+extern const int av_cast5_size;
+
+struct AVCAST5;
+
+/**
+  * Allocate an AVCAST5 context
+  * To free the struct: av_free(ptr)
+  */
+struct AVCAST5 *av_cast5_alloc(void);
+/**
+  * Initialize an AVCAST5 context.
+  *
+  * @param ctx an AVCAST5 context
+  * @param key a key of 5,6,...16 bytes used for encryption/decryption
+  * @param key_bits number of keybits: possible are 40,48,...,128
+  * @return 0 on success, less than 0 on failure
+ */
+int av_cast5_init(struct AVCAST5 *ctx, const uint8_t *key, int key_bits);
+
+/**
+  * Encrypt or decrypt a buffer using a previously initialized context, ECB mode only
+  *
+  * @param ctx an AVCAST5 context
+  * @param dst destination array, can be equal to src
+  * @param src source array, can be equal to dst
+  * @param count number of 8 byte blocks
+  * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_cast5_crypt(struct AVCAST5 *ctx, uint8_t *dst, const uint8_t *src, int count, int decrypt);
+
+/**
+  * Encrypt or decrypt a buffer using a previously initialized context
+  *
+  * @param ctx an AVCAST5 context
+  * @param dst destination array, can be equal to src
+  * @param src source array, can be equal to dst
+  * @param count number of 8 byte blocks
+  * @param iv initialization vector for CBC mode, NULL for ECB mode
+  * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_cast5_crypt2(struct AVCAST5 *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt);
+/**
+ * @}
+ */
+#endif /* AVUTIL_CAST5_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/channel_layout.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/channel_layout.h
new file mode 100644
index 0000000..50bb8f0
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/channel_layout.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (c) 2008 Peter Ross
+ *
+ * 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 AVUTIL_CHANNEL_LAYOUT_H
+#define AVUTIL_CHANNEL_LAYOUT_H
+
+#include <stdint.h>
+
+/**
+ * @file
+ * audio channel layout utility functions
+ */
+
+/**
+ * @addtogroup lavu_audio
+ * @{
+ */
+
+/**
+ * @defgroup channel_masks Audio channel masks
+ *
+ * A channel layout is a 64-bits integer with a bit set for every channel.
+ * The number of bits set must be equal to the number of channels.
+ * The value 0 means that the channel layout is not known.
+ * @note this data structure is not powerful enough to handle channels
+ * combinations that have the same channel multiple times, such as
+ * dual-mono.
+ *
+ * @{
+ */
+#define AV_CH_FRONT_LEFT             0x00000001
+#define AV_CH_FRONT_RIGHT            0x00000002
+#define AV_CH_FRONT_CENTER           0x00000004
+#define AV_CH_LOW_FREQUENCY          0x00000008
+#define AV_CH_BACK_LEFT              0x00000010
+#define AV_CH_BACK_RIGHT             0x00000020
+#define AV_CH_FRONT_LEFT_OF_CENTER   0x00000040
+#define AV_CH_FRONT_RIGHT_OF_CENTER  0x00000080
+#define AV_CH_BACK_CENTER            0x00000100
+#define AV_CH_SIDE_LEFT              0x00000200
+#define AV_CH_SIDE_RIGHT             0x00000400
+#define AV_CH_TOP_CENTER             0x00000800
+#define AV_CH_TOP_FRONT_LEFT         0x00001000
+#define AV_CH_TOP_FRONT_CENTER       0x00002000
+#define AV_CH_TOP_FRONT_RIGHT        0x00004000
+#define AV_CH_TOP_BACK_LEFT          0x00008000
+#define AV_CH_TOP_BACK_CENTER        0x00010000
+#define AV_CH_TOP_BACK_RIGHT         0x00020000
+#define AV_CH_STEREO_LEFT            0x20000000  ///< Stereo downmix.
+#define AV_CH_STEREO_RIGHT           0x40000000  ///< See AV_CH_STEREO_LEFT.
+#define AV_CH_WIDE_LEFT              0x0000000080000000ULL
+#define AV_CH_WIDE_RIGHT             0x0000000100000000ULL
+#define AV_CH_SURROUND_DIRECT_LEFT   0x0000000200000000ULL
+#define AV_CH_SURROUND_DIRECT_RIGHT  0x0000000400000000ULL
+#define AV_CH_LOW_FREQUENCY_2        0x0000000800000000ULL
+
+/** Channel mask value used for AVCodecContext.request_channel_layout
+    to indicate that the user requests the channel order of the decoder output
+    to be the native codec channel order. */
+#define AV_CH_LAYOUT_NATIVE          0x8000000000000000ULL
+
+/**
+ * @}
+ * @defgroup channel_mask_c Audio channel layouts
+ * @{
+ * */
+#define AV_CH_LAYOUT_MONO              (AV_CH_FRONT_CENTER)
+#define AV_CH_LAYOUT_STEREO            (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
+#define AV_CH_LAYOUT_2POINT1           (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
+#define AV_CH_LAYOUT_2_1               (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
+#define AV_CH_LAYOUT_SURROUND          (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
+#define AV_CH_LAYOUT_3POINT1           (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)
+#define AV_CH_LAYOUT_4POINT0           (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)
+#define AV_CH_LAYOUT_4POINT1           (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)
+#define AV_CH_LAYOUT_2_2               (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
+#define AV_CH_LAYOUT_QUAD              (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
+#define AV_CH_LAYOUT_5POINT0           (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
+#define AV_CH_LAYOUT_5POINT1           (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)
+#define AV_CH_LAYOUT_5POINT0_BACK      (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
+#define AV_CH_LAYOUT_5POINT1_BACK      (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY)
+#define AV_CH_LAYOUT_6POINT0           (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER)
+#define AV_CH_LAYOUT_6POINT0_FRONT     (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
+#define AV_CH_LAYOUT_HEXAGONAL         (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER)
+#define AV_CH_LAYOUT_6POINT1           (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER)
+#define AV_CH_LAYOUT_6POINT1_BACK      (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER)
+#define AV_CH_LAYOUT_6POINT1_FRONT     (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY)
+#define AV_CH_LAYOUT_7POINT0           (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
+#define AV_CH_LAYOUT_7POINT0_FRONT     (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
+#define AV_CH_LAYOUT_7POINT1           (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
+#define AV_CH_LAYOUT_7POINT1_WIDE      (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
+#define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
+#define AV_CH_LAYOUT_OCTAGONAL         (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT)
+#define AV_CH_LAYOUT_HEXADECAGONAL     (AV_CH_LAYOUT_OCTAGONAL|AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT|AV_CH_TOP_BACK_CENTER|AV_CH_TOP_FRONT_CENTER|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT)
+#define AV_CH_LAYOUT_STEREO_DOWNMIX    (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT)
+
+enum AVMatrixEncoding {
+    AV_MATRIX_ENCODING_NONE,
+    AV_MATRIX_ENCODING_DOLBY,
+    AV_MATRIX_ENCODING_DPLII,
+    AV_MATRIX_ENCODING_DPLIIX,
+    AV_MATRIX_ENCODING_DPLIIZ,
+    AV_MATRIX_ENCODING_DOLBYEX,
+    AV_MATRIX_ENCODING_DOLBYHEADPHONE,
+    AV_MATRIX_ENCODING_NB
+};
+
+/**
+ * Return a channel layout id that matches name, or 0 if no match is found.
+ *
+ * name can be one or several of the following notations,
+ * separated by '+' or '|':
+ * - the name of an usual channel layout (mono, stereo, 4.0, quad, 5.0,
+ *   5.0(side), 5.1, 5.1(side), 7.1, 7.1(wide), downmix);
+ * - the name of a single channel (FL, FR, FC, LFE, BL, BR, FLC, FRC, BC,
+ *   SL, SR, TC, TFL, TFC, TFR, TBL, TBC, TBR, DL, DR);
+ * - a number of channels, in decimal, followed by 'c', yielding
+ *   the default channel layout for that number of channels (@see
+ *   av_get_default_channel_layout);
+ * - a channel layout mask, in hexadecimal starting with "0x" (see the
+ *   AV_CH_* macros).
+ *
+ * Example: "stereo+FC" = "2c+FC" = "2c+1c" = "0x7"
+ */
+uint64_t av_get_channel_layout(const char *name);
+
+/**
+ * Return a channel layout and the number of channels based on the specified name.
+ *
+ * This function is similar to (@see av_get_channel_layout), but can also parse
+ * unknown channel layout specifications.
+ *
+ * @param[in]  name             channel layout specification string
+ * @param[out] channel_layout   parsed channel layout (0 if unknown)
+ * @param[out] nb_channels      number of channels
+ *
+ * @return 0 on success, AVERROR(EINVAL) if the parsing fails.
+ */
+int av_get_extended_channel_layout(const char *name, uint64_t* channel_layout, int* nb_channels);
+
+/**
+ * Return a description of a channel layout.
+ * If nb_channels is <= 0, it is guessed from the channel_layout.
+ *
+ * @param buf put here the string containing the channel layout
+ * @param buf_size size in bytes of the buffer
+ */
+void av_get_channel_layout_string(char *buf, int buf_size, int nb_channels, uint64_t channel_layout);
+
+struct AVBPrint;
+/**
+ * Append a description of a channel layout to a bprint buffer.
+ */
+void av_bprint_channel_layout(struct AVBPrint *bp, int nb_channels, uint64_t channel_layout);
+
+/**
+ * Return the number of channels in the channel layout.
+ */
+int av_get_channel_layout_nb_channels(uint64_t channel_layout);
+
+/**
+ * Return default channel layout for a given number of channels.
+ */
+int64_t av_get_default_channel_layout(int nb_channels);
+
+/**
+ * Get the index of a channel in channel_layout.
+ *
+ * @param channel a channel layout describing exactly one channel which must be
+ *                present in channel_layout.
+ *
+ * @return index of channel in channel_layout on success, a negative AVERROR
+ *         on error.
+ */
+int av_get_channel_layout_channel_index(uint64_t channel_layout,
+                                        uint64_t channel);
+
+/**
+ * Get the channel with the given index in channel_layout.
+ */
+uint64_t av_channel_layout_extract_channel(uint64_t channel_layout, int index);
+
+/**
+ * Get the name of a given channel.
+ *
+ * @return channel name on success, NULL on error.
+ */
+const char *av_get_channel_name(uint64_t channel);
+
+/**
+ * Get the description of a given channel.
+ *
+ * @param channel  a channel layout with a single channel
+ * @return  channel description on success, NULL on error
+ */
+const char *av_get_channel_description(uint64_t channel);
+
+/**
+ * Get the value and name of a standard channel layout.
+ *
+ * @param[in]  index   index in an internal list, starting at 0
+ * @param[out] layout  channel layout mask
+ * @param[out] name    name of the layout
+ * @return  0  if the layout exists,
+ *          <0 if index is beyond the limits
+ */
+int av_get_standard_channel_layout(unsigned index, uint64_t *layout,
+                                   const char **name);
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_CHANNEL_LAYOUT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/common.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/common.h
new file mode 100644
index 0000000..8db0291
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/common.h
@@ -0,0 +1,560 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * common internal and external API header
+ */
+
+#ifndef AVUTIL_COMMON_H
+#define AVUTIL_COMMON_H
+
+#if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C)
+#error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "attributes.h"
+#include "macros.h"
+#include "version.h"
+#include "libavutil/avconfig.h"
+
+#if AV_HAVE_BIGENDIAN
+#   define AV_NE(be, le) (be)
+#else
+#   define AV_NE(be, le) (le)
+#endif
+
+//rounded division & shift
+#define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b))
+/* assume b>0 */
+#define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
+/* Fast a/(1<<b) rounded toward +inf. Assume a>=0 and b>=0 */
+#define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \
+                                                       : ((a) + (1<<(b)) - 1) >> (b))
+/* Backwards compat. */
+#define FF_CEIL_RSHIFT AV_CEIL_RSHIFT
+
+#define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b))
+#define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b))
+
+/**
+ * Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they
+ * are not representable as absolute values of their type. This is the same
+ * as with *abs()
+ * @see FFNABS()
+ */
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFSIGN(a) ((a) > 0 ? 1 : -1)
+
+/**
+ * Negative Absolute value.
+ * this works for all integers of all types.
+ * As with many macros, this evaluates its argument twice, it thus must not have
+ * a sideeffect, that is FFNABS(x++) has undefined behavior.
+ */
+#define FFNABS(a) ((a) <= 0 ? (a) : (-(a)))
+
+/**
+ * Comparator.
+ * For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0
+ * if x == y. This is useful for instance in a qsort comparator callback.
+ * Furthermore, compilers are able to optimize this to branchless code, and
+ * there is no risk of overflow with signed types.
+ * As with many macros, this evaluates its argument multiple times, it thus
+ * must not have a side-effect.
+ */
+#define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y)))
+
+#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
+#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c)
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c)
+
+#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
+#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
+
+/* misc math functions */
+
+#ifdef HAVE_AV_CONFIG_H
+#   include "config.h"
+#   include "intmath.h"
+#endif
+
+/* Pull in unguarded fallback defines at the end of this file. */
+#include "common.h"
+
+#ifndef av_log2
+av_const int av_log2(unsigned v);
+#endif
+
+#ifndef av_log2_16bit
+av_const int av_log2_16bit(unsigned v);
+#endif
+
+/**
+ * Clip a signed integer value into the amin-amax range.
+ * @param a value to clip
+ * @param amin minimum value of the clip range
+ * @param amax maximum value of the clip range
+ * @return clipped value
+ */
+static av_always_inline av_const int av_clip_c(int a, int amin, int amax)
+{
+#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
+    if (amin > amax) abort();
+#endif
+    if      (a < amin) return amin;
+    else if (a > amax) return amax;
+    else               return a;
+}
+
+/**
+ * Clip a signed 64bit integer value into the amin-amax range.
+ * @param a value to clip
+ * @param amin minimum value of the clip range
+ * @param amax maximum value of the clip range
+ * @return clipped value
+ */
+static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax)
+{
+#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
+    if (amin > amax) abort();
+#endif
+    if      (a < amin) return amin;
+    else if (a > amax) return amax;
+    else               return a;
+}
+
+/**
+ * Clip a signed integer value into the 0-255 range.
+ * @param a value to clip
+ * @return clipped value
+ */
+static av_always_inline av_const uint8_t av_clip_uint8_c(int a)
+{
+    if (a&(~0xFF)) return (~a)>>31;
+    else           return a;
+}
+
+/**
+ * Clip a signed integer value into the -128,127 range.
+ * @param a value to clip
+ * @return clipped value
+ */
+static av_always_inline av_const int8_t av_clip_int8_c(int a)
+{
+    if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F;
+    else                  return a;
+}
+
+/**
+ * Clip a signed integer value into the 0-65535 range.
+ * @param a value to clip
+ * @return clipped value
+ */
+static av_always_inline av_const uint16_t av_clip_uint16_c(int a)
+{
+    if (a&(~0xFFFF)) return (~a)>>31;
+    else             return a;
+}
+
+/**
+ * Clip a signed integer value into the -32768,32767 range.
+ * @param a value to clip
+ * @return clipped value
+ */
+static av_always_inline av_const int16_t av_clip_int16_c(int a)
+{
+    if ((a+0x8000U) & ~0xFFFF) return (a>>31) ^ 0x7FFF;
+    else                      return a;
+}
+
+/**
+ * Clip a signed 64-bit integer value into the -2147483648,2147483647 range.
+ * @param a value to clip
+ * @return clipped value
+ */
+static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a)
+{
+    if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF);
+    else                                         return (int32_t)a;
+}
+
+/**
+ * Clip a signed integer into the -(2^p),(2^p-1) range.
+ * @param  a value to clip
+ * @param  p bit position to clip at
+ * @return clipped value
+ */
+static av_always_inline av_const int av_clip_intp2_c(int a, int p)
+{
+    if (((unsigned)a + (1 << p)) & ~((2 << p) - 1))
+        return (a >> 31) ^ ((1 << p) - 1);
+    else
+        return a;
+}
+
+/**
+ * Clip a signed integer to an unsigned power of two range.
+ * @param  a value to clip
+ * @param  p bit position to clip at
+ * @return clipped value
+ */
+static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p)
+{
+    if (a & ~((1<<p) - 1)) return (~a) >> 31 & ((1<<p) - 1);
+    else                   return  a;
+}
+
+/**
+ * Clear high bits from an unsigned integer starting with specific bit position
+ * @param  a value to clip
+ * @param  p bit position to clip at
+ * @return clipped value
+ */
+static av_always_inline av_const unsigned av_mod_uintp2_c(unsigned a, unsigned p)
+{
+    return a & ((1 << p) - 1);
+}
+
+/**
+ * Add two signed 32-bit values with saturation.
+ *
+ * @param  a one value
+ * @param  b another value
+ * @return sum with signed saturation
+ */
+static av_always_inline int av_sat_add32_c(int a, int b)
+{
+    return av_clipl_int32((int64_t)a + b);
+}
+
+/**
+ * Add a doubled value to another value with saturation at both stages.
+ *
+ * @param  a first value
+ * @param  b value doubled and added to a
+ * @return sum sat(a + sat(2*b)) with signed saturation
+ */
+static av_always_inline int av_sat_dadd32_c(int a, int b)
+{
+    return av_sat_add32(a, av_sat_add32(b, b));
+}
+
+/**
+ * Subtract two signed 32-bit values with saturation.
+ *
+ * @param  a one value
+ * @param  b another value
+ * @return difference with signed saturation
+ */
+static av_always_inline int av_sat_sub32_c(int a, int b)
+{
+    return av_clipl_int32((int64_t)a - b);
+}
+
+/**
+ * Subtract a doubled value from another value with saturation at both stages.
+ *
+ * @param  a first value
+ * @param  b value doubled and subtracted from a
+ * @return difference sat(a - sat(2*b)) with signed saturation
+ */
+static av_always_inline int av_sat_dsub32_c(int a, int b)
+{
+    return av_sat_sub32(a, av_sat_add32(b, b));
+}
+
+/**
+ * Clip a float value into the amin-amax range.
+ * @param a value to clip
+ * @param amin minimum value of the clip range
+ * @param amax maximum value of the clip range
+ * @return clipped value
+ */
+static av_always_inline av_const float av_clipf_c(float a, float amin, float amax)
+{
+#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
+    if (amin > amax) abort();
+#endif
+    if      (a < amin) return amin;
+    else if (a > amax) return amax;
+    else               return a;
+}
+
+/**
+ * Clip a double value into the amin-amax range.
+ * @param a value to clip
+ * @param amin minimum value of the clip range
+ * @param amax maximum value of the clip range
+ * @return clipped value
+ */
+static av_always_inline av_const double av_clipd_c(double a, double amin, double amax)
+{
+#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
+    if (amin > amax) abort();
+#endif
+    if      (a < amin) return amin;
+    else if (a > amax) return amax;
+    else               return a;
+}
+
+/** Compute ceil(log2(x)).
+ * @param x value used to compute ceil(log2(x))
+ * @return computed ceiling of log2(x)
+ */
+static av_always_inline av_const int av_ceil_log2_c(int x)
+{
+    return av_log2((x - 1) << 1);
+}
+
+/**
+ * Count number of bits set to one in x
+ * @param x value to count bits of
+ * @return the number of bits set to one in x
+ */
+static av_always_inline av_const int av_popcount_c(uint32_t x)
+{
+    x -= (x >> 1) & 0x55555555;
+    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+    x = (x + (x >> 4)) & 0x0F0F0F0F;
+    x += x >> 8;
+    return (x + (x >> 16)) & 0x3F;
+}
+
+/**
+ * Count number of bits set to one in x
+ * @param x value to count bits of
+ * @return the number of bits set to one in x
+ */
+static av_always_inline av_const int av_popcount64_c(uint64_t x)
+{
+    return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32));
+}
+
+static av_always_inline av_const int av_parity_c(uint32_t v)
+{
+    return av_popcount(v) & 1;
+}
+
+#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))
+#define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24))
+
+/**
+ * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form.
+ *
+ * @param val      Output value, must be an lvalue of type uint32_t.
+ * @param GET_BYTE Expression reading one byte from the input.
+ *                 Evaluated up to 7 times (4 for the currently
+ *                 assigned Unicode range).  With a memory buffer
+ *                 input, this could be *ptr++.
+ * @param ERROR    Expression to be evaluated on invalid input,
+ *                 typically a goto statement.
+ *
+ * @warning ERROR should not contain a loop control statement which
+ * could interact with the internal while loop, and should force an
+ * exit from the macro code (e.g. through a goto or a return) in order
+ * to prevent undefined results.
+ */
+#define GET_UTF8(val, GET_BYTE, ERROR)\
+    val= (GET_BYTE);\
+    {\
+        uint32_t top = (val & 128) >> 1;\
+        if ((val & 0xc0) == 0x80 || val >= 0xFE)\
+            ERROR\
+        while (val & top) {\
+            int tmp= (GET_BYTE) - 128;\
+            if(tmp>>6)\
+                ERROR\
+            val= (val<<6) + tmp;\
+            top <<= 5;\
+        }\
+        val &= (top << 1) - 1;\
+    }
+
+/**
+ * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form.
+ *
+ * @param val       Output value, must be an lvalue of type uint32_t.
+ * @param GET_16BIT Expression returning two bytes of UTF-16 data converted
+ *                  to native byte order.  Evaluated one or two times.
+ * @param ERROR     Expression to be evaluated on invalid input,
+ *                  typically a goto statement.
+ */
+#define GET_UTF16(val, GET_16BIT, ERROR)\
+    val = GET_16BIT;\
+    {\
+        unsigned int hi = val - 0xD800;\
+        if (hi < 0x800) {\
+            val = GET_16BIT - 0xDC00;\
+            if (val > 0x3FFU || hi > 0x3FFU)\
+                ERROR\
+            val += (hi<<10) + 0x10000;\
+        }\
+    }\
+
+/**
+ * @def PUT_UTF8(val, tmp, PUT_BYTE)
+ * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long).
+ * @param val is an input-only argument and should be of type uint32_t. It holds
+ * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If
+ * val is given as a function it is executed only once.
+ * @param tmp is a temporary variable and should be of type uint8_t. It
+ * represents an intermediate value during conversion that is to be
+ * output by PUT_BYTE.
+ * @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination.
+ * It could be a function or a statement, and uses tmp as the input byte.
+ * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be
+ * executed up to 4 times for values in the valid UTF-8 range and up to
+ * 7 times in the general case, depending on the length of the converted
+ * Unicode character.
+ */
+#define PUT_UTF8(val, tmp, PUT_BYTE)\
+    {\
+        int bytes, shift;\
+        uint32_t in = val;\
+        if (in < 0x80) {\
+            tmp = in;\
+            PUT_BYTE\
+        } else {\
+            bytes = (av_log2(in) + 4) / 5;\
+            shift = (bytes - 1) * 6;\
+            tmp = (256 - (256 >> bytes)) | (in >> shift);\
+            PUT_BYTE\
+            while (shift >= 6) {\
+                shift -= 6;\
+                tmp = 0x80 | ((in >> shift) & 0x3f);\
+                PUT_BYTE\
+            }\
+        }\
+    }
+
+/**
+ * @def PUT_UTF16(val, tmp, PUT_16BIT)
+ * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes).
+ * @param val is an input-only argument and should be of type uint32_t. It holds
+ * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If
+ * val is given as a function it is executed only once.
+ * @param tmp is a temporary variable and should be of type uint16_t. It
+ * represents an intermediate value during conversion that is to be
+ * output by PUT_16BIT.
+ * @param PUT_16BIT writes the converted UTF-16 data to any proper destination
+ * in desired endianness. It could be a function or a statement, and uses tmp
+ * as the input byte.  For example, PUT_BYTE could be "*output++ = tmp;"
+ * PUT_BYTE will be executed 1 or 2 times depending on input character.
+ */
+#define PUT_UTF16(val, tmp, PUT_16BIT)\
+    {\
+        uint32_t in = val;\
+        if (in < 0x10000) {\
+            tmp = in;\
+            PUT_16BIT\
+        } else {\
+            tmp = 0xD800 | ((in - 0x10000) >> 10);\
+            PUT_16BIT\
+            tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\
+            PUT_16BIT\
+        }\
+    }\
+
+
+
+#include "mem.h"
+
+#ifdef HAVE_AV_CONFIG_H
+#    include "internal.h"
+#endif /* HAVE_AV_CONFIG_H */
+
+#endif /* AVUTIL_COMMON_H */
+
+/*
+ * The following definitions are outside the multiple inclusion guard
+ * to ensure they are immediately available in intmath.h.
+ */
+
+#ifndef av_ceil_log2
+#   define av_ceil_log2     av_ceil_log2_c
+#endif
+#ifndef av_clip
+#   define av_clip          av_clip_c
+#endif
+#ifndef av_clip64
+#   define av_clip64        av_clip64_c
+#endif
+#ifndef av_clip_uint8
+#   define av_clip_uint8    av_clip_uint8_c
+#endif
+#ifndef av_clip_int8
+#   define av_clip_int8     av_clip_int8_c
+#endif
+#ifndef av_clip_uint16
+#   define av_clip_uint16   av_clip_uint16_c
+#endif
+#ifndef av_clip_int16
+#   define av_clip_int16    av_clip_int16_c
+#endif
+#ifndef av_clipl_int32
+#   define av_clipl_int32   av_clipl_int32_c
+#endif
+#ifndef av_clip_intp2
+#   define av_clip_intp2    av_clip_intp2_c
+#endif
+#ifndef av_clip_uintp2
+#   define av_clip_uintp2   av_clip_uintp2_c
+#endif
+#ifndef av_mod_uintp2
+#   define av_mod_uintp2    av_mod_uintp2_c
+#endif
+#ifndef av_sat_add32
+#   define av_sat_add32     av_sat_add32_c
+#endif
+#ifndef av_sat_dadd32
+#   define av_sat_dadd32    av_sat_dadd32_c
+#endif
+#ifndef av_sat_sub32
+#   define av_sat_sub32     av_sat_sub32_c
+#endif
+#ifndef av_sat_dsub32
+#   define av_sat_dsub32    av_sat_dsub32_c
+#endif
+#ifndef av_clipf
+#   define av_clipf         av_clipf_c
+#endif
+#ifndef av_clipd
+#   define av_clipd         av_clipd_c
+#endif
+#ifndef av_popcount
+#   define av_popcount      av_popcount_c
+#endif
+#ifndef av_popcount64
+#   define av_popcount64    av_popcount64_c
+#endif
+#ifndef av_parity
+#   define av_parity        av_parity_c
+#endif
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cpu.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cpu.h
new file mode 100644
index 0000000..8bb9eb6
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/cpu.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2000, 2001, 2002 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 AVUTIL_CPU_H
+#define AVUTIL_CPU_H
+
+#include <stddef.h>
+
+#include "attributes.h"
+
+#define AV_CPU_FLAG_FORCE    0x80000000 /* force usage of selected flags (OR) */
+
+    /* lower 16 bits - CPU features */
+#define AV_CPU_FLAG_MMX          0x0001 ///< standard MMX
+#define AV_CPU_FLAG_MMXEXT       0x0002 ///< SSE integer functions or AMD MMX ext
+#define AV_CPU_FLAG_MMX2         0x0002 ///< SSE integer functions or AMD MMX ext
+#define AV_CPU_FLAG_3DNOW        0x0004 ///< AMD 3DNOW
+#define AV_CPU_FLAG_SSE          0x0008 ///< SSE functions
+#define AV_CPU_FLAG_SSE2         0x0010 ///< PIV SSE2 functions
+#define AV_CPU_FLAG_SSE2SLOW 0x40000000 ///< SSE2 supported, but usually not faster
+                                        ///< than regular MMX/SSE (e.g. Core1)
+#define AV_CPU_FLAG_3DNOWEXT     0x0020 ///< AMD 3DNowExt
+#define AV_CPU_FLAG_SSE3         0x0040 ///< Prescott SSE3 functions
+#define AV_CPU_FLAG_SSE3SLOW 0x20000000 ///< SSE3 supported, but usually not faster
+                                        ///< than regular MMX/SSE (e.g. Core1)
+#define AV_CPU_FLAG_SSSE3        0x0080 ///< Conroe SSSE3 functions
+#define AV_CPU_FLAG_SSSE3SLOW 0x4000000 ///< SSSE3 supported, but usually not faster
+#define AV_CPU_FLAG_ATOM     0x10000000 ///< Atom processor, some SSSE3 instructions are slower
+#define AV_CPU_FLAG_SSE4         0x0100 ///< Penryn SSE4.1 functions
+#define AV_CPU_FLAG_SSE42        0x0200 ///< Nehalem SSE4.2 functions
+#define AV_CPU_FLAG_AESNI       0x80000 ///< Advanced Encryption Standard functions
+#define AV_CPU_FLAG_AVX          0x4000 ///< AVX functions: requires OS support even if YMM registers aren't used
+#define AV_CPU_FLAG_AVXSLOW   0x8000000 ///< AVX supported, but slow when using YMM registers (e.g. Bulldozer)
+#define AV_CPU_FLAG_XOP          0x0400 ///< Bulldozer XOP functions
+#define AV_CPU_FLAG_FMA4         0x0800 ///< Bulldozer FMA4 functions
+#define AV_CPU_FLAG_CMOV         0x1000 ///< supports cmov instruction
+#define AV_CPU_FLAG_AVX2         0x8000 ///< AVX2 functions: requires OS support even if YMM registers aren't used
+#define AV_CPU_FLAG_FMA3        0x10000 ///< Haswell FMA3 functions
+#define AV_CPU_FLAG_BMI1        0x20000 ///< Bit Manipulation Instruction Set 1
+#define AV_CPU_FLAG_BMI2        0x40000 ///< Bit Manipulation Instruction Set 2
+#define AV_CPU_FLAG_AVX512     0x100000 ///< AVX-512 functions: requires OS support even if YMM/ZMM registers aren't used
+
+#define AV_CPU_FLAG_ALTIVEC      0x0001 ///< standard
+#define AV_CPU_FLAG_VSX          0x0002 ///< ISA 2.06
+#define AV_CPU_FLAG_POWER8       0x0004 ///< ISA 2.07
+
+#define AV_CPU_FLAG_ARMV5TE      (1 << 0)
+#define AV_CPU_FLAG_ARMV6        (1 << 1)
+#define AV_CPU_FLAG_ARMV6T2      (1 << 2)
+#define AV_CPU_FLAG_VFP          (1 << 3)
+#define AV_CPU_FLAG_VFPV3        (1 << 4)
+#define AV_CPU_FLAG_NEON         (1 << 5)
+#define AV_CPU_FLAG_ARMV8        (1 << 6)
+#define AV_CPU_FLAG_VFP_VM       (1 << 7) ///< VFPv2 vector mode, deprecated in ARMv7-A and unavailable in various CPUs implementations
+#define AV_CPU_FLAG_SETEND       (1 <<16)
+
+/**
+ * Return the flags which specify extensions supported by the CPU.
+ * The returned value is affected by av_force_cpu_flags() if that was used
+ * before. So av_get_cpu_flags() can easily be used in an application to
+ * detect the enabled cpu flags.
+ */
+int av_get_cpu_flags(void);
+
+/**
+ * Disables cpu detection and forces the specified flags.
+ * -1 is a special case that disables forcing of specific flags.
+ */
+void av_force_cpu_flags(int flags);
+
+/**
+ * Set a mask on flags returned by av_get_cpu_flags().
+ * This function is mainly useful for testing.
+ * Please use av_force_cpu_flags() and av_get_cpu_flags() instead which are more flexible
+ */
+attribute_deprecated void av_set_cpu_flags_mask(int mask);
+
+/**
+ * Parse CPU flags from a string.
+ *
+ * The returned flags contain the specified flags as well as related unspecified flags.
+ *
+ * This function exists only for compatibility with libav.
+ * Please use av_parse_cpu_caps() when possible.
+ * @return a combination of AV_CPU_* flags, negative on error.
+ */
+attribute_deprecated
+int av_parse_cpu_flags(const char *s);
+
+/**
+ * Parse CPU caps from a string and update the given AV_CPU_* flags based on that.
+ *
+ * @return negative on error.
+ */
+int av_parse_cpu_caps(unsigned *flags, const char *s);
+
+/**
+ * @return the number of logical CPU cores present.
+ */
+int av_cpu_count(void);
+
+/**
+ * Get the maximum data alignment that may be required by FFmpeg.
+ *
+ * Note that this is affected by the build configuration and the CPU flags mask,
+ * so e.g. if the CPU supports AVX, but libavutil has been built with
+ * --disable-avx or the AV_CPU_FLAG_AVX flag has been disabled through
+ *  av_set_cpu_flags_mask(), then this function will behave as if AVX is not
+ *  present.
+ */
+size_t av_cpu_max_align(void);
+
+#endif /* AVUTIL_CPU_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/crc.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/crc.h
new file mode 100644
index 0000000..47e22b4
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/crc.h
@@ -0,0 +1,100 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_crc32
+ * Public header for CRC hash function implementation.
+ */
+
+#ifndef AVUTIL_CRC_H
+#define AVUTIL_CRC_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include "attributes.h"
+#include "version.h"
+
+/**
+ * @defgroup lavu_crc32 CRC
+ * @ingroup lavu_hash
+ * CRC (Cyclic Redundancy Check) hash function implementation.
+ *
+ * This module supports numerous CRC polynomials, in addition to the most
+ * widely used CRC-32-IEEE. See @ref AVCRCId for a list of available
+ * polynomials.
+ *
+ * @{
+ */
+
+typedef uint32_t AVCRC;
+
+typedef enum {
+    AV_CRC_8_ATM,
+    AV_CRC_16_ANSI,
+    AV_CRC_16_CCITT,
+    AV_CRC_32_IEEE,
+    AV_CRC_32_IEEE_LE,  /*< reversed bitorder version of AV_CRC_32_IEEE */
+    AV_CRC_16_ANSI_LE,  /*< reversed bitorder version of AV_CRC_16_ANSI */
+    AV_CRC_24_IEEE,
+    AV_CRC_8_EBU,
+    AV_CRC_MAX,         /*< Not part of public API! Do not use outside libavutil. */
+}AVCRCId;
+
+/**
+ * Initialize a CRC table.
+ * @param ctx must be an array of size sizeof(AVCRC)*257 or sizeof(AVCRC)*1024
+ * @param le If 1, the lowest bit represents the coefficient for the highest
+ *           exponent of the corresponding polynomial (both for poly and
+ *           actual CRC).
+ *           If 0, you must swap the CRC parameter and the result of av_crc
+ *           if you need the standard representation (can be simplified in
+ *           most cases to e.g. bswap16):
+ *           av_bswap32(crc << (32-bits))
+ * @param bits number of bits for the CRC
+ * @param poly generator polynomial without the x**bits coefficient, in the
+ *             representation as specified by le
+ * @param ctx_size size of ctx in bytes
+ * @return <0 on failure
+ */
+int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size);
+
+/**
+ * Get an initialized standard CRC table.
+ * @param crc_id ID of a standard CRC
+ * @return a pointer to the CRC table or NULL on failure
+ */
+const AVCRC *av_crc_get_table(AVCRCId crc_id);
+
+/**
+ * Calculate the CRC of a block.
+ * @param crc CRC of previous blocks if any or initial value for CRC
+ * @return CRC updated with the data from the given block
+ *
+ * @see av_crc_init() "le" parameter
+ */
+uint32_t av_crc(const AVCRC *ctx, uint32_t crc,
+                const uint8_t *buffer, size_t length) av_pure;
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_CRC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/des.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/des.h
new file mode 100644
index 0000000..4cf11f5
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/des.h
@@ -0,0 +1,77 @@
+/*
+ * DES encryption/decryption
+ * Copyright (c) 2007 Reimar Doeffinger
+ *
+ * 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 AVUTIL_DES_H
+#define AVUTIL_DES_H
+
+#include <stdint.h>
+
+/**
+ * @defgroup lavu_des DES
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+typedef struct AVDES {
+    uint64_t round_keys[3][16];
+    int triple_des;
+} AVDES;
+
+/**
+ * Allocate an AVDES context.
+ */
+AVDES *av_des_alloc(void);
+
+/**
+ * @brief Initializes an AVDES context.
+ *
+ * @param key_bits must be 64 or 192
+ * @param decrypt 0 for encryption/CBC-MAC, 1 for decryption
+ * @return zero on success, negative value otherwise
+ */
+int av_des_init(struct AVDES *d, const uint8_t *key, int key_bits, int decrypt);
+
+/**
+ * @brief Encrypts / decrypts using the DES algorithm.
+ *
+ * @param count number of 8 byte blocks
+ * @param dst destination array, can be equal to src, must be 8-byte aligned
+ * @param src source array, can be equal to dst, must be 8-byte aligned, may be NULL
+ * @param iv initialization vector for CBC mode, if NULL then ECB will be used,
+ *           must be 8-byte aligned
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_des_crypt(struct AVDES *d, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt);
+
+/**
+ * @brief Calculates CBC-MAC using the DES algorithm.
+ *
+ * @param count number of 8 byte blocks
+ * @param dst destination array, can be equal to src, must be 8-byte aligned
+ * @param src source array, can be equal to dst, must be 8-byte aligned, may be NULL
+ */
+void av_des_mac(struct AVDES *d, uint8_t *dst, const uint8_t *src, int count);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_DES_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/dict.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/dict.h
new file mode 100644
index 0000000..118f1f0
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/dict.h
@@ -0,0 +1,200 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * Public dictionary API.
+ * @deprecated
+ *  AVDictionary is provided for compatibility with libav. It is both in
+ *  implementation as well as API inefficient. It does not scale and is
+ *  extremely slow with large dictionaries.
+ *  It is recommended that new code uses our tree container from tree.c/h
+ *  where applicable, which uses AVL trees to achieve O(log n) performance.
+ */
+
+#ifndef AVUTIL_DICT_H
+#define AVUTIL_DICT_H
+
+#include <stdint.h>
+
+#include "version.h"
+
+/**
+ * @addtogroup lavu_dict AVDictionary
+ * @ingroup lavu_data
+ *
+ * @brief Simple key:value store
+ *
+ * @{
+ * Dictionaries are used for storing key:value pairs. To create
+ * an AVDictionary, simply pass an address of a NULL pointer to
+ * av_dict_set(). NULL can be used as an empty dictionary wherever
+ * a pointer to an AVDictionary is required.
+ * Use av_dict_get() to retrieve an entry or iterate over all
+ * entries and finally av_dict_free() to free the dictionary
+ * and all its contents.
+ *
+ @code
+   AVDictionary *d = NULL;           // "create" an empty dictionary
+   AVDictionaryEntry *t = NULL;
+
+   av_dict_set(&d, "foo", "bar", 0); // add an entry
+
+   char *k = av_strdup("key");       // if your strings are already allocated,
+   char *v = av_strdup("value");     // you can avoid copying them like this
+   av_dict_set(&d, k, v, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
+
+   while (t = av_dict_get(d, "", t, AV_DICT_IGNORE_SUFFIX)) {
+       <....>                             // iterate over all entries in d
+   }
+   av_dict_free(&d);
+ @endcode
+ */
+
+#define AV_DICT_MATCH_CASE      1   /**< Only get an entry with exact-case key match. Only relevant in av_dict_get(). */
+#define AV_DICT_IGNORE_SUFFIX   2   /**< Return first entry in a dictionary whose first part corresponds to the search key,
+                                         ignoring the suffix of the found key string. Only relevant in av_dict_get(). */
+#define AV_DICT_DONT_STRDUP_KEY 4   /**< Take ownership of a key that's been
+                                         allocated with av_malloc() or another memory allocation function. */
+#define AV_DICT_DONT_STRDUP_VAL 8   /**< Take ownership of a value that's been
+                                         allocated with av_malloc() or another memory allocation function. */
+#define AV_DICT_DONT_OVERWRITE 16   ///< Don't overwrite existing entries.
+#define AV_DICT_APPEND         32   /**< If the entry already exists, append to it.  Note that no
+                                      delimiter is added, the strings are simply concatenated. */
+#define AV_DICT_MULTIKEY       64   /**< Allow to store several equal keys in the dictionary */
+
+typedef struct AVDictionaryEntry {
+    char *key;
+    char *value;
+} AVDictionaryEntry;
+
+typedef struct AVDictionary AVDictionary;
+
+/**
+ * Get a dictionary entry with matching key.
+ *
+ * The returned entry key or value must not be changed, or it will
+ * cause undefined behavior.
+ *
+ * To iterate through all the dictionary entries, you can set the matching key
+ * to the null string "" and set the AV_DICT_IGNORE_SUFFIX flag.
+ *
+ * @param prev Set to the previous matching element to find the next.
+ *             If set to NULL the first matching element is returned.
+ * @param key matching key
+ * @param flags a collection of AV_DICT_* flags controlling how the entry is retrieved
+ * @return found entry or NULL in case no matching entry was found in the dictionary
+ */
+AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key,
+                               const AVDictionaryEntry *prev, int flags);
+
+/**
+ * Get number of entries in dictionary.
+ *
+ * @param m dictionary
+ * @return  number of entries in dictionary
+ */
+int av_dict_count(const AVDictionary *m);
+
+/**
+ * Set the given entry in *pm, overwriting an existing entry.
+ *
+ * Note: If AV_DICT_DONT_STRDUP_KEY or AV_DICT_DONT_STRDUP_VAL is set,
+ * these arguments will be freed on error.
+ *
+ * Warning: Adding a new entry to a dictionary invalidates all existing entries
+ * previously returned with av_dict_get.
+ *
+ * @param pm pointer to a pointer to a dictionary struct. If *pm is NULL
+ * a dictionary struct is allocated and put in *pm.
+ * @param key entry key to add to *pm (will either be av_strduped or added as a new key depending on flags)
+ * @param value entry value to add to *pm (will be av_strduped or added as a new key depending on flags).
+ *        Passing a NULL value will cause an existing entry to be deleted.
+ * @return >= 0 on success otherwise an error code <0
+ */
+int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags);
+
+/**
+ * Convenience wrapper for av_dict_set that converts the value to a string
+ * and stores it.
+ *
+ * Note: If AV_DICT_DONT_STRDUP_KEY is set, key will be freed on error.
+ */
+int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags);
+
+/**
+ * Parse the key/value pairs list and add the parsed entries to a dictionary.
+ *
+ * In case of failure, all the successfully set entries are stored in
+ * *pm. You may need to manually free the created dictionary.
+ *
+ * @param key_val_sep  a 0-terminated list of characters used to separate
+ *                     key from value
+ * @param pairs_sep    a 0-terminated list of characters used to separate
+ *                     two pairs from each other
+ * @param flags        flags to use when adding to dictionary.
+ *                     AV_DICT_DONT_STRDUP_KEY and AV_DICT_DONT_STRDUP_VAL
+ *                     are ignored since the key/value tokens will always
+ *                     be duplicated.
+ * @return             0 on success, negative AVERROR code on failure
+ */
+int av_dict_parse_string(AVDictionary **pm, const char *str,
+                         const char *key_val_sep, const char *pairs_sep,
+                         int flags);
+
+/**
+ * Copy entries from one AVDictionary struct into another.
+ * @param dst pointer to a pointer to a AVDictionary struct. If *dst is NULL,
+ *            this function will allocate a struct for you and put it in *dst
+ * @param src pointer to source AVDictionary struct
+ * @param flags flags to use when setting entries in *dst
+ * @note metadata is read using the AV_DICT_IGNORE_SUFFIX flag
+ * @return 0 on success, negative AVERROR code on failure. If dst was allocated
+ *           by this function, callers should free the associated memory.
+ */
+int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags);
+
+/**
+ * Free all the memory allocated for an AVDictionary struct
+ * and all keys and values.
+ */
+void av_dict_free(AVDictionary **m);
+
+/**
+ * Get dictionary entries as a string.
+ *
+ * Create a string containing dictionary's entries.
+ * Such string may be passed back to av_dict_parse_string().
+ * @note String is escaped with backslashes ('\').
+ *
+ * @param[in]  m             dictionary
+ * @param[out] buffer        Pointer to buffer that will be allocated with string containg entries.
+ *                           Buffer must be freed by the caller when is no longer needed.
+ * @param[in]  key_val_sep   character used to separate key from value
+ * @param[in]  pairs_sep     character used to separate two pairs from each other
+ * @return                   >= 0 on success, negative on error
+ * @warning Separators cannot be neither '\\' nor '\0'. They also cannot be the same.
+ */
+int av_dict_get_string(const AVDictionary *m, char **buffer,
+                       const char key_val_sep, const char pairs_sep);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_DICT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/display.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/display.h
new file mode 100644
index 0000000..515adad
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/display.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2014 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Display matrix
+ */
+
+#ifndef AVUTIL_DISPLAY_H
+#define AVUTIL_DISPLAY_H
+
+#include <stdint.h>
+#include "common.h"
+
+/**
+ * @addtogroup lavu_video
+ * @{
+ *
+ * @defgroup lavu_video_display Display transformation matrix functions
+ * @{
+ */
+
+/**
+ * @addtogroup lavu_video_display
+ * The display transformation matrix specifies an affine transformation that
+ * should be applied to video frames for correct presentation. It is compatible
+ * with the matrices stored in the ISO/IEC 14496-12 container format.
+ *
+ * The data is a 3x3 matrix represented as a 9-element array:
+ *
+ * @code{.unparsed}
+ *                                  | a b u |
+ *   (a, b, u, c, d, v, x, y, w) -> | c d v |
+ *                                  | x y w |
+ * @endcode
+ *
+ * All numbers are stored in native endianness, as 16.16 fixed-point values,
+ * except for u, v and w, which are stored as 2.30 fixed-point values.
+ *
+ * The transformation maps a point (p, q) in the source (pre-transformation)
+ * frame to the point (p', q') in the destination (post-transformation) frame as
+ * follows:
+ *
+ * @code{.unparsed}
+ *               | a b u |
+ *   (p, q, 1) . | c d v | = z * (p', q', 1)
+ *               | x y w |
+ * @endcode
+ *
+ * The transformation can also be more explicitly written in components as
+ * follows:
+ *
+ * @code{.unparsed}
+ *   p' = (a * p + c * q + x) / z;
+ *   q' = (b * p + d * q + y) / z;
+ *   z  =  u * p + v * q + w
+ * @endcode
+ */
+
+/**
+ * Extract the rotation component of the transformation matrix.
+ *
+ * @param matrix the transformation matrix
+ * @return the angle (in degrees) by which the transformation rotates the frame
+ *         counterclockwise. The angle will be in range [-180.0, 180.0],
+ *         or NaN if the matrix is singular.
+ *
+ * @note floating point numbers are inherently inexact, so callers are
+ *       recommended to round the return value to nearest integer before use.
+ */
+double av_display_rotation_get(const int32_t matrix[9]);
+
+/**
+ * Initialize a transformation matrix describing a pure counterclockwise
+ * rotation by the specified angle (in degrees).
+ *
+ * @param matrix an allocated transformation matrix (will be fully overwritten
+ *               by this function)
+ * @param angle rotation angle in degrees.
+ */
+void av_display_rotation_set(int32_t matrix[9], double angle);
+
+/**
+ * Flip the input matrix horizontally and/or vertically.
+ *
+ * @param matrix an allocated transformation matrix
+ * @param hflip whether the matrix should be flipped horizontally
+ * @param vflip whether the matrix should be flipped vertically
+ */
+void av_display_matrix_flip(int32_t matrix[9], int hflip, int vflip);
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_DISPLAY_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/downmix_info.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/downmix_info.h
new file mode 100644
index 0000000..221cf5b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/downmix_info.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014 Tim Walker <tdskywalker@gmail.com>
+ *
+ * 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 AVUTIL_DOWNMIX_INFO_H
+#define AVUTIL_DOWNMIX_INFO_H
+
+#include "frame.h"
+
+/**
+ * @file
+ * audio downmix medatata
+ */
+
+/**
+ * @addtogroup lavu_audio
+ * @{
+ */
+
+/**
+ * @defgroup downmix_info Audio downmix metadata
+ * @{
+ */
+
+/**
+ * Possible downmix types.
+ */
+enum AVDownmixType {
+    AV_DOWNMIX_TYPE_UNKNOWN, /**< Not indicated. */
+    AV_DOWNMIX_TYPE_LORO,    /**< Lo/Ro 2-channel downmix (Stereo). */
+    AV_DOWNMIX_TYPE_LTRT,    /**< Lt/Rt 2-channel downmix, Dolby Surround compatible. */
+    AV_DOWNMIX_TYPE_DPLII,   /**< Lt/Rt 2-channel downmix, Dolby Pro Logic II compatible. */
+    AV_DOWNMIX_TYPE_NB       /**< Number of downmix types. Not part of ABI. */
+};
+
+/**
+ * This structure describes optional metadata relevant to a downmix procedure.
+ *
+ * All fields are set by the decoder to the value indicated in the audio
+ * bitstream (if present), or to a "sane" default otherwise.
+ */
+typedef struct AVDownmixInfo {
+    /**
+     * Type of downmix preferred by the mastering engineer.
+     */
+    enum AVDownmixType preferred_downmix_type;
+
+    /**
+     * Absolute scale factor representing the nominal level of the center
+     * channel during a regular downmix.
+     */
+    double center_mix_level;
+
+    /**
+     * Absolute scale factor representing the nominal level of the center
+     * channel during an Lt/Rt compatible downmix.
+     */
+    double center_mix_level_ltrt;
+
+    /**
+     * Absolute scale factor representing the nominal level of the surround
+     * channels during a regular downmix.
+     */
+    double surround_mix_level;
+
+    /**
+     * Absolute scale factor representing the nominal level of the surround
+     * channels during an Lt/Rt compatible downmix.
+     */
+    double surround_mix_level_ltrt;
+
+    /**
+     * Absolute scale factor representing the level at which the LFE data is
+     * mixed into L/R channels during downmixing.
+     */
+    double lfe_mix_level;
+} AVDownmixInfo;
+
+/**
+ * Get a frame's AV_FRAME_DATA_DOWNMIX_INFO side data for editing.
+ *
+ * If the side data is absent, it is created and added to the frame.
+ *
+ * @param frame the frame for which the side data is to be obtained or created
+ *
+ * @return the AVDownmixInfo structure to be edited by the caller, or NULL if
+ *         the structure cannot be allocated.
+ */
+AVDownmixInfo *av_downmix_info_update_side_data(AVFrame *frame);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_DOWNMIX_INFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/encryption_info.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/encryption_info.h
new file mode 100644
index 0000000..8fe7ebf
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/encryption_info.h
@@ -0,0 +1,205 @@
+/**
+ * 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 AVUTIL_ENCRYPTION_INFO_H
+#define AVUTIL_ENCRYPTION_INFO_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct AVSubsampleEncryptionInfo {
+    /** The number of bytes that are clear. */
+    unsigned int bytes_of_clear_data;
+
+    /**
+     * The number of bytes that are protected.  If using pattern encryption,
+     * the pattern applies to only the protected bytes; if not using pattern
+     * encryption, all these bytes are encrypted.
+     */
+    unsigned int bytes_of_protected_data;
+} AVSubsampleEncryptionInfo;
+
+/**
+ * This describes encryption info for a packet.  This contains frame-specific
+ * info for how to decrypt the packet before passing it to the decoder.
+ *
+ * The size of this struct is not part of the public ABI.
+ */
+typedef struct AVEncryptionInfo {
+    /** The fourcc encryption scheme, in big-endian byte order. */
+    uint32_t scheme;
+
+    /**
+     * Only used for pattern encryption.  This is the number of 16-byte blocks
+     * that are encrypted.
+     */
+    uint32_t crypt_byte_block;
+
+    /**
+     * Only used for pattern encryption.  This is the number of 16-byte blocks
+     * that are clear.
+     */
+    uint32_t skip_byte_block;
+
+    /**
+     * The ID of the key used to encrypt the packet.  This should always be
+     * 16 bytes long, but may be changed in the future.
+     */
+    uint8_t *key_id;
+    uint32_t key_id_size;
+
+    /**
+     * The initialization vector.  This may have been zero-filled to be the
+     * correct block size.  This should always be 16 bytes long, but may be
+     * changed in the future.
+     */
+    uint8_t *iv;
+    uint32_t iv_size;
+
+    /**
+     * An array of subsample encryption info specifying how parts of the sample
+     * are encrypted.  If there are no subsamples, then the whole sample is
+     * encrypted.
+     */
+    AVSubsampleEncryptionInfo *subsamples;
+    uint32_t subsample_count;
+} AVEncryptionInfo;
+
+/**
+ * This describes info used to initialize an encryption key system.
+ *
+ * The size of this struct is not part of the public ABI.
+ */
+typedef struct AVEncryptionInitInfo {
+    /**
+     * A unique identifier for the key system this is for, can be NULL if it
+     * is not known.  This should always be 16 bytes, but may change in the
+     * future.
+     */
+    uint8_t* system_id;
+    uint32_t system_id_size;
+
+    /**
+     * An array of key IDs this initialization data is for.  All IDs are the
+     * same length.  Can be NULL if there are no known key IDs.
+     */
+    uint8_t** key_ids;
+    /** The number of key IDs. */
+    uint32_t num_key_ids;
+    /**
+     * The number of bytes in each key ID.  This should always be 16, but may
+     * change in the future.
+     */
+    uint32_t key_id_size;
+
+    /**
+     * Key-system specific initialization data.  This data is copied directly
+     * from the file and the format depends on the specific key system.  This
+     * can be NULL if there is no initialization data; in that case, there
+     * will be at least one key ID.
+     */
+    uint8_t* data;
+    uint32_t data_size;
+
+    /**
+     * An optional pointer to the next initialization info in the list.
+     */
+    struct AVEncryptionInitInfo *next;
+} AVEncryptionInitInfo;
+
+/**
+ * Allocates an AVEncryptionInfo structure and sub-pointers to hold the given
+ * number of subsamples.  This will allocate pointers for the key ID, IV,
+ * and subsample entries, set the size members, and zero-initialize the rest.
+ *
+ * @param subsample_count The number of subsamples.
+ * @param key_id_size The number of bytes in the key ID, should be 16.
+ * @param iv_size The number of bytes in the IV, should be 16.
+ *
+ * @return The new AVEncryptionInfo structure, or NULL on error.
+ */
+AVEncryptionInfo *av_encryption_info_alloc(uint32_t subsample_count, uint32_t key_id_size, uint32_t iv_size);
+
+/**
+ * Allocates an AVEncryptionInfo structure with a copy of the given data.
+ * @return The new AVEncryptionInfo structure, or NULL on error.
+ */
+AVEncryptionInfo *av_encryption_info_clone(const AVEncryptionInfo *info);
+
+/**
+ * Frees the given encryption info object.  This MUST NOT be used to free the
+ * side-data data pointer, that should use normal side-data methods.
+ */
+void av_encryption_info_free(AVEncryptionInfo *info);
+
+/**
+ * Creates a copy of the AVEncryptionInfo that is contained in the given side
+ * data.  The resulting object should be passed to av_encryption_info_free()
+ * when done.
+ *
+ * @return The new AVEncryptionInfo structure, or NULL on error.
+ */
+AVEncryptionInfo *av_encryption_info_get_side_data(const uint8_t *side_data, size_t side_data_size);
+
+/**
+ * Allocates and initializes side data that holds a copy of the given encryption
+ * info.  The resulting pointer should be either freed using av_free or given
+ * to av_packet_add_side_data().
+ *
+ * @return The new side-data pointer, or NULL.
+ */
+uint8_t *av_encryption_info_add_side_data(
+      const AVEncryptionInfo *info, size_t *side_data_size);
+
+
+/**
+ * Allocates an AVEncryptionInitInfo structure and sub-pointers to hold the
+ * given sizes.  This will allocate pointers and set all the fields.
+ *
+ * @return The new AVEncryptionInitInfo structure, or NULL on error.
+ */
+AVEncryptionInitInfo *av_encryption_init_info_alloc(
+    uint32_t system_id_size, uint32_t num_key_ids, uint32_t key_id_size, uint32_t data_size);
+
+/**
+ * Frees the given encryption init info object.  This MUST NOT be used to free
+ * the side-data data pointer, that should use normal side-data methods.
+ */
+void av_encryption_init_info_free(AVEncryptionInitInfo* info);
+
+/**
+ * Creates a copy of the AVEncryptionInitInfo that is contained in the given
+ * side data.  The resulting object should be passed to
+ * av_encryption_init_info_free() when done.
+ *
+ * @return The new AVEncryptionInitInfo structure, or NULL on error.
+ */
+AVEncryptionInitInfo *av_encryption_init_info_get_side_data(
+    const uint8_t* side_data, size_t side_data_size);
+
+/**
+ * Allocates and initializes side data that holds a copy of the given encryption
+ * init info.  The resulting pointer should be either freed using av_free or
+ * given to av_packet_add_side_data().
+ *
+ * @return The new side-data pointer, or NULL.
+ */
+uint8_t *av_encryption_init_info_add_side_data(
+    const AVEncryptionInitInfo *info, size_t *side_data_size);
+
+#endif /* AVUTIL_ENCRYPTION_INFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/error.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/error.h
new file mode 100644
index 0000000..71df4da
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/error.h
@@ -0,0 +1,126 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * error code definitions
+ */
+
+#ifndef AVUTIL_ERROR_H
+#define AVUTIL_ERROR_H
+
+#include <errno.h>
+#include <stddef.h>
+
+/**
+ * @addtogroup lavu_error
+ *
+ * @{
+ */
+
+
+/* error handling */
+#if EDOM > 0
+#define AVERROR(e) (-(e))   ///< Returns a negative error code from a POSIX error code, to return from library functions.
+#define AVUNERROR(e) (-(e)) ///< Returns a POSIX error code from a library function error return value.
+#else
+/* Some platforms have E* and errno already negated. */
+#define AVERROR(e) (e)
+#define AVUNERROR(e) (e)
+#endif
+
+#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d))
+
+#define AVERROR_BSF_NOT_FOUND      FFERRTAG(0xF8,'B','S','F') ///< Bitstream filter not found
+#define AVERROR_BUG                FFERRTAG( 'B','U','G','!') ///< Internal bug, also see AVERROR_BUG2
+#define AVERROR_BUFFER_TOO_SMALL   FFERRTAG( 'B','U','F','S') ///< Buffer too small
+#define AVERROR_DECODER_NOT_FOUND  FFERRTAG(0xF8,'D','E','C') ///< Decoder not found
+#define AVERROR_DEMUXER_NOT_FOUND  FFERRTAG(0xF8,'D','E','M') ///< Demuxer not found
+#define AVERROR_ENCODER_NOT_FOUND  FFERRTAG(0xF8,'E','N','C') ///< Encoder not found
+#define AVERROR_EOF                FFERRTAG( 'E','O','F',' ') ///< End of file
+#define AVERROR_EXIT               FFERRTAG( 'E','X','I','T') ///< Immediate exit was requested; the called function should not be restarted
+#define AVERROR_EXTERNAL           FFERRTAG( 'E','X','T',' ') ///< Generic error in an external library
+#define AVERROR_FILTER_NOT_FOUND   FFERRTAG(0xF8,'F','I','L') ///< Filter not found
+#define AVERROR_INVALIDDATA        FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input
+#define AVERROR_MUXER_NOT_FOUND    FFERRTAG(0xF8,'M','U','X') ///< Muxer not found
+#define AVERROR_OPTION_NOT_FOUND   FFERRTAG(0xF8,'O','P','T') ///< Option not found
+#define AVERROR_PATCHWELCOME       FFERRTAG( 'P','A','W','E') ///< Not yet implemented in FFmpeg, patches welcome
+#define AVERROR_PROTOCOL_NOT_FOUND FFERRTAG(0xF8,'P','R','O') ///< Protocol not found
+
+#define AVERROR_STREAM_NOT_FOUND   FFERRTAG(0xF8,'S','T','R') ///< Stream not found
+/**
+ * This is semantically identical to AVERROR_BUG
+ * it has been introduced in Libav after our AVERROR_BUG and with a modified value.
+ */
+#define AVERROR_BUG2               FFERRTAG( 'B','U','G',' ')
+#define AVERROR_UNKNOWN            FFERRTAG( 'U','N','K','N') ///< Unknown error, typically from an external library
+#define AVERROR_EXPERIMENTAL       (-0x2bb2afa8) ///< Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it.
+#define AVERROR_INPUT_CHANGED      (-0x636e6701) ///< Input changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_OUTPUT_CHANGED)
+#define AVERROR_OUTPUT_CHANGED     (-0x636e6702) ///< Output changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_INPUT_CHANGED)
+/* HTTP & RTSP errors */
+#define AVERROR_HTTP_BAD_REQUEST   FFERRTAG(0xF8,'4','0','0')
+#define AVERROR_HTTP_UNAUTHORIZED  FFERRTAG(0xF8,'4','0','1')
+#define AVERROR_HTTP_FORBIDDEN     FFERRTAG(0xF8,'4','0','3')
+#define AVERROR_HTTP_NOT_FOUND     FFERRTAG(0xF8,'4','0','4')
+#define AVERROR_HTTP_OTHER_4XX     FFERRTAG(0xF8,'4','X','X')
+#define AVERROR_HTTP_SERVER_ERROR  FFERRTAG(0xF8,'5','X','X')
+
+#define AV_ERROR_MAX_STRING_SIZE 64
+
+/**
+ * Put a description of the AVERROR code errnum in errbuf.
+ * In case of failure the global variable errno is set to indicate the
+ * error. Even in case of failure av_strerror() will print a generic
+ * error message indicating the errnum provided to errbuf.
+ *
+ * @param errnum      error code to describe
+ * @param errbuf      buffer to which description is written
+ * @param errbuf_size the size in bytes of errbuf
+ * @return 0 on success, a negative value if a description for errnum
+ * cannot be found
+ */
+int av_strerror(int errnum, char *errbuf, size_t errbuf_size);
+
+/**
+ * Fill the provided buffer with a string containing an error string
+ * corresponding to the AVERROR code errnum.
+ *
+ * @param errbuf         a buffer
+ * @param errbuf_size    size in bytes of errbuf
+ * @param errnum         error code to describe
+ * @return the buffer in input, filled with the error description
+ * @see av_strerror()
+ */
+static inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum)
+{
+    av_strerror(errnum, errbuf, errbuf_size);
+    return errbuf;
+}
+
+/**
+ * Convenience macro, the return value should be used only directly in
+ * function arguments but never stand-alone.
+ */
+#define av_err2str(errnum) \
+    av_make_error_string((char[AV_ERROR_MAX_STRING_SIZE]){0}, AV_ERROR_MAX_STRING_SIZE, errnum)
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_ERROR_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/eval.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/eval.h
new file mode 100644
index 0000000..dacd22b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/eval.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * simple arithmetic expression evaluator
+ */
+
+#ifndef AVUTIL_EVAL_H
+#define AVUTIL_EVAL_H
+
+#include "avutil.h"
+
+typedef struct AVExpr AVExpr;
+
+/**
+ * Parse and evaluate an expression.
+ * Note, this is significantly slower than av_expr_eval().
+ *
+ * @param res a pointer to a double where is put the result value of
+ * the expression, or NAN in case of error
+ * @param s expression as a zero terminated string, for example "1+2^3+5*5+sin(2/3)"
+ * @param const_names NULL terminated array of zero terminated strings of constant identifiers, for example {"PI", "E", 0}
+ * @param const_values a zero terminated array of values for the identifiers from const_names
+ * @param func1_names NULL terminated array of zero terminated strings of funcs1 identifiers
+ * @param funcs1 NULL terminated array of function pointers for functions which take 1 argument
+ * @param func2_names NULL terminated array of zero terminated strings of funcs2 identifiers
+ * @param funcs2 NULL terminated array of function pointers for functions which take 2 arguments
+ * @param opaque a pointer which will be passed to all functions from funcs1 and funcs2
+ * @param log_ctx parent logging context
+ * @return >= 0 in case of success, a negative value corresponding to an
+ * AVERROR code otherwise
+ */
+int av_expr_parse_and_eval(double *res, const char *s,
+                           const char * const *const_names, const double *const_values,
+                           const char * const *func1_names, double (* const *funcs1)(void *, double),
+                           const char * const *func2_names, double (* const *funcs2)(void *, double, double),
+                           void *opaque, int log_offset, void *log_ctx);
+
+/**
+ * Parse an expression.
+ *
+ * @param expr a pointer where is put an AVExpr containing the parsed
+ * value in case of successful parsing, or NULL otherwise.
+ * The pointed to AVExpr must be freed with av_expr_free() by the user
+ * when it is not needed anymore.
+ * @param s expression as a zero terminated string, for example "1+2^3+5*5+sin(2/3)"
+ * @param const_names NULL terminated array of zero terminated strings of constant identifiers, for example {"PI", "E", 0}
+ * @param func1_names NULL terminated array of zero terminated strings of funcs1 identifiers
+ * @param funcs1 NULL terminated array of function pointers for functions which take 1 argument
+ * @param func2_names NULL terminated array of zero terminated strings of funcs2 identifiers
+ * @param funcs2 NULL terminated array of function pointers for functions which take 2 arguments
+ * @param log_ctx parent logging context
+ * @return >= 0 in case of success, a negative value corresponding to an
+ * AVERROR code otherwise
+ */
+int av_expr_parse(AVExpr **expr, const char *s,
+                  const char * const *const_names,
+                  const char * const *func1_names, double (* const *funcs1)(void *, double),
+                  const char * const *func2_names, double (* const *funcs2)(void *, double, double),
+                  int log_offset, void *log_ctx);
+
+/**
+ * Evaluate a previously parsed expression.
+ *
+ * @param const_values a zero terminated array of values for the identifiers from av_expr_parse() const_names
+ * @param opaque a pointer which will be passed to all functions from funcs1 and funcs2
+ * @return the value of the expression
+ */
+double av_expr_eval(AVExpr *e, const double *const_values, void *opaque);
+
+/**
+ * Free a parsed expression previously created with av_expr_parse().
+ */
+void av_expr_free(AVExpr *e);
+
+/**
+ * Parse the string in numstr and return its value as a double. If
+ * the string is empty, contains only whitespaces, or does not contain
+ * an initial substring that has the expected syntax for a
+ * floating-point number, no conversion is performed. In this case,
+ * returns a value of zero and the value returned in tail is the value
+ * of numstr.
+ *
+ * @param numstr a string representing a number, may contain one of
+ * the International System number postfixes, for example 'K', 'M',
+ * 'G'. If 'i' is appended after the postfix, powers of 2 are used
+ * instead of powers of 10. The 'B' postfix multiplies the value by
+ * 8, and can be appended after another postfix or used alone. This
+ * allows using for example 'KB', 'MiB', 'G' and 'B' as postfix.
+ * @param tail if non-NULL puts here the pointer to the char next
+ * after the last parsed character
+ */
+double av_strtod(const char *numstr, char **tail);
+
+#endif /* AVUTIL_EVAL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ffversion.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ffversion.h
new file mode 100644
index 0000000..c5bec5d
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ffversion.h
@@ -0,0 +1,5 @@
+/* Automatically generated by version.sh, do not manually edit! */
+#ifndef AVUTIL_FFVERSION_H
+#define AVUTIL_FFVERSION_H
+#define FFMPEG_VERSION "4.1.3-1+build1"
+#endif /* AVUTIL_FFVERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/fifo.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/fifo.h
new file mode 100644
index 0000000..dc7bc6f
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/fifo.h
@@ -0,0 +1,179 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * a very simple circular buffer FIFO implementation
+ */
+
+#ifndef AVUTIL_FIFO_H
+#define AVUTIL_FIFO_H
+
+#include <stdint.h>
+#include "avutil.h"
+#include "attributes.h"
+
+typedef struct AVFifoBuffer {
+    uint8_t *buffer;
+    uint8_t *rptr, *wptr, *end;
+    uint32_t rndx, wndx;
+} AVFifoBuffer;
+
+/**
+ * Initialize an AVFifoBuffer.
+ * @param size of FIFO
+ * @return AVFifoBuffer or NULL in case of memory allocation failure
+ */
+AVFifoBuffer *av_fifo_alloc(unsigned int size);
+
+/**
+ * Initialize an AVFifoBuffer.
+ * @param nmemb number of elements
+ * @param size  size of the single element
+ * @return AVFifoBuffer or NULL in case of memory allocation failure
+ */
+AVFifoBuffer *av_fifo_alloc_array(size_t nmemb, size_t size);
+
+/**
+ * Free an AVFifoBuffer.
+ * @param f AVFifoBuffer to free
+ */
+void av_fifo_free(AVFifoBuffer *f);
+
+/**
+ * Free an AVFifoBuffer and reset pointer to NULL.
+ * @param f AVFifoBuffer to free
+ */
+void av_fifo_freep(AVFifoBuffer **f);
+
+/**
+ * Reset the AVFifoBuffer to the state right after av_fifo_alloc, in particular it is emptied.
+ * @param f AVFifoBuffer to reset
+ */
+void av_fifo_reset(AVFifoBuffer *f);
+
+/**
+ * Return the amount of data in bytes in the AVFifoBuffer, that is the
+ * amount of data you can read from it.
+ * @param f AVFifoBuffer to read from
+ * @return size
+ */
+int av_fifo_size(const AVFifoBuffer *f);
+
+/**
+ * Return the amount of space in bytes in the AVFifoBuffer, that is the
+ * amount of data you can write into it.
+ * @param f AVFifoBuffer to write into
+ * @return size
+ */
+int av_fifo_space(const AVFifoBuffer *f);
+
+/**
+ * Feed data at specific position from an AVFifoBuffer to a user-supplied callback.
+ * Similar as av_fifo_gereric_read but without discarding data.
+ * @param f AVFifoBuffer to read from
+ * @param offset offset from current read position
+ * @param buf_size number of bytes to read
+ * @param func generic read function
+ * @param dest data destination
+ */
+int av_fifo_generic_peek_at(AVFifoBuffer *f, void *dest, int offset, int buf_size, void (*func)(void*, void*, int));
+
+/**
+ * Feed data from an AVFifoBuffer to a user-supplied callback.
+ * Similar as av_fifo_gereric_read but without discarding data.
+ * @param f AVFifoBuffer to read from
+ * @param buf_size number of bytes to read
+ * @param func generic read function
+ * @param dest data destination
+ */
+int av_fifo_generic_peek(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int));
+
+/**
+ * Feed data from an AVFifoBuffer to a user-supplied callback.
+ * @param f AVFifoBuffer to read from
+ * @param buf_size number of bytes to read
+ * @param func generic read function
+ * @param dest data destination
+ */
+int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int));
+
+/**
+ * Feed data from a user-supplied callback to an AVFifoBuffer.
+ * @param f AVFifoBuffer to write to
+ * @param src data source; non-const since it may be used as a
+ * modifiable context by the function defined in func
+ * @param size number of bytes to write
+ * @param func generic write function; the first parameter is src,
+ * the second is dest_buf, the third is dest_buf_size.
+ * func must return the number of bytes written to dest_buf, or <= 0 to
+ * indicate no more data available to write.
+ * If func is NULL, src is interpreted as a simple byte array for source data.
+ * @return the number of bytes written to the FIFO
+ */
+int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int));
+
+/**
+ * Resize an AVFifoBuffer.
+ * In case of reallocation failure, the old FIFO is kept unchanged.
+ *
+ * @param f AVFifoBuffer to resize
+ * @param size new AVFifoBuffer size in bytes
+ * @return <0 for failure, >=0 otherwise
+ */
+int av_fifo_realloc2(AVFifoBuffer *f, unsigned int size);
+
+/**
+ * Enlarge an AVFifoBuffer.
+ * In case of reallocation failure, the old FIFO is kept unchanged.
+ * The new fifo size may be larger than the requested size.
+ *
+ * @param f AVFifoBuffer to resize
+ * @param additional_space the amount of space in bytes to allocate in addition to av_fifo_size()
+ * @return <0 for failure, >=0 otherwise
+ */
+int av_fifo_grow(AVFifoBuffer *f, unsigned int additional_space);
+
+/**
+ * Read and discard the specified amount of data from an AVFifoBuffer.
+ * @param f AVFifoBuffer to read from
+ * @param size amount of data to read in bytes
+ */
+void av_fifo_drain(AVFifoBuffer *f, int size);
+
+/**
+ * Return a pointer to the data stored in a FIFO buffer at a certain offset.
+ * The FIFO buffer is not modified.
+ *
+ * @param f    AVFifoBuffer to peek at, f must be non-NULL
+ * @param offs an offset in bytes, its absolute value must be less
+ *             than the used buffer size or the returned pointer will
+ *             point outside to the buffer data.
+ *             The used buffer size can be checked with av_fifo_size().
+ */
+static inline uint8_t *av_fifo_peek2(const AVFifoBuffer *f, int offs)
+{
+    uint8_t *ptr = f->rptr + offs;
+    if (ptr >= f->end)
+        ptr = f->buffer + (ptr - f->end);
+    else if (ptr < f->buffer)
+        ptr = f->end - (f->buffer - ptr);
+    return ptr;
+}
+
+#endif /* AVUTIL_FIFO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/file.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/file.h
new file mode 100644
index 0000000..3ef4a60
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/file.h
@@ -0,0 +1,71 @@
+/*
+ * 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 AVUTIL_FILE_H
+#define AVUTIL_FILE_H
+
+#include <stdint.h>
+
+#include "avutil.h"
+
+/**
+ * @file
+ * Misc file utilities.
+ */
+
+/**
+ * Read the file with name filename, and put its content in a newly
+ * allocated buffer or map it with mmap() when available.
+ * In case of success set *bufptr to the read or mmapped buffer, and
+ * *size to the size in bytes of the buffer in *bufptr.
+ * Unlike mmap this function succeeds with zero sized files, in this
+ * case *bufptr will be set to NULL and *size will be set to 0.
+ * The returned buffer must be released with av_file_unmap().
+ *
+ * @param log_offset loglevel offset used for logging
+ * @param log_ctx context used for logging
+ * @return a non negative number in case of success, a negative value
+ * corresponding to an AVERROR error code in case of failure
+ */
+av_warn_unused_result
+int av_file_map(const char *filename, uint8_t **bufptr, size_t *size,
+                int log_offset, void *log_ctx);
+
+/**
+ * Unmap or free the buffer bufptr created by av_file_map().
+ *
+ * @param size size in bytes of bufptr, must be the same as returned
+ * by av_file_map()
+ */
+void av_file_unmap(uint8_t *bufptr, size_t size);
+
+/**
+ * Wrapper to work around the lack of mkstemp() on mingw.
+ * Also, tries to create file in /tmp first, if possible.
+ * *prefix can be a character constant; *filename will be allocated internally.
+ * @return file descriptor of opened file (or negative value corresponding to an
+ * AVERROR code on error)
+ * and opened file name in **filename.
+ * @note On very old libcs it is necessary to set a secure umask before
+ *       calling this, av_tempfile() can't call umask itself as it is used in
+ *       libraries and could interfere with the calling application.
+ * @deprecated as fd numbers cannot be passed saftely between libs on some platforms
+ */
+int av_tempfile(const char *prefix, char **filename, int log_offset, void *log_ctx);
+
+#endif /* AVUTIL_FILE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/frame.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/frame.h
new file mode 100644
index 0000000..e2a2929
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/frame.h
@@ -0,0 +1,901 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_frame
+ * reference-counted frame API
+ */
+
+#ifndef AVUTIL_FRAME_H
+#define AVUTIL_FRAME_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "avutil.h"
+#include "buffer.h"
+#include "dict.h"
+#include "rational.h"
+#include "samplefmt.h"
+#include "pixfmt.h"
+#include "version.h"
+
+
+/**
+ * @defgroup lavu_frame AVFrame
+ * @ingroup lavu_data
+ *
+ * @{
+ * AVFrame is an abstraction for reference-counted raw multimedia data.
+ */
+
+enum AVFrameSideDataType {
+    /**
+     * The data is the AVPanScan struct defined in libavcodec.
+     */
+    AV_FRAME_DATA_PANSCAN,
+    /**
+     * ATSC A53 Part 4 Closed Captions.
+     * A53 CC bitstream is stored as uint8_t in AVFrameSideData.data.
+     * The number of bytes of CC data is AVFrameSideData.size.
+     */
+    AV_FRAME_DATA_A53_CC,
+    /**
+     * Stereoscopic 3d metadata.
+     * The data is the AVStereo3D struct defined in libavutil/stereo3d.h.
+     */
+    AV_FRAME_DATA_STEREO3D,
+    /**
+     * The data is the AVMatrixEncoding enum defined in libavutil/channel_layout.h.
+     */
+    AV_FRAME_DATA_MATRIXENCODING,
+    /**
+     * Metadata relevant to a downmix procedure.
+     * The data is the AVDownmixInfo struct defined in libavutil/downmix_info.h.
+     */
+    AV_FRAME_DATA_DOWNMIX_INFO,
+    /**
+     * ReplayGain information in the form of the AVReplayGain struct.
+     */
+    AV_FRAME_DATA_REPLAYGAIN,
+    /**
+     * This side data contains a 3x3 transformation matrix describing an affine
+     * transformation that needs to be applied to the frame for correct
+     * presentation.
+     *
+     * See libavutil/display.h for a detailed description of the data.
+     */
+    AV_FRAME_DATA_DISPLAYMATRIX,
+    /**
+     * Active Format Description data consisting of a single byte as specified
+     * in ETSI TS 101 154 using AVActiveFormatDescription enum.
+     */
+    AV_FRAME_DATA_AFD,
+    /**
+     * Motion vectors exported by some codecs (on demand through the export_mvs
+     * flag set in the libavcodec AVCodecContext flags2 option).
+     * The data is the AVMotionVector struct defined in
+     * libavutil/motion_vector.h.
+     */
+    AV_FRAME_DATA_MOTION_VECTORS,
+    /**
+     * Recommmends skipping the specified number of samples. This is exported
+     * only if the "skip_manual" AVOption is set in libavcodec.
+     * This has the same format as AV_PKT_DATA_SKIP_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_FRAME_DATA_SKIP_SAMPLES,
+    /**
+     * This side data must be associated with an audio frame and corresponds to
+     * enum AVAudioServiceType defined in avcodec.h.
+     */
+    AV_FRAME_DATA_AUDIO_SERVICE_TYPE,
+    /**
+     * Mastering display metadata associated with a video frame. The payload is
+     * an AVMasteringDisplayMetadata type and contains information about the
+     * mastering display color volume.
+     */
+    AV_FRAME_DATA_MASTERING_DISPLAY_METADATA,
+    /**
+     * The GOP timecode in 25 bit timecode format. Data format is 64-bit integer.
+     * This is set on the first frame of a GOP that has a temporal reference of 0.
+     */
+    AV_FRAME_DATA_GOP_TIMECODE,
+
+    /**
+     * The data represents the AVSphericalMapping structure defined in
+     * libavutil/spherical.h.
+     */
+    AV_FRAME_DATA_SPHERICAL,
+
+    /**
+     * Content light level (based on CTA-861.3). This payload contains data in
+     * the form of the AVContentLightMetadata struct.
+     */
+    AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,
+
+    /**
+     * The data contains an ICC profile as an opaque octet buffer following the
+     * format described by ISO 15076-1 with an optional name defined in the
+     * metadata key entry "name".
+     */
+    AV_FRAME_DATA_ICC_PROFILE,
+
+#if FF_API_FRAME_QP
+    /**
+     * Implementation-specific description of the format of AV_FRAME_QP_TABLE_DATA.
+     * The contents of this side data are undocumented and internal; use
+     * av_frame_set_qp_table() and av_frame_get_qp_table() to access this in a
+     * meaningful way instead.
+     */
+    AV_FRAME_DATA_QP_TABLE_PROPERTIES,
+
+    /**
+     * Raw QP table data. Its format is described by
+     * AV_FRAME_DATA_QP_TABLE_PROPERTIES. Use av_frame_set_qp_table() and
+     * av_frame_get_qp_table() to access this instead.
+     */
+    AV_FRAME_DATA_QP_TABLE_DATA,
+#endif
+
+    /**
+     * Timecode which conforms to SMPTE ST 12-1. The data is an array of 4 uint32_t
+     * where the first uint32_t describes how many (1-3) of the other timecodes are used.
+     * The timecode format is described in the av_timecode_get_smpte_from_framenum()
+     * function in libavutil/timecode.c.
+     */
+    AV_FRAME_DATA_S12M_TIMECODE,
+};
+
+enum AVActiveFormatDescription {
+    AV_AFD_SAME         = 8,
+    AV_AFD_4_3          = 9,
+    AV_AFD_16_9         = 10,
+    AV_AFD_14_9         = 11,
+    AV_AFD_4_3_SP_14_9  = 13,
+    AV_AFD_16_9_SP_14_9 = 14,
+    AV_AFD_SP_4_3       = 15,
+};
+
+
+/**
+ * Structure to hold side data for an AVFrame.
+ *
+ * sizeof(AVFrameSideData) is not a part of the public ABI, so new fields may be added
+ * to the end with a minor bump.
+ */
+typedef struct AVFrameSideData {
+    enum AVFrameSideDataType type;
+    uint8_t *data;
+    int      size;
+    AVDictionary *metadata;
+    AVBufferRef *buf;
+} AVFrameSideData;
+
+/**
+ * This structure describes decoded (raw) audio or video data.
+ *
+ * AVFrame must be allocated using av_frame_alloc(). Note that this only
+ * allocates the AVFrame itself, the buffers for the data must be managed
+ * through other means (see below).
+ * AVFrame must be freed with av_frame_free().
+ *
+ * AVFrame is typically allocated once and then reused multiple times to hold
+ * different data (e.g. a single AVFrame to hold frames received from a
+ * decoder). In such a case, av_frame_unref() will free any references held by
+ * the frame and reset it to its original clean state before it
+ * is reused again.
+ *
+ * The data described by an AVFrame is usually reference counted through the
+ * AVBuffer API. The underlying buffer references are stored in AVFrame.buf /
+ * AVFrame.extended_buf. An AVFrame is considered to be reference counted if at
+ * least one reference is set, i.e. if AVFrame.buf[0] != NULL. In such a case,
+ * every single data plane must be contained in one of the buffers in
+ * AVFrame.buf or AVFrame.extended_buf.
+ * There may be a single buffer for all the data, or one separate buffer for
+ * each plane, or anything in between.
+ *
+ * sizeof(AVFrame) is not a part of the public ABI, so new fields may be added
+ * to the end with a minor bump.
+ *
+ * Fields can be accessed through AVOptions, the name string used, matches the
+ * C structure field name for fields accessible through AVOptions. The AVClass
+ * for AVFrame can be obtained from avcodec_get_frame_class()
+ */
+typedef struct AVFrame {
+#define AV_NUM_DATA_POINTERS 8
+    /**
+     * pointer to the picture/channel planes.
+     * This might be different from the first allocated byte
+     *
+     * Some decoders access areas outside 0,0 - width,height, please
+     * see avcodec_align_dimensions2(). Some filters and swscale can read
+     * up to 16 bytes beyond the planes, if these filters are to be used,
+     * then 16 extra bytes must be allocated.
+     *
+     * NOTE: Except for hwaccel formats, pointers not needed by the format
+     * MUST be set to NULL.
+     */
+    uint8_t *data[AV_NUM_DATA_POINTERS];
+
+    /**
+     * For video, size in bytes of each picture line.
+     * For audio, size in bytes of each plane.
+     *
+     * For audio, only linesize[0] may be set. For planar audio, each channel
+     * plane must be the same size.
+     *
+     * For video the linesizes should be multiples of the CPUs alignment
+     * preference, this is 16 or 32 for modern desktop CPUs.
+     * Some code requires such alignment other code can be slower without
+     * correct alignment, for yet other it makes no difference.
+     *
+     * @note The linesize may be larger than the size of usable data -- there
+     * may be extra padding present for performance reasons.
+     */
+    int linesize[AV_NUM_DATA_POINTERS];
+
+    /**
+     * pointers to the data planes/channels.
+     *
+     * For video, this should simply point to data[].
+     *
+     * For planar audio, each channel has a separate data pointer, and
+     * linesize[0] contains the size of each channel buffer.
+     * For packed audio, there is just one data pointer, and linesize[0]
+     * contains the total size of the buffer for all channels.
+     *
+     * Note: Both data and extended_data should always be set in a valid frame,
+     * but for planar audio with more channels that can fit in data,
+     * extended_data must be used in order to access all channels.
+     */
+    uint8_t **extended_data;
+
+    /**
+     * @name Video dimensions
+     * Video frames only. The coded dimensions (in pixels) of the video frame,
+     * i.e. the size of the rectangle that contains some well-defined values.
+     *
+     * @note The part of the frame intended for display/presentation is further
+     * restricted by the @ref cropping "Cropping rectangle".
+     * @{
+     */
+    int width, height;
+    /**
+     * @}
+     */
+
+    /**
+     * number of audio samples (per channel) described by this frame
+     */
+    int nb_samples;
+
+    /**
+     * format of the frame, -1 if unknown or unset
+     * Values correspond to enum AVPixelFormat for video frames,
+     * enum AVSampleFormat for audio)
+     */
+    int format;
+
+    /**
+     * 1 -> keyframe, 0-> not
+     */
+    int key_frame;
+
+    /**
+     * Picture type of the frame.
+     */
+    enum AVPictureType pict_type;
+
+    /**
+     * Sample aspect ratio for the video frame, 0/1 if unknown/unspecified.
+     */
+    AVRational sample_aspect_ratio;
+
+    /**
+     * Presentation timestamp in time_base units (time when frame should be shown to user).
+     */
+    int64_t pts;
+
+#if FF_API_PKT_PTS
+    /**
+     * PTS copied from the AVPacket that was decoded to produce this frame.
+     * @deprecated use the pts field instead
+     */
+    attribute_deprecated
+    int64_t pkt_pts;
+#endif
+
+    /**
+     * DTS copied from the AVPacket that triggered returning this frame. (if frame threading isn't used)
+     * This is also the Presentation time of this AVFrame calculated from
+     * only AVPacket.dts values without pts values.
+     */
+    int64_t pkt_dts;
+
+    /**
+     * picture number in bitstream order
+     */
+    int coded_picture_number;
+    /**
+     * picture number in display order
+     */
+    int display_picture_number;
+
+    /**
+     * quality (between 1 (good) and FF_LAMBDA_MAX (bad))
+     */
+    int quality;
+
+    /**
+     * for some private data of the user
+     */
+    void *opaque;
+
+#if FF_API_ERROR_FRAME
+    /**
+     * @deprecated unused
+     */
+    attribute_deprecated
+    uint64_t error[AV_NUM_DATA_POINTERS];
+#endif
+
+    /**
+     * When decoding, this signals how much the picture must be delayed.
+     * extra_delay = repeat_pict / (2*fps)
+     */
+    int repeat_pict;
+
+    /**
+     * The content of the picture is interlaced.
+     */
+    int interlaced_frame;
+
+    /**
+     * If the content is interlaced, is top field displayed first.
+     */
+    int top_field_first;
+
+    /**
+     * Tell user application that palette has changed from previous frame.
+     */
+    int palette_has_changed;
+
+    /**
+     * reordered opaque 64 bits (generally an integer or a double precision float
+     * PTS but can be anything).
+     * The user sets AVCodecContext.reordered_opaque to represent the input at
+     * that time,
+     * the decoder reorders values as needed and sets AVFrame.reordered_opaque
+     * to exactly one of the values provided by the user through AVCodecContext.reordered_opaque
+     * @deprecated in favor of pkt_pts
+     */
+    int64_t reordered_opaque;
+
+    /**
+     * Sample rate of the audio data.
+     */
+    int sample_rate;
+
+    /**
+     * Channel layout of the audio data.
+     */
+    uint64_t channel_layout;
+
+    /**
+     * AVBuffer references backing the data for this frame. If all elements of
+     * this array are NULL, then this frame is not reference counted. This array
+     * must be filled contiguously -- if buf[i] is non-NULL then buf[j] must
+     * also be non-NULL for all j < i.
+     *
+     * There may be at most one AVBuffer per data plane, so for video this array
+     * always contains all the references. For planar audio with more than
+     * AV_NUM_DATA_POINTERS channels, there may be more buffers than can fit in
+     * this array. Then the extra AVBufferRef pointers are stored in the
+     * extended_buf array.
+     */
+    AVBufferRef *buf[AV_NUM_DATA_POINTERS];
+
+    /**
+     * For planar audio which requires more than AV_NUM_DATA_POINTERS
+     * AVBufferRef pointers, this array will hold all the references which
+     * cannot fit into AVFrame.buf.
+     *
+     * Note that this is different from AVFrame.extended_data, which always
+     * contains all the pointers. This array only contains the extra pointers,
+     * which cannot fit into AVFrame.buf.
+     *
+     * This array is always allocated using av_malloc() by whoever constructs
+     * the frame. It is freed in av_frame_unref().
+     */
+    AVBufferRef **extended_buf;
+    /**
+     * Number of elements in extended_buf.
+     */
+    int        nb_extended_buf;
+
+    AVFrameSideData **side_data;
+    int            nb_side_data;
+
+/**
+ * @defgroup lavu_frame_flags AV_FRAME_FLAGS
+ * @ingroup lavu_frame
+ * Flags describing additional frame properties.
+ *
+ * @{
+ */
+
+/**
+ * The frame data may be corrupted, e.g. due to decoding errors.
+ */
+#define AV_FRAME_FLAG_CORRUPT       (1 << 0)
+/**
+ * A flag to mark the frames which need to be decoded, but shouldn't be output.
+ */
+#define AV_FRAME_FLAG_DISCARD   (1 << 2)
+/**
+ * @}
+ */
+
+    /**
+     * Frame flags, a combination of @ref lavu_frame_flags
+     */
+    int flags;
+
+    /**
+     * MPEG vs JPEG YUV range.
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
+     */
+    enum AVColorRange color_range;
+
+    enum AVColorPrimaries color_primaries;
+
+    enum AVColorTransferCharacteristic color_trc;
+
+    /**
+     * YUV colorspace type.
+     * - encoding: Set by user
+     * - decoding: Set by libavcodec
+     */
+    enum AVColorSpace colorspace;
+
+    enum AVChromaLocation chroma_location;
+
+    /**
+     * frame timestamp estimated using various heuristics, in stream time base
+     * - encoding: unused
+     * - decoding: set by libavcodec, read by user.
+     */
+    int64_t best_effort_timestamp;
+
+    /**
+     * reordered pos from the last AVPacket that has been input into the decoder
+     * - encoding: unused
+     * - decoding: Read by user.
+     */
+    int64_t pkt_pos;
+
+    /**
+     * duration of the corresponding packet, expressed in
+     * AVStream->time_base units, 0 if unknown.
+     * - encoding: unused
+     * - decoding: Read by user.
+     */
+    int64_t pkt_duration;
+
+    /**
+     * metadata.
+     * - encoding: Set by user.
+     * - decoding: Set by libavcodec.
+     */
+    AVDictionary *metadata;
+
+    /**
+     * decode error flags of the frame, set to a combination of
+     * FF_DECODE_ERROR_xxx flags if the decoder produced a frame, but there
+     * were errors during the decoding.
+     * - encoding: unused
+     * - decoding: set by libavcodec, read by user.
+     */
+    int decode_error_flags;
+#define FF_DECODE_ERROR_INVALID_BITSTREAM   1
+#define FF_DECODE_ERROR_MISSING_REFERENCE   2
+
+    /**
+     * number of audio channels, only used for audio.
+     * - encoding: unused
+     * - decoding: Read by user.
+     */
+    int channels;
+
+    /**
+     * size of the corresponding packet containing the compressed
+     * frame.
+     * It is set to a negative value if unknown.
+     * - encoding: unused
+     * - decoding: set by libavcodec, read by user.
+     */
+    int pkt_size;
+
+#if FF_API_FRAME_QP
+    /**
+     * QP table
+     */
+    attribute_deprecated
+    int8_t *qscale_table;
+    /**
+     * QP store stride
+     */
+    attribute_deprecated
+    int qstride;
+
+    attribute_deprecated
+    int qscale_type;
+
+    attribute_deprecated
+    AVBufferRef *qp_table_buf;
+#endif
+    /**
+     * For hwaccel-format frames, this should be a reference to the
+     * AVHWFramesContext describing the frame.
+     */
+    AVBufferRef *hw_frames_ctx;
+
+    /**
+     * AVBufferRef for free use by the API user. FFmpeg will never check the
+     * contents of the buffer ref. FFmpeg calls av_buffer_unref() on it when
+     * the frame is unreferenced. av_frame_copy_props() calls create a new
+     * reference with av_buffer_ref() for the target frame's opaque_ref field.
+     *
+     * This is unrelated to the opaque field, although it serves a similar
+     * purpose.
+     */
+    AVBufferRef *opaque_ref;
+
+    /**
+     * @anchor cropping
+     * @name Cropping
+     * Video frames only. The number of pixels to discard from the the
+     * top/bottom/left/right border of the frame to obtain the sub-rectangle of
+     * the frame intended for presentation.
+     * @{
+     */
+    size_t crop_top;
+    size_t crop_bottom;
+    size_t crop_left;
+    size_t crop_right;
+    /**
+     * @}
+     */
+
+    /**
+     * AVBufferRef for internal use by a single libav* library.
+     * Must not be used to transfer data between libraries.
+     * Has to be NULL when ownership of the frame leaves the respective library.
+     *
+     * Code outside the FFmpeg libs should never check or change the contents of the buffer ref.
+     *
+     * FFmpeg calls av_buffer_unref() on it when the frame is unreferenced.
+     * av_frame_copy_props() calls create a new reference with av_buffer_ref()
+     * for the target frame's private_ref field.
+     */
+    AVBufferRef *private_ref;
+} AVFrame;
+
+#if FF_API_FRAME_GET_SET
+/**
+ * Accessors for some AVFrame fields. These used to be provided for ABI
+ * compatibility, and do not need to be used anymore.
+ */
+attribute_deprecated
+int64_t av_frame_get_best_effort_timestamp(const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_best_effort_timestamp(AVFrame *frame, int64_t val);
+attribute_deprecated
+int64_t av_frame_get_pkt_duration         (const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_pkt_duration         (AVFrame *frame, int64_t val);
+attribute_deprecated
+int64_t av_frame_get_pkt_pos              (const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_pkt_pos              (AVFrame *frame, int64_t val);
+attribute_deprecated
+int64_t av_frame_get_channel_layout       (const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_channel_layout       (AVFrame *frame, int64_t val);
+attribute_deprecated
+int     av_frame_get_channels             (const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_channels             (AVFrame *frame, int     val);
+attribute_deprecated
+int     av_frame_get_sample_rate          (const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_sample_rate          (AVFrame *frame, int     val);
+attribute_deprecated
+AVDictionary *av_frame_get_metadata       (const AVFrame *frame);
+attribute_deprecated
+void          av_frame_set_metadata       (AVFrame *frame, AVDictionary *val);
+attribute_deprecated
+int     av_frame_get_decode_error_flags   (const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_decode_error_flags   (AVFrame *frame, int     val);
+attribute_deprecated
+int     av_frame_get_pkt_size(const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_pkt_size(AVFrame *frame, int val);
+#if FF_API_FRAME_QP
+attribute_deprecated
+int8_t *av_frame_get_qp_table(AVFrame *f, int *stride, int *type);
+attribute_deprecated
+int av_frame_set_qp_table(AVFrame *f, AVBufferRef *buf, int stride, int type);
+#endif
+attribute_deprecated
+enum AVColorSpace av_frame_get_colorspace(const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_colorspace(AVFrame *frame, enum AVColorSpace val);
+attribute_deprecated
+enum AVColorRange av_frame_get_color_range(const AVFrame *frame);
+attribute_deprecated
+void    av_frame_set_color_range(AVFrame *frame, enum AVColorRange val);
+#endif
+
+/**
+ * Get the name of a colorspace.
+ * @return a static string identifying the colorspace; can be NULL.
+ */
+const char *av_get_colorspace_name(enum AVColorSpace val);
+
+/**
+ * Allocate an AVFrame and set its fields to default values.  The resulting
+ * struct must be freed using av_frame_free().
+ *
+ * @return An AVFrame filled with default values or NULL on failure.
+ *
+ * @note this only allocates the AVFrame itself, not the data buffers. Those
+ * must be allocated through other means, e.g. with av_frame_get_buffer() or
+ * manually.
+ */
+AVFrame *av_frame_alloc(void);
+
+/**
+ * Free the frame and any dynamically allocated objects in it,
+ * e.g. extended_data. If the frame is reference counted, it will be
+ * unreferenced first.
+ *
+ * @param frame frame to be freed. The pointer will be set to NULL.
+ */
+void av_frame_free(AVFrame **frame);
+
+/**
+ * Set up a new reference to the data described by the source frame.
+ *
+ * Copy frame properties from src to dst and create a new reference for each
+ * AVBufferRef from src.
+ *
+ * If src is not reference counted, new buffers are allocated and the data is
+ * copied.
+ *
+ * @warning: dst MUST have been either unreferenced with av_frame_unref(dst),
+ *           or newly allocated with av_frame_alloc() before calling this
+ *           function, or undefined behavior will occur.
+ *
+ * @return 0 on success, a negative AVERROR on error
+ */
+int av_frame_ref(AVFrame *dst, const AVFrame *src);
+
+/**
+ * Create a new frame that references the same data as src.
+ *
+ * This is a shortcut for av_frame_alloc()+av_frame_ref().
+ *
+ * @return newly created AVFrame on success, NULL on error.
+ */
+AVFrame *av_frame_clone(const AVFrame *src);
+
+/**
+ * Unreference all the buffers referenced by frame and reset the frame fields.
+ */
+void av_frame_unref(AVFrame *frame);
+
+/**
+ * Move everything contained in src to dst and reset src.
+ *
+ * @warning: dst is not unreferenced, but directly overwritten without reading
+ *           or deallocating its contents. Call av_frame_unref(dst) manually
+ *           before calling this function to ensure that no memory is leaked.
+ */
+void av_frame_move_ref(AVFrame *dst, AVFrame *src);
+
+/**
+ * Allocate new buffer(s) for audio or video data.
+ *
+ * The following fields must be set on frame before calling this function:
+ * - format (pixel format for video, sample format for audio)
+ * - width and height for video
+ * - nb_samples and channel_layout for audio
+ *
+ * This function will fill AVFrame.data and AVFrame.buf arrays and, if
+ * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
+ * For planar formats, one buffer will be allocated for each plane.
+ *
+ * @warning: if frame already has been allocated, calling this function will
+ *           leak memory. In addition, undefined behavior can occur in certain
+ *           cases.
+ *
+ * @param frame frame in which to store the new buffers.
+ * @param align Required buffer size alignment. If equal to 0, alignment will be
+ *              chosen automatically for the current CPU. It is highly
+ *              recommended to pass 0 here unless you know what you are doing.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_frame_get_buffer(AVFrame *frame, int align);
+
+/**
+ * Check if the frame data is writable.
+ *
+ * @return A positive value if the frame data is writable (which is true if and
+ * only if each of the underlying buffers has only one reference, namely the one
+ * stored in this frame). Return 0 otherwise.
+ *
+ * If 1 is returned the answer is valid until av_buffer_ref() is called on any
+ * of the underlying AVBufferRefs (e.g. through av_frame_ref() or directly).
+ *
+ * @see av_frame_make_writable(), av_buffer_is_writable()
+ */
+int av_frame_is_writable(AVFrame *frame);
+
+/**
+ * Ensure that the frame data is writable, avoiding data copy if possible.
+ *
+ * Do nothing if the frame is writable, allocate new buffers and copy the data
+ * if it is not.
+ *
+ * @return 0 on success, a negative AVERROR on error.
+ *
+ * @see av_frame_is_writable(), av_buffer_is_writable(),
+ * av_buffer_make_writable()
+ */
+int av_frame_make_writable(AVFrame *frame);
+
+/**
+ * Copy the frame data from src to dst.
+ *
+ * This function does not allocate anything, dst must be already initialized and
+ * allocated with the same parameters as src.
+ *
+ * This function only copies the frame data (i.e. the contents of the data /
+ * extended data arrays), not any other properties.
+ *
+ * @return >= 0 on success, a negative AVERROR on error.
+ */
+int av_frame_copy(AVFrame *dst, const AVFrame *src);
+
+/**
+ * Copy only "metadata" fields from src to dst.
+ *
+ * Metadata for the purpose of this function are those fields that do not affect
+ * the data layout in the buffers.  E.g. pts, sample rate (for audio) or sample
+ * aspect ratio (for video), but not width/height or channel layout.
+ * Side data is also copied.
+ */
+int av_frame_copy_props(AVFrame *dst, const AVFrame *src);
+
+/**
+ * Get the buffer reference a given data plane is stored in.
+ *
+ * @param plane index of the data plane of interest in frame->extended_data.
+ *
+ * @return the buffer reference that contains the plane or NULL if the input
+ * frame is not valid.
+ */
+AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane);
+
+/**
+ * Add a new side data to a frame.
+ *
+ * @param frame a frame to which the side data should be added
+ * @param type type of the added side data
+ * @param size size of the side data
+ *
+ * @return newly added side data on success, NULL on error
+ */
+AVFrameSideData *av_frame_new_side_data(AVFrame *frame,
+                                        enum AVFrameSideDataType type,
+                                        int size);
+
+/**
+ * Add a new side data to a frame from an existing AVBufferRef
+ *
+ * @param frame a frame to which the side data should be added
+ * @param type  the type of the added side data
+ * @param buf   an AVBufferRef to add as side data. The ownership of
+ *              the reference is transferred to the frame.
+ *
+ * @return newly added side data on success, NULL on error. On failure
+ *         the frame is unchanged and the AVBufferRef remains owned by
+ *         the caller.
+ */
+AVFrameSideData *av_frame_new_side_data_from_buf(AVFrame *frame,
+                                                 enum AVFrameSideDataType type,
+                                                 AVBufferRef *buf);
+
+/**
+ * @return a pointer to the side data of a given type on success, NULL if there
+ * is no side data with such type in this frame.
+ */
+AVFrameSideData *av_frame_get_side_data(const AVFrame *frame,
+                                        enum AVFrameSideDataType type);
+
+/**
+ * If side data of the supplied type exists in the frame, free it and remove it
+ * from the frame.
+ */
+void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type);
+
+
+/**
+ * Flags for frame cropping.
+ */
+enum {
+    /**
+     * Apply the maximum possible cropping, even if it requires setting the
+     * AVFrame.data[] entries to unaligned pointers. Passing unaligned data
+     * to FFmpeg API is generally not allowed, and causes undefined behavior
+     * (such as crashes). You can pass unaligned data only to FFmpeg APIs that
+     * are explicitly documented to accept it. Use this flag only if you
+     * absolutely know what you are doing.
+     */
+    AV_FRAME_CROP_UNALIGNED     = 1 << 0,
+};
+
+/**
+ * Crop the given video AVFrame according to its crop_left/crop_top/crop_right/
+ * crop_bottom fields. If cropping is successful, the function will adjust the
+ * data pointers and the width/height fields, and set the crop fields to 0.
+ *
+ * In all cases, the cropping boundaries will be rounded to the inherent
+ * alignment of the pixel format. In some cases, such as for opaque hwaccel
+ * formats, the left/top cropping is ignored. The crop fields are set to 0 even
+ * if the cropping was rounded or ignored.
+ *
+ * @param frame the frame which should be cropped
+ * @param flags Some combination of AV_FRAME_CROP_* flags, or 0.
+ *
+ * @return >= 0 on success, a negative AVERROR on error. If the cropping fields
+ * were invalid, AVERROR(ERANGE) is returned, and nothing is changed.
+ */
+int av_frame_apply_cropping(AVFrame *frame, int flags);
+
+/**
+ * @return a string identifying the side data type
+ */
+const char *av_frame_side_data_name(enum AVFrameSideDataType type);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_FRAME_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hash.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hash.h
new file mode 100644
index 0000000..7693e6b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hash.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2013 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_hash_generic
+ * Generic hashing API
+ */
+
+#ifndef AVUTIL_HASH_H
+#define AVUTIL_HASH_H
+
+#include <stdint.h>
+
+#include "version.h"
+
+/**
+ * @defgroup lavu_hash Hash Functions
+ * @ingroup lavu_crypto
+ * Hash functions useful in multimedia.
+ *
+ * Hash functions are widely used in multimedia, from error checking and
+ * concealment to internal regression testing. libavutil has efficient
+ * implementations of a variety of hash functions that may be useful for
+ * FFmpeg and other multimedia applications.
+ *
+ * @{
+ *
+ * @defgroup lavu_hash_generic Generic Hashing API
+ * An abstraction layer for all hash functions supported by libavutil.
+ *
+ * If your application needs to support a wide range of different hash
+ * functions, then the Generic Hashing API is for you. It provides a generic,
+ * reusable API for @ref lavu_hash "all hash functions" implemented in libavutil.
+ * If you just need to use one particular hash function, use the @ref lavu_hash
+ * "individual hash" directly.
+ *
+ * @section Sample Code
+ *
+ * A basic template for using the Generic Hashing API follows:
+ *
+ * @code
+ * struct AVHashContext *ctx = NULL;
+ * const char *hash_name = NULL;
+ * uint8_t *output_buf = NULL;
+ *
+ * // Select from a string returned by av_hash_names()
+ * hash_name = ...;
+ *
+ * // Allocate a hash context
+ * ret = av_hash_alloc(&ctx, hash_name);
+ * if (ret < 0)
+ *     return ret;
+ *
+ * // Initialize the hash context
+ * av_hash_init(ctx);
+ *
+ * // Update the hash context with data
+ * while (data_left) {
+ *     av_hash_update(ctx, data, size);
+ * }
+ *
+ * // Now we have no more data, so it is time to finalize the hash and get the
+ * // output. But we need to first allocate an output buffer. Note that you can
+ * // use any memory allocation function, including malloc(), not just
+ * // av_malloc().
+ * output_buf = av_malloc(av_hash_get_size(ctx));
+ * if (!output_buf)
+ *     return AVERROR(ENOMEM);
+ *
+ * // Finalize the hash context.
+ * // You can use any of the av_hash_final*() functions provided, for other
+ * // output formats. If you do so, be sure to adjust the memory allocation
+ * // above. See the function documentation below for the exact amount of extra
+ * // memory needed.
+ * av_hash_final(ctx, output_buffer);
+ *
+ * // Free the context
+ * av_hash_freep(&ctx);
+ * @endcode
+ *
+ * @section Hash Function-Specific Information
+ * If the CRC32 hash is selected, the #AV_CRC_32_IEEE polynomial will be
+ * used.
+ *
+ * If the Murmur3 hash is selected, the default seed will be used. See @ref
+ * lavu_murmur3_seedinfo "Murmur3" for more information.
+ *
+ * @{
+ */
+
+/**
+ * @example ffhash.c
+ * This example is a simple command line application that takes one or more
+ * arguments. It demonstrates a typical use of the hashing API with allocation,
+ * initialization, updating, and finalizing.
+ */
+
+struct AVHashContext;
+
+/**
+ * Allocate a hash context for the algorithm specified by name.
+ *
+ * @return  >= 0 for success, a negative error code for failure
+ *
+ * @note The context is not initialized after a call to this function; you must
+ * call av_hash_init() to do so.
+ */
+int av_hash_alloc(struct AVHashContext **ctx, const char *name);
+
+/**
+ * Get the names of available hash algorithms.
+ *
+ * This function can be used to enumerate the algorithms.
+ *
+ * @param[in] i  Index of the hash algorithm, starting from 0
+ * @return       Pointer to a static string or `NULL` if `i` is out of range
+ */
+const char *av_hash_names(int i);
+
+/**
+ * Get the name of the algorithm corresponding to the given hash context.
+ */
+const char *av_hash_get_name(const struct AVHashContext *ctx);
+
+/**
+ * Maximum value that av_hash_get_size() will currently return.
+ *
+ * You can use this if you absolutely want or need to use static allocation for
+ * the output buffer and are fine with not supporting hashes newly added to
+ * libavutil without recompilation.
+ *
+ * @warning
+ * Adding new hashes with larger sizes, and increasing the macro while doing
+ * so, will not be considered an ABI change. To prevent your code from
+ * overflowing a buffer, either dynamically allocate the output buffer with
+ * av_hash_get_size(), or limit your use of the Hashing API to hashes that are
+ * already in FFmpeg during the time of compilation.
+ */
+#define AV_HASH_MAX_SIZE 64
+
+/**
+ * Get the size of the resulting hash value in bytes.
+ *
+ * The maximum value this function will currently return is available as macro
+ * #AV_HASH_MAX_SIZE.
+ *
+ * @param[in]     ctx Hash context
+ * @return            Size of the hash value in bytes
+ */
+int av_hash_get_size(const struct AVHashContext *ctx);
+
+/**
+ * Initialize or reset a hash context.
+ *
+ * @param[in,out] ctx Hash context
+ */
+void av_hash_init(struct AVHashContext *ctx);
+
+/**
+ * Update a hash context with additional data.
+ *
+ * @param[in,out] ctx Hash context
+ * @param[in]     src Data to be added to the hash context
+ * @param[in]     len Size of the additional data
+ */
+#if FF_API_CRYPTO_SIZE_T
+void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, int len);
+#else
+void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, size_t len);
+#endif
+
+/**
+ * Finalize a hash context and compute the actual hash value.
+ *
+ * The minimum size of `dst` buffer is given by av_hash_get_size() or
+ * #AV_HASH_MAX_SIZE. The use of the latter macro is discouraged.
+ *
+ * It is not safe to update or finalize a hash context again, if it has already
+ * been finalized.
+ *
+ * @param[in,out] ctx Hash context
+ * @param[out]    dst Where the final hash value will be stored
+ *
+ * @see av_hash_final_bin() provides an alternative API
+ */
+void av_hash_final(struct AVHashContext *ctx, uint8_t *dst);
+
+/**
+ * Finalize a hash context and store the actual hash value in a buffer.
+ *
+ * It is not safe to update or finalize a hash context again, if it has already
+ * been finalized.
+ *
+ * If `size` is smaller than the hash size (given by av_hash_get_size()), the
+ * hash is truncated; if size is larger, the buffer is padded with 0.
+ *
+ * @param[in,out] ctx  Hash context
+ * @param[out]    dst  Where the final hash value will be stored
+ * @param[in]     size Number of bytes to write to `dst`
+ */
+void av_hash_final_bin(struct AVHashContext *ctx, uint8_t *dst, int size);
+
+/**
+ * Finalize a hash context and store the hexadecimal representation of the
+ * actual hash value as a string.
+ *
+ * It is not safe to update or finalize a hash context again, if it has already
+ * been finalized.
+ *
+ * The string is always 0-terminated.
+ *
+ * If `size` is smaller than `2 * hash_size + 1`, where `hash_size` is the
+ * value returned by av_hash_get_size(), the string will be truncated.
+ *
+ * @param[in,out] ctx  Hash context
+ * @param[out]    dst  Where the string will be stored
+ * @param[in]     size Maximum number of bytes to write to `dst`
+ */
+void av_hash_final_hex(struct AVHashContext *ctx, uint8_t *dst, int size);
+
+/**
+ * Finalize a hash context and store the Base64 representation of the
+ * actual hash value as a string.
+ *
+ * It is not safe to update or finalize a hash context again, if it has already
+ * been finalized.
+ *
+ * The string is always 0-terminated.
+ *
+ * If `size` is smaller than AV_BASE64_SIZE(hash_size), where `hash_size` is
+ * the value returned by av_hash_get_size(), the string will be truncated.
+ *
+ * @param[in,out] ctx  Hash context
+ * @param[out]    dst  Where the final hash value will be stored
+ * @param[in]     size Maximum number of bytes to write to `dst`
+ */
+void av_hash_final_b64(struct AVHashContext *ctx, uint8_t *dst, int size);
+
+/**
+ * Free hash context and set hash context pointer to `NULL`.
+ *
+ * @param[in,out] ctx  Pointer to hash context
+ */
+void av_hash_freep(struct AVHashContext **ctx);
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_HASH_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hmac.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hmac.h
new file mode 100644
index 0000000..412e950
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hmac.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 Martin Storsjo
+ *
+ * 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 AVUTIL_HMAC_H
+#define AVUTIL_HMAC_H
+
+#include <stdint.h>
+
+#include "version.h"
+/**
+ * @defgroup lavu_hmac HMAC
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+enum AVHMACType {
+    AV_HMAC_MD5,
+    AV_HMAC_SHA1,
+    AV_HMAC_SHA224,
+    AV_HMAC_SHA256,
+    AV_HMAC_SHA384,
+    AV_HMAC_SHA512,
+};
+
+typedef struct AVHMAC AVHMAC;
+
+/**
+ * Allocate an AVHMAC context.
+ * @param type The hash function used for the HMAC.
+ */
+AVHMAC *av_hmac_alloc(enum AVHMACType type);
+
+/**
+ * Free an AVHMAC context.
+ * @param ctx The context to free, may be NULL
+ */
+void av_hmac_free(AVHMAC *ctx);
+
+/**
+ * Initialize an AVHMAC context with an authentication key.
+ * @param ctx    The HMAC context
+ * @param key    The authentication key
+ * @param keylen The length of the key, in bytes
+ */
+void av_hmac_init(AVHMAC *ctx, const uint8_t *key, unsigned int keylen);
+
+/**
+ * Hash data with the HMAC.
+ * @param ctx  The HMAC context
+ * @param data The data to hash
+ * @param len  The length of the data, in bytes
+ */
+void av_hmac_update(AVHMAC *ctx, const uint8_t *data, unsigned int len);
+
+/**
+ * Finish hashing and output the HMAC digest.
+ * @param ctx    The HMAC context
+ * @param out    The output buffer to write the digest into
+ * @param outlen The length of the out buffer, in bytes
+ * @return       The number of bytes written to out, or a negative error code.
+ */
+int av_hmac_final(AVHMAC *ctx, uint8_t *out, unsigned int outlen);
+
+/**
+ * Hash an array of data with a key.
+ * @param ctx    The HMAC context
+ * @param data   The data to hash
+ * @param len    The length of the data, in bytes
+ * @param key    The authentication key
+ * @param keylen The length of the key, in bytes
+ * @param out    The output buffer to write the digest into
+ * @param outlen The length of the out buffer, in bytes
+ * @return       The number of bytes written to out, or a negative error code.
+ */
+int av_hmac_calc(AVHMAC *ctx, const uint8_t *data, unsigned int len,
+                 const uint8_t *key, unsigned int keylen,
+                 uint8_t *out, unsigned int outlen);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_HMAC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext.h
new file mode 100644
index 0000000..f5a4b62
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext.h
@@ -0,0 +1,584 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_H
+#define AVUTIL_HWCONTEXT_H
+
+#include "buffer.h"
+#include "frame.h"
+#include "log.h"
+#include "pixfmt.h"
+
+enum AVHWDeviceType {
+    AV_HWDEVICE_TYPE_NONE,
+    AV_HWDEVICE_TYPE_VDPAU,
+    AV_HWDEVICE_TYPE_CUDA,
+    AV_HWDEVICE_TYPE_VAAPI,
+    AV_HWDEVICE_TYPE_DXVA2,
+    AV_HWDEVICE_TYPE_QSV,
+    AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
+    AV_HWDEVICE_TYPE_D3D11VA,
+    AV_HWDEVICE_TYPE_DRM,
+    AV_HWDEVICE_TYPE_OPENCL,
+    AV_HWDEVICE_TYPE_MEDIACODEC,
+};
+
+typedef struct AVHWDeviceInternal AVHWDeviceInternal;
+
+/**
+ * This struct aggregates all the (hardware/vendor-specific) "high-level" state,
+ * i.e. state that is not tied to a concrete processing configuration.
+ * E.g., in an API that supports hardware-accelerated encoding and decoding,
+ * this struct will (if possible) wrap the state that is common to both encoding
+ * and decoding and from which specific instances of encoders or decoders can be
+ * derived.
+ *
+ * This struct is reference-counted with the AVBuffer mechanism. The
+ * av_hwdevice_ctx_alloc() constructor yields a reference, whose data field
+ * points to the actual AVHWDeviceContext. Further objects derived from
+ * AVHWDeviceContext (such as AVHWFramesContext, describing a frame pool with
+ * specific properties) will hold an internal reference to it. After all the
+ * references are released, the AVHWDeviceContext itself will be freed,
+ * optionally invoking a user-specified callback for uninitializing the hardware
+ * state.
+ */
+typedef struct AVHWDeviceContext {
+    /**
+     * A class for logging. Set by av_hwdevice_ctx_alloc().
+     */
+    const AVClass *av_class;
+
+    /**
+     * Private data used internally by libavutil. Must not be accessed in any
+     * way by the caller.
+     */
+    AVHWDeviceInternal *internal;
+
+    /**
+     * This field identifies the underlying API used for hardware access.
+     *
+     * This field is set when this struct is allocated and never changed
+     * afterwards.
+     */
+    enum AVHWDeviceType type;
+
+    /**
+     * The format-specific data, allocated and freed by libavutil along with
+     * this context.
+     *
+     * Should be cast by the user to the format-specific context defined in the
+     * corresponding header (hwcontext_*.h) and filled as described in the
+     * documentation before calling av_hwdevice_ctx_init().
+     *
+     * After calling av_hwdevice_ctx_init() this struct should not be modified
+     * by the caller.
+     */
+    void *hwctx;
+
+    /**
+     * This field may be set by the caller before calling av_hwdevice_ctx_init().
+     *
+     * If non-NULL, this callback will be called when the last reference to
+     * this context is unreferenced, immediately before it is freed.
+     *
+     * @note when other objects (e.g an AVHWFramesContext) are derived from this
+     *       struct, this callback will be invoked after all such child objects
+     *       are fully uninitialized and their respective destructors invoked.
+     */
+    void (*free)(struct AVHWDeviceContext *ctx);
+
+    /**
+     * Arbitrary user data, to be used e.g. by the free() callback.
+     */
+    void *user_opaque;
+} AVHWDeviceContext;
+
+typedef struct AVHWFramesInternal AVHWFramesInternal;
+
+/**
+ * This struct describes a set or pool of "hardware" frames (i.e. those with
+ * data not located in normal system memory). All the frames in the pool are
+ * assumed to be allocated in the same way and interchangeable.
+ *
+ * This struct is reference-counted with the AVBuffer mechanism and tied to a
+ * given AVHWDeviceContext instance. The av_hwframe_ctx_alloc() constructor
+ * yields a reference, whose data field points to the actual AVHWFramesContext
+ * struct.
+ */
+typedef struct AVHWFramesContext {
+    /**
+     * A class for logging.
+     */
+    const AVClass *av_class;
+
+    /**
+     * Private data used internally by libavutil. Must not be accessed in any
+     * way by the caller.
+     */
+    AVHWFramesInternal *internal;
+
+    /**
+     * A reference to the parent AVHWDeviceContext. This reference is owned and
+     * managed by the enclosing AVHWFramesContext, but the caller may derive
+     * additional references from it.
+     */
+    AVBufferRef *device_ref;
+
+    /**
+     * The parent AVHWDeviceContext. This is simply a pointer to
+     * device_ref->data provided for convenience.
+     *
+     * Set by libavutil in av_hwframe_ctx_init().
+     */
+    AVHWDeviceContext *device_ctx;
+
+    /**
+     * The format-specific data, allocated and freed automatically along with
+     * this context.
+     *
+     * Should be cast by the user to the format-specific context defined in the
+     * corresponding header (hwframe_*.h) and filled as described in the
+     * documentation before calling av_hwframe_ctx_init().
+     *
+     * After any frames using this context are created, the contents of this
+     * struct should not be modified by the caller.
+     */
+    void *hwctx;
+
+    /**
+     * This field may be set by the caller before calling av_hwframe_ctx_init().
+     *
+     * If non-NULL, this callback will be called when the last reference to
+     * this context is unreferenced, immediately before it is freed.
+     */
+    void (*free)(struct AVHWFramesContext *ctx);
+
+    /**
+     * Arbitrary user data, to be used e.g. by the free() callback.
+     */
+    void *user_opaque;
+
+    /**
+     * A pool from which the frames are allocated by av_hwframe_get_buffer().
+     * This field may be set by the caller before calling av_hwframe_ctx_init().
+     * The buffers returned by calling av_buffer_pool_get() on this pool must
+     * have the properties described in the documentation in the corresponding hw
+     * type's header (hwcontext_*.h). The pool will be freed strictly before
+     * this struct's free() callback is invoked.
+     *
+     * This field may be NULL, then libavutil will attempt to allocate a pool
+     * internally. Note that certain device types enforce pools allocated at
+     * fixed size (frame count), which cannot be extended dynamically. In such a
+     * case, initial_pool_size must be set appropriately.
+     */
+    AVBufferPool *pool;
+
+    /**
+     * Initial size of the frame pool. If a device type does not support
+     * dynamically resizing the pool, then this is also the maximum pool size.
+     *
+     * May be set by the caller before calling av_hwframe_ctx_init(). Must be
+     * set if pool is NULL and the device type does not support dynamic pools.
+     */
+    int initial_pool_size;
+
+    /**
+     * The pixel format identifying the underlying HW surface type.
+     *
+     * Must be a hwaccel format, i.e. the corresponding descriptor must have the
+     * AV_PIX_FMT_FLAG_HWACCEL flag set.
+     *
+     * Must be set by the user before calling av_hwframe_ctx_init().
+     */
+    enum AVPixelFormat format;
+
+    /**
+     * The pixel format identifying the actual data layout of the hardware
+     * frames.
+     *
+     * Must be set by the caller before calling av_hwframe_ctx_init().
+     *
+     * @note when the underlying API does not provide the exact data layout, but
+     * only the colorspace/bit depth, this field should be set to the fully
+     * planar version of that format (e.g. for 8-bit 420 YUV it should be
+     * AV_PIX_FMT_YUV420P, not AV_PIX_FMT_NV12 or anything else).
+     */
+    enum AVPixelFormat sw_format;
+
+    /**
+     * The allocated dimensions of the frames in this pool.
+     *
+     * Must be set by the user before calling av_hwframe_ctx_init().
+     */
+    int width, height;
+} AVHWFramesContext;
+
+/**
+ * Look up an AVHWDeviceType by name.
+ *
+ * @param name String name of the device type (case-insensitive).
+ * @return The type from enum AVHWDeviceType, or AV_HWDEVICE_TYPE_NONE if
+ *         not found.
+ */
+enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name);
+
+/** Get the string name of an AVHWDeviceType.
+ *
+ * @param type Type from enum AVHWDeviceType.
+ * @return Pointer to a static string containing the name, or NULL if the type
+ *         is not valid.
+ */
+const char *av_hwdevice_get_type_name(enum AVHWDeviceType type);
+
+/**
+ * Iterate over supported device types.
+ *
+ * @param type AV_HWDEVICE_TYPE_NONE initially, then the previous type
+ *             returned by this function in subsequent iterations.
+ * @return The next usable device type from enum AVHWDeviceType, or
+ *         AV_HWDEVICE_TYPE_NONE if there are no more.
+ */
+enum AVHWDeviceType av_hwdevice_iterate_types(enum AVHWDeviceType prev);
+
+/**
+ * Allocate an AVHWDeviceContext for a given hardware type.
+ *
+ * @param type the type of the hardware device to allocate.
+ * @return a reference to the newly created AVHWDeviceContext on success or NULL
+ *         on failure.
+ */
+AVBufferRef *av_hwdevice_ctx_alloc(enum AVHWDeviceType type);
+
+/**
+ * Finalize the device context before use. This function must be called after
+ * the context is filled with all the required information and before it is
+ * used in any way.
+ *
+ * @param ref a reference to the AVHWDeviceContext
+ * @return 0 on success, a negative AVERROR code on failure
+ */
+int av_hwdevice_ctx_init(AVBufferRef *ref);
+
+/**
+ * Open a device of the specified type and create an AVHWDeviceContext for it.
+ *
+ * This is a convenience function intended to cover the simple cases. Callers
+ * who need to fine-tune device creation/management should open the device
+ * manually and then wrap it in an AVHWDeviceContext using
+ * av_hwdevice_ctx_alloc()/av_hwdevice_ctx_init().
+ *
+ * The returned context is already initialized and ready for use, the caller
+ * should not call av_hwdevice_ctx_init() on it. The user_opaque/free fields of
+ * the created AVHWDeviceContext are set by this function and should not be
+ * touched by the caller.
+ *
+ * @param device_ctx On success, a reference to the newly-created device context
+ *                   will be written here. The reference is owned by the caller
+ *                   and must be released with av_buffer_unref() when no longer
+ *                   needed. On failure, NULL will be written to this pointer.
+ * @param type The type of the device to create.
+ * @param device A type-specific string identifying the device to open.
+ * @param opts A dictionary of additional (type-specific) options to use in
+ *             opening the device. The dictionary remains owned by the caller.
+ * @param flags currently unused
+ *
+ * @return 0 on success, a negative AVERROR code on failure.
+ */
+int av_hwdevice_ctx_create(AVBufferRef **device_ctx, enum AVHWDeviceType type,
+                           const char *device, AVDictionary *opts, int flags);
+
+/**
+ * Create a new device of the specified type from an existing device.
+ *
+ * If the source device is a device of the target type or was originally
+ * derived from such a device (possibly through one or more intermediate
+ * devices of other types), then this will return a reference to the
+ * existing device of the same type as is requested.
+ *
+ * Otherwise, it will attempt to derive a new device from the given source
+ * device.  If direct derivation to the new type is not implemented, it will
+ * attempt the same derivation from each ancestor of the source device in
+ * turn looking for an implemented derivation method.
+ *
+ * @param dst_ctx On success, a reference to the newly-created
+ *                AVHWDeviceContext.
+ * @param type    The type of the new device to create.
+ * @param src_ctx A reference to an existing AVHWDeviceContext which will be
+ *                used to create the new device.
+ * @param flags   Currently unused; should be set to zero.
+ * @return        Zero on success, a negative AVERROR code on failure.
+ */
+int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ctx,
+                                   enum AVHWDeviceType type,
+                                   AVBufferRef *src_ctx, int flags);
+
+
+/**
+ * Allocate an AVHWFramesContext tied to a given device context.
+ *
+ * @param device_ctx a reference to a AVHWDeviceContext. This function will make
+ *                   a new reference for internal use, the one passed to the
+ *                   function remains owned by the caller.
+ * @return a reference to the newly created AVHWFramesContext on success or NULL
+ *         on failure.
+ */
+AVBufferRef *av_hwframe_ctx_alloc(AVBufferRef *device_ctx);
+
+/**
+ * Finalize the context before use. This function must be called after the
+ * context is filled with all the required information and before it is attached
+ * to any frames.
+ *
+ * @param ref a reference to the AVHWFramesContext
+ * @return 0 on success, a negative AVERROR code on failure
+ */
+int av_hwframe_ctx_init(AVBufferRef *ref);
+
+/**
+ * Allocate a new frame attached to the given AVHWFramesContext.
+ *
+ * @param hwframe_ctx a reference to an AVHWFramesContext
+ * @param frame an empty (freshly allocated or unreffed) frame to be filled with
+ *              newly allocated buffers.
+ * @param flags currently unused, should be set to zero
+ * @return 0 on success, a negative AVERROR code on failure
+ */
+int av_hwframe_get_buffer(AVBufferRef *hwframe_ctx, AVFrame *frame, int flags);
+
+/**
+ * Copy data to or from a hw surface. At least one of dst/src must have an
+ * AVHWFramesContext attached.
+ *
+ * If src has an AVHWFramesContext attached, then the format of dst (if set)
+ * must use one of the formats returned by av_hwframe_transfer_get_formats(src,
+ * AV_HWFRAME_TRANSFER_DIRECTION_FROM).
+ * If dst has an AVHWFramesContext attached, then the format of src must use one
+ * of the formats returned by av_hwframe_transfer_get_formats(dst,
+ * AV_HWFRAME_TRANSFER_DIRECTION_TO)
+ *
+ * dst may be "clean" (i.e. with data/buf pointers unset), in which case the
+ * data buffers will be allocated by this function using av_frame_get_buffer().
+ * If dst->format is set, then this format will be used, otherwise (when
+ * dst->format is AV_PIX_FMT_NONE) the first acceptable format will be chosen.
+ *
+ * The two frames must have matching allocated dimensions (i.e. equal to
+ * AVHWFramesContext.width/height), since not all device types support
+ * transferring a sub-rectangle of the whole surface. The display dimensions
+ * (i.e. AVFrame.width/height) may be smaller than the allocated dimensions, but
+ * also have to be equal for both frames. When the display dimensions are
+ * smaller than the allocated dimensions, the content of the padding in the
+ * destination frame is unspecified.
+ *
+ * @param dst the destination frame. dst is not touched on failure.
+ * @param src the source frame.
+ * @param flags currently unused, should be set to zero
+ * @return 0 on success, a negative AVERROR error code on failure.
+ */
+int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags);
+
+enum AVHWFrameTransferDirection {
+    /**
+     * Transfer the data from the queried hw frame.
+     */
+    AV_HWFRAME_TRANSFER_DIRECTION_FROM,
+
+    /**
+     * Transfer the data to the queried hw frame.
+     */
+    AV_HWFRAME_TRANSFER_DIRECTION_TO,
+};
+
+/**
+ * Get a list of possible source or target formats usable in
+ * av_hwframe_transfer_data().
+ *
+ * @param hwframe_ctx the frame context to obtain the information for
+ * @param dir the direction of the transfer
+ * @param formats the pointer to the output format list will be written here.
+ *                The list is terminated with AV_PIX_FMT_NONE and must be freed
+ *                by the caller when no longer needed using av_free().
+ *                If this function returns successfully, the format list will
+ *                have at least one item (not counting the terminator).
+ *                On failure, the contents of this pointer are unspecified.
+ * @param flags currently unused, should be set to zero
+ * @return 0 on success, a negative AVERROR code on failure.
+ */
+int av_hwframe_transfer_get_formats(AVBufferRef *hwframe_ctx,
+                                    enum AVHWFrameTransferDirection dir,
+                                    enum AVPixelFormat **formats, int flags);
+
+
+/**
+ * This struct describes the constraints on hardware frames attached to
+ * a given device with a hardware-specific configuration.  This is returned
+ * by av_hwdevice_get_hwframe_constraints() and must be freed by
+ * av_hwframe_constraints_free() after use.
+ */
+typedef struct AVHWFramesConstraints {
+    /**
+     * A list of possible values for format in the hw_frames_ctx,
+     * terminated by AV_PIX_FMT_NONE.  This member will always be filled.
+     */
+    enum AVPixelFormat *valid_hw_formats;
+
+    /**
+     * A list of possible values for sw_format in the hw_frames_ctx,
+     * terminated by AV_PIX_FMT_NONE.  Can be NULL if this information is
+     * not known.
+     */
+    enum AVPixelFormat *valid_sw_formats;
+
+    /**
+     * The minimum size of frames in this hw_frames_ctx.
+     * (Zero if not known.)
+     */
+    int min_width;
+    int min_height;
+
+    /**
+     * The maximum size of frames in this hw_frames_ctx.
+     * (INT_MAX if not known / no limit.)
+     */
+    int max_width;
+    int max_height;
+} AVHWFramesConstraints;
+
+/**
+ * Allocate a HW-specific configuration structure for a given HW device.
+ * After use, the user must free all members as required by the specific
+ * hardware structure being used, then free the structure itself with
+ * av_free().
+ *
+ * @param device_ctx a reference to the associated AVHWDeviceContext.
+ * @return The newly created HW-specific configuration structure on
+ *         success or NULL on failure.
+ */
+void *av_hwdevice_hwconfig_alloc(AVBufferRef *device_ctx);
+
+/**
+ * Get the constraints on HW frames given a device and the HW-specific
+ * configuration to be used with that device.  If no HW-specific
+ * configuration is provided, returns the maximum possible capabilities
+ * of the device.
+ *
+ * @param ref a reference to the associated AVHWDeviceContext.
+ * @param hwconfig a filled HW-specific configuration structure, or NULL
+ *        to return the maximum possible capabilities of the device.
+ * @return AVHWFramesConstraints structure describing the constraints
+ *         on the device, or NULL if not available.
+ */
+AVHWFramesConstraints *av_hwdevice_get_hwframe_constraints(AVBufferRef *ref,
+                                                           const void *hwconfig);
+
+/**
+ * Free an AVHWFrameConstraints structure.
+ *
+ * @param constraints The (filled or unfilled) AVHWFrameConstraints structure.
+ */
+void av_hwframe_constraints_free(AVHWFramesConstraints **constraints);
+
+
+/**
+ * Flags to apply to frame mappings.
+ */
+enum {
+    /**
+     * The mapping must be readable.
+     */
+    AV_HWFRAME_MAP_READ      = 1 << 0,
+    /**
+     * The mapping must be writeable.
+     */
+    AV_HWFRAME_MAP_WRITE     = 1 << 1,
+    /**
+     * The mapped frame will be overwritten completely in subsequent
+     * operations, so the current frame data need not be loaded.  Any values
+     * which are not overwritten are unspecified.
+     */
+    AV_HWFRAME_MAP_OVERWRITE = 1 << 2,
+    /**
+     * The mapping must be direct.  That is, there must not be any copying in
+     * the map or unmap steps.  Note that performance of direct mappings may
+     * be much lower than normal memory.
+     */
+    AV_HWFRAME_MAP_DIRECT    = 1 << 3,
+};
+
+/**
+ * Map a hardware frame.
+ *
+ * This has a number of different possible effects, depending on the format
+ * and origin of the src and dst frames.  On input, src should be a usable
+ * frame with valid buffers and dst should be blank (typically as just created
+ * by av_frame_alloc()).  src should have an associated hwframe context, and
+ * dst may optionally have a format and associated hwframe context.
+ *
+ * If src was created by mapping a frame from the hwframe context of dst,
+ * then this function undoes the mapping - dst is replaced by a reference to
+ * the frame that src was originally mapped from.
+ *
+ * If both src and dst have an associated hwframe context, then this function
+ * attempts to map the src frame from its hardware context to that of dst and
+ * then fill dst with appropriate data to be usable there.  This will only be
+ * possible if the hwframe contexts and associated devices are compatible -
+ * given compatible devices, av_hwframe_ctx_create_derived() can be used to
+ * create a hwframe context for dst in which mapping should be possible.
+ *
+ * If src has a hwframe context but dst does not, then the src frame is
+ * mapped to normal memory and should thereafter be usable as a normal frame.
+ * If the format is set on dst, then the mapping will attempt to create dst
+ * with that format and fail if it is not possible.  If format is unset (is
+ * AV_PIX_FMT_NONE) then dst will be mapped with whatever the most appropriate
+ * format to use is (probably the sw_format of the src hwframe context).
+ *
+ * A return value of AVERROR(ENOSYS) indicates that the mapping is not
+ * possible with the given arguments and hwframe setup, while other return
+ * values indicate that it failed somehow.
+ *
+ * @param dst Destination frame, to contain the mapping.
+ * @param src Source frame, to be mapped.
+ * @param flags Some combination of AV_HWFRAME_MAP_* flags.
+ * @return Zero on success, negative AVERROR code on failure.
+ */
+int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags);
+
+
+/**
+ * Create and initialise an AVHWFramesContext as a mapping of another existing
+ * AVHWFramesContext on a different device.
+ *
+ * av_hwframe_ctx_init() should not be called after this.
+ *
+ * @param derived_frame_ctx  On success, a reference to the newly created
+ *                           AVHWFramesContext.
+ * @param derived_device_ctx A reference to the device to create the new
+ *                           AVHWFramesContext on.
+ * @param source_frame_ctx   A reference to an existing AVHWFramesContext
+ *                           which will be mapped to the derived context.
+ * @param flags  Some combination of AV_HWFRAME_MAP_* flags, defining the
+ *               mapping parameters to apply to frames which are allocated
+ *               in the derived device.
+ * @return       Zero on success, negative AVERROR code on failure.
+ */
+int av_hwframe_ctx_create_derived(AVBufferRef **derived_frame_ctx,
+                                  enum AVPixelFormat format,
+                                  AVBufferRef *derived_device_ctx,
+                                  AVBufferRef *source_frame_ctx,
+                                  int flags);
+
+#endif /* AVUTIL_HWCONTEXT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_cuda.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_cuda.h
new file mode 100644
index 0000000..81a0552
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_cuda.h
@@ -0,0 +1,52 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_CUDA_H
+#define AVUTIL_HWCONTEXT_CUDA_H
+
+#ifndef CUDA_VERSION
+#include <cuda.h>
+#endif
+
+#include "pixfmt.h"
+
+/**
+ * @file
+ * An API-specific header for AV_HWDEVICE_TYPE_CUDA.
+ *
+ * This API supports dynamic frame pools. AVHWFramesContext.pool must return
+ * AVBufferRefs whose data pointer is a CUdeviceptr.
+ */
+
+typedef struct AVCUDADeviceContextInternal AVCUDADeviceContextInternal;
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVCUDADeviceContext {
+    CUcontext cuda_ctx;
+    CUstream stream;
+    AVCUDADeviceContextInternal *internal;
+} AVCUDADeviceContext;
+
+/**
+ * AVHWFramesContext.hwctx is currently not used
+ */
+
+#endif /* AVUTIL_HWCONTEXT_CUDA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_d3d11va.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_d3d11va.h
new file mode 100644
index 0000000..9f91e9b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_d3d11va.h
@@ -0,0 +1,169 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_D3D11VA_H
+#define AVUTIL_HWCONTEXT_D3D11VA_H
+
+/**
+ * @file
+ * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
+ *
+ * The default pool implementation will be fixed-size if initial_pool_size is
+ * set (and allocate elements from an array texture). Otherwise it will allocate
+ * individual textures. Be aware that decoding requires a single array texture.
+ *
+ * Using sw_format==AV_PIX_FMT_YUV420P has special semantics, and maps to
+ * DXGI_FORMAT_420_OPAQUE. av_hwframe_transfer_data() is not supported for
+ * this format. Refer to MSDN for details.
+ *
+ * av_hwdevice_ctx_create() for this device type supports a key named "debug"
+ * for the AVDictionary entry. If this is set to any value, the device creation
+ * code will try to load various supported D3D debugging layers.
+ */
+
+#include <d3d11.h>
+#include <stdint.h>
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVD3D11VADeviceContext {
+    /**
+     * Device used for texture creation and access. This can also be used to
+     * set the libavcodec decoding device.
+     *
+     * Must be set by the user. This is the only mandatory field - the other
+     * device context fields are set from this and are available for convenience.
+     *
+     * Deallocating the AVHWDeviceContext will always release this interface,
+     * and it does not matter whether it was user-allocated.
+     */
+    ID3D11Device        *device;
+
+    /**
+     * If unset, this will be set from the device field on init.
+     *
+     * Deallocating the AVHWDeviceContext will always release this interface,
+     * and it does not matter whether it was user-allocated.
+     */
+    ID3D11DeviceContext *device_context;
+
+    /**
+     * If unset, this will be set from the device field on init.
+     *
+     * Deallocating the AVHWDeviceContext will always release this interface,
+     * and it does not matter whether it was user-allocated.
+     */
+    ID3D11VideoDevice   *video_device;
+
+    /**
+     * If unset, this will be set from the device_context field on init.
+     *
+     * Deallocating the AVHWDeviceContext will always release this interface,
+     * and it does not matter whether it was user-allocated.
+     */
+    ID3D11VideoContext  *video_context;
+
+    /**
+     * Callbacks for locking. They protect accesses to device_context and
+     * video_context calls. They also protect access to the internal staging
+     * texture (for av_hwframe_transfer_data() calls). They do NOT protect
+     * access to hwcontext or decoder state in general.
+     *
+     * If unset on init, the hwcontext implementation will set them to use an
+     * internal mutex.
+     *
+     * The underlying lock must be recursive. lock_ctx is for free use by the
+     * locking implementation.
+     */
+    void (*lock)(void *lock_ctx);
+    void (*unlock)(void *lock_ctx);
+    void *lock_ctx;
+} AVD3D11VADeviceContext;
+
+/**
+ * D3D11 frame descriptor for pool allocation.
+ *
+ * In user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
+ * with the data pointer pointing at an object of this type describing the
+ * planes of the frame.
+ *
+ * This has no use outside of custom allocation, and AVFrame AVBufferRef do not
+ * necessarily point to an instance of this struct.
+ */
+typedef struct AVD3D11FrameDescriptor {
+    /**
+     * The texture in which the frame is located. The reference count is
+     * managed by the AVBufferRef, and destroying the reference will release
+     * the interface.
+     *
+     * Normally stored in AVFrame.data[0].
+     */
+    ID3D11Texture2D *texture;
+
+    /**
+     * The index into the array texture element representing the frame, or 0
+     * if the texture is not an array texture.
+     *
+     * Normally stored in AVFrame.data[1] (cast from intptr_t).
+     */
+    intptr_t index;
+} AVD3D11FrameDescriptor;
+
+/**
+ * This struct is allocated as AVHWFramesContext.hwctx
+ */
+typedef struct AVD3D11VAFramesContext {
+    /**
+     * The canonical texture used for pool allocation. If this is set to NULL
+     * on init, the hwframes implementation will allocate and set an array
+     * texture if initial_pool_size > 0.
+     *
+     * The only situation when the API user should set this is:
+     * - the user wants to do manual pool allocation (setting
+     *   AVHWFramesContext.pool), instead of letting AVHWFramesContext
+     *   allocate the pool
+     * - of an array texture
+     * - and wants it to use it for decoding
+     * - this has to be done before calling av_hwframe_ctx_init()
+     *
+     * Deallocating the AVHWFramesContext will always release this interface,
+     * and it does not matter whether it was user-allocated.
+     *
+     * This is in particular used by the libavcodec D3D11VA hwaccel, which
+     * requires a single array texture. It will create ID3D11VideoDecoderOutputView
+     * objects for each array texture element on decoder initialization.
+     */
+    ID3D11Texture2D *texture;
+
+    /**
+     * D3D11_TEXTURE2D_DESC.BindFlags used for texture creation. The user must
+     * at least set D3D11_BIND_DECODER if the frames context is to be used for
+     * video decoding.
+     * This field is ignored/invalid if a user-allocated texture is provided.
+     */
+    UINT BindFlags;
+
+    /**
+     * D3D11_TEXTURE2D_DESC.MiscFlags used for texture creation.
+     * This field is ignored/invalid if a user-allocated texture is provided.
+     */
+    UINT MiscFlags;
+} AVD3D11VAFramesContext;
+
+#endif /* AVUTIL_HWCONTEXT_D3D11VA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_drm.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_drm.h
new file mode 100644
index 0000000..42709f2
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_drm.h
@@ -0,0 +1,169 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_DRM_H
+#define AVUTIL_HWCONTEXT_DRM_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * @file
+ * API-specific header for AV_HWDEVICE_TYPE_DRM.
+ *
+ * Internal frame allocation is not currently supported - all frames
+ * must be allocated by the user.  Thus AVHWFramesContext is always
+ * NULL, though this may change if support for frame allocation is
+ * added in future.
+ */
+
+enum {
+    /**
+     * The maximum number of layers/planes in a DRM frame.
+     */
+    AV_DRM_MAX_PLANES = 4
+};
+
+/**
+ * DRM object descriptor.
+ *
+ * Describes a single DRM object, addressing it as a PRIME file
+ * descriptor.
+ */
+typedef struct AVDRMObjectDescriptor {
+    /**
+     * DRM PRIME fd for the object.
+     */
+    int fd;
+    /**
+     * Total size of the object.
+     *
+     * (This includes any parts not which do not contain image data.)
+     */
+    size_t size;
+    /**
+     * Format modifier applied to the object (DRM_FORMAT_MOD_*).
+     *
+     * If the format modifier is unknown then this should be set to
+     * DRM_FORMAT_MOD_INVALID.
+     */
+    uint64_t format_modifier;
+} AVDRMObjectDescriptor;
+
+/**
+ * DRM plane descriptor.
+ *
+ * Describes a single plane of a layer, which is contained within
+ * a single object.
+ */
+typedef struct AVDRMPlaneDescriptor {
+    /**
+     * Index of the object containing this plane in the objects
+     * array of the enclosing frame descriptor.
+     */
+    int object_index;
+    /**
+     * Offset within that object of this plane.
+     */
+    ptrdiff_t offset;
+    /**
+     * Pitch (linesize) of this plane.
+     */
+    ptrdiff_t pitch;
+} AVDRMPlaneDescriptor;
+
+/**
+ * DRM layer descriptor.
+ *
+ * Describes a single layer within a frame.  This has the structure
+ * defined by its format, and will contain one or more planes.
+ */
+typedef struct AVDRMLayerDescriptor {
+    /**
+     * Format of the layer (DRM_FORMAT_*).
+     */
+    uint32_t format;
+    /**
+     * Number of planes in the layer.
+     *
+     * This must match the number of planes required by format.
+     */
+    int nb_planes;
+    /**
+     * Array of planes in this layer.
+     */
+    AVDRMPlaneDescriptor planes[AV_DRM_MAX_PLANES];
+} AVDRMLayerDescriptor;
+
+/**
+ * DRM frame descriptor.
+ *
+ * This is used as the data pointer for AV_PIX_FMT_DRM_PRIME frames.
+ * It is also used by user-allocated frame pools - allocating in
+ * AVHWFramesContext.pool must return AVBufferRefs which contain
+ * an object of this type.
+ *
+ * The fields of this structure should be set such it can be
+ * imported directly by EGL using the EGL_EXT_image_dma_buf_import
+ * and EGL_EXT_image_dma_buf_import_modifiers extensions.
+ * (Note that the exact layout of a particular format may vary between
+ * platforms - we only specify that the same platform should be able
+ * to import it.)
+ *
+ * The total number of planes must not exceed AV_DRM_MAX_PLANES, and
+ * the order of the planes by increasing layer index followed by
+ * increasing plane index must be the same as the order which would
+ * be used for the data pointers in the equivalent software format.
+ */
+typedef struct AVDRMFrameDescriptor {
+    /**
+     * Number of DRM objects making up this frame.
+     */
+    int nb_objects;
+    /**
+     * Array of objects making up the frame.
+     */
+    AVDRMObjectDescriptor objects[AV_DRM_MAX_PLANES];
+    /**
+     * Number of layers in the frame.
+     */
+    int nb_layers;
+    /**
+     * Array of layers in the frame.
+     */
+    AVDRMLayerDescriptor layers[AV_DRM_MAX_PLANES];
+} AVDRMFrameDescriptor;
+
+/**
+ * DRM device.
+ *
+ * Allocated as AVHWDeviceContext.hwctx.
+ */
+typedef struct AVDRMDeviceContext {
+    /**
+     * File descriptor of DRM device.
+     *
+     * This is used as the device to create frames on, and may also be
+     * used in some derivation and mapping operations.
+     *
+     * If no device is required, set to -1.
+     */
+    int fd;
+} AVDRMDeviceContext;
+
+#endif /* AVUTIL_HWCONTEXT_DRM_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_dxva2.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_dxva2.h
new file mode 100644
index 0000000..e1b79bc
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_dxva2.h
@@ -0,0 +1,75 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_DXVA2_H
+#define AVUTIL_HWCONTEXT_DXVA2_H
+
+/**
+ * @file
+ * An API-specific header for AV_HWDEVICE_TYPE_DXVA2.
+ *
+ * Only fixed-size pools are supported.
+ *
+ * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
+ * with the data pointer set to a pointer to IDirect3DSurface9.
+ */
+
+#include <d3d9.h>
+#include <dxva2api.h>
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVDXVA2DeviceContext {
+    IDirect3DDeviceManager9 *devmgr;
+} AVDXVA2DeviceContext;
+
+/**
+ * This struct is allocated as AVHWFramesContext.hwctx
+ */
+typedef struct AVDXVA2FramesContext {
+    /**
+     * The surface type (e.g. DXVA2_VideoProcessorRenderTarget or
+     * DXVA2_VideoDecoderRenderTarget). Must be set by the caller.
+     */
+    DWORD               surface_type;
+
+    /**
+     * The surface pool. When an external pool is not provided by the caller,
+     * this will be managed (allocated and filled on init, freed on uninit) by
+     * libavutil.
+     */
+    IDirect3DSurface9 **surfaces;
+    int              nb_surfaces;
+
+    /**
+     * Certain drivers require the decoder to be destroyed before the surfaces.
+     * To allow internally managed pools to work properly in such cases, this
+     * field is provided.
+     *
+     * If it is non-NULL, libavutil will call IDirectXVideoDecoder_Release() on
+     * it just before the internal surface pool is freed.
+     *
+     * This is for convenience only. Some code uses other methods to manage the
+     * decoder reference.
+     */
+    IDirectXVideoDecoder *decoder_to_release;
+} AVDXVA2FramesContext;
+
+#endif /* AVUTIL_HWCONTEXT_DXVA2_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_mediacodec.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_mediacodec.h
new file mode 100644
index 0000000..101a980
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_mediacodec.h
@@ -0,0 +1,36 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_MEDIACODEC_H
+#define AVUTIL_HWCONTEXT_MEDIACODEC_H
+
+/**
+ * MediaCodec details.
+ *
+ * Allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVMediaCodecDeviceContext {
+    /**
+     * android/view/Surface handle, to be filled by the user.
+     *
+     * This is the default surface used by decoders on this device.
+     */
+    void *surface;
+} AVMediaCodecDeviceContext;
+
+#endif /* AVUTIL_HWCONTEXT_MEDIACODEC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_qsv.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_qsv.h
new file mode 100644
index 0000000..b98d611
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_qsv.h
@@ -0,0 +1,53 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_QSV_H
+#define AVUTIL_HWCONTEXT_QSV_H
+
+#include <mfx/mfxvideo.h>
+
+/**
+ * @file
+ * An API-specific header for AV_HWDEVICE_TYPE_QSV.
+ *
+ * This API does not support dynamic frame pools. AVHWFramesContext.pool must
+ * contain AVBufferRefs whose data pointer points to an mfxFrameSurface1 struct.
+ */
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVQSVDeviceContext {
+    mfxSession session;
+} AVQSVDeviceContext;
+
+/**
+ * This struct is allocated as AVHWFramesContext.hwctx
+ */
+typedef struct AVQSVFramesContext {
+    mfxFrameSurface1 *surfaces;
+    int            nb_surfaces;
+
+    /**
+     * A combination of MFX_MEMTYPE_* describing the frame pool.
+     */
+    int frame_type;
+} AVQSVFramesContext;
+
+#endif /* AVUTIL_HWCONTEXT_QSV_H */
+
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vaapi.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vaapi.h
new file mode 100644
index 0000000..0b2e071
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vaapi.h
@@ -0,0 +1,117 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_VAAPI_H
+#define AVUTIL_HWCONTEXT_VAAPI_H
+
+#include <va/va.h>
+
+/**
+ * @file
+ * API-specific header for AV_HWDEVICE_TYPE_VAAPI.
+ *
+ * Dynamic frame pools are supported, but note that any pool used as a render
+ * target is required to be of fixed size in order to be be usable as an
+ * argument to vaCreateContext().
+ *
+ * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
+ * with the data pointer set to a VASurfaceID.
+ */
+
+enum {
+    /**
+     * The quirks field has been set by the user and should not be detected
+     * automatically by av_hwdevice_ctx_init().
+     */
+    AV_VAAPI_DRIVER_QUIRK_USER_SET = (1 << 0),
+    /**
+     * The driver does not destroy parameter buffers when they are used by
+     * vaRenderPicture().  Additional code will be required to destroy them
+     * separately afterwards.
+     */
+    AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS = (1 << 1),
+
+    /**
+     * The driver does not support the VASurfaceAttribMemoryType attribute,
+     * so the surface allocation code will not try to use it.
+     */
+    AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE = (1 << 2),
+
+    /**
+     * The driver does not support surface attributes at all.
+     * The surface allocation code will never pass them to surface allocation,
+     * and the results of the vaQuerySurfaceAttributes() call will be faked.
+     */
+    AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES = (1 << 3),
+};
+
+/**
+ * VAAPI connection details.
+ *
+ * Allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVVAAPIDeviceContext {
+    /**
+     * The VADisplay handle, to be filled by the user.
+     */
+    VADisplay display;
+    /**
+     * Driver quirks to apply - this is filled by av_hwdevice_ctx_init(),
+     * with reference to a table of known drivers, unless the
+     * AV_VAAPI_DRIVER_QUIRK_USER_SET bit is already present.  The user
+     * may need to refer to this field when performing any later
+     * operations using VAAPI with the same VADisplay.
+     */
+    unsigned int driver_quirks;
+} AVVAAPIDeviceContext;
+
+/**
+ * VAAPI-specific data associated with a frame pool.
+ *
+ * Allocated as AVHWFramesContext.hwctx.
+ */
+typedef struct AVVAAPIFramesContext {
+    /**
+     * Set by the user to apply surface attributes to all surfaces in
+     * the frame pool.  If null, default settings are used.
+     */
+    VASurfaceAttrib *attributes;
+    int           nb_attributes;
+    /**
+     * The surfaces IDs of all surfaces in the pool after creation.
+     * Only valid if AVHWFramesContext.initial_pool_size was positive.
+     * These are intended to be used as the render_targets arguments to
+     * vaCreateContext().
+     */
+    VASurfaceID     *surface_ids;
+    int           nb_surfaces;
+} AVVAAPIFramesContext;
+
+/**
+ * VAAPI hardware pipeline configuration details.
+ *
+ * Allocated with av_hwdevice_hwconfig_alloc().
+ */
+typedef struct AVVAAPIHWConfig {
+    /**
+     * ID of a VAAPI pipeline configuration.
+     */
+    VAConfigID config_id;
+} AVVAAPIHWConfig;
+
+#endif /* AVUTIL_HWCONTEXT_VAAPI_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vdpau.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vdpau.h
new file mode 100644
index 0000000..1b7ea1e
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_vdpau.h
@@ -0,0 +1,44 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_VDPAU_H
+#define AVUTIL_HWCONTEXT_VDPAU_H
+
+#include <vdpau/vdpau.h>
+
+/**
+ * @file
+ * An API-specific header for AV_HWDEVICE_TYPE_VDPAU.
+ *
+ * This API supports dynamic frame pools. AVHWFramesContext.pool must return
+ * AVBufferRefs whose data pointer is a VdpVideoSurface.
+ */
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVVDPAUDeviceContext {
+    VdpDevice          device;
+    VdpGetProcAddress *get_proc_address;
+} AVVDPAUDeviceContext;
+
+/**
+ * AVHWFramesContext.hwctx is currently not used
+ */
+
+#endif /* AVUTIL_HWCONTEXT_VDPAU_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_videotoolbox.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_videotoolbox.h
new file mode 100644
index 0000000..380918d
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/hwcontext_videotoolbox.h
@@ -0,0 +1,54 @@
+/*
+ * 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 AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H
+#define AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H
+
+#include <stdint.h>
+
+#include <VideoToolbox/VideoToolbox.h>
+
+#include "pixfmt.h"
+
+/**
+ * @file
+ * An API-specific header for AV_HWDEVICE_TYPE_VIDEOTOOLBOX.
+ *
+ * This API currently does not support frame allocation, as the raw VideoToolbox
+ * API does allocation, and FFmpeg itself never has the need to allocate frames.
+ *
+ * If the API user sets a custom pool, AVHWFramesContext.pool must return
+ * AVBufferRefs whose data pointer is a CVImageBufferRef or CVPixelBufferRef.
+ *
+ * Currently AVHWDeviceContext.hwctx and AVHWFramesContext.hwctx are always
+ * NULL.
+ */
+
+/**
+ * Convert a VideoToolbox (actually CoreVideo) format to AVPixelFormat.
+ * Returns AV_PIX_FMT_NONE if no known equivalent was found.
+ */
+enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt);
+
+/**
+ * Convert an AVPixelFormat to a VideoToolbox (actually CoreVideo) format.
+ * Returns 0 if no known equivalent was found.
+ */
+uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt);
+
+#endif /* AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/imgutils.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/imgutils.h
new file mode 100644
index 0000000..5b790ec
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/imgutils.h
@@ -0,0 +1,277 @@
+/*
+ * 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 AVUTIL_IMGUTILS_H
+#define AVUTIL_IMGUTILS_H
+
+/**
+ * @file
+ * misc image utilities
+ *
+ * @addtogroup lavu_picture
+ * @{
+ */
+
+#include "avutil.h"
+#include "pixdesc.h"
+#include "rational.h"
+
+/**
+ * Compute the max pixel step for each plane of an image with a
+ * format described by pixdesc.
+ *
+ * The pixel step is the distance in bytes between the first byte of
+ * the group of bytes which describe a pixel component and the first
+ * byte of the successive group in the same plane for the same
+ * component.
+ *
+ * @param max_pixsteps an array which is filled with the max pixel step
+ * for each plane. Since a plane may contain different pixel
+ * components, the computed max_pixsteps[plane] is relative to the
+ * component in the plane with the max pixel step.
+ * @param max_pixstep_comps an array which is filled with the component
+ * for each plane which has the max pixel step. May be NULL.
+ */
+void av_image_fill_max_pixsteps(int max_pixsteps[4], int max_pixstep_comps[4],
+                                const AVPixFmtDescriptor *pixdesc);
+
+/**
+ * Compute the size of an image line with format pix_fmt and width
+ * width for the plane plane.
+ *
+ * @return the computed size in bytes
+ */
+int av_image_get_linesize(enum AVPixelFormat pix_fmt, int width, int plane);
+
+/**
+ * Fill plane linesizes for an image with pixel format pix_fmt and
+ * width width.
+ *
+ * @param linesizes array to be filled with the linesize for each plane
+ * @return >= 0 in case of success, a negative error code otherwise
+ */
+int av_image_fill_linesizes(int linesizes[4], enum AVPixelFormat pix_fmt, int width);
+
+/**
+ * Fill plane data pointers for an image with pixel format pix_fmt and
+ * height height.
+ *
+ * @param data pointers array to be filled with the pointer for each image plane
+ * @param ptr the pointer to a buffer which will contain the image
+ * @param linesizes the array containing the linesize for each
+ * plane, should be filled by av_image_fill_linesizes()
+ * @return the size in bytes required for the image buffer, a negative
+ * error code in case of failure
+ */
+int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height,
+                           uint8_t *ptr, const int linesizes[4]);
+
+/**
+ * Allocate an image with size w and h and pixel format pix_fmt, and
+ * fill pointers and linesizes accordingly.
+ * The allocated image buffer has to be freed by using
+ * av_freep(&pointers[0]).
+ *
+ * @param align the value to use for buffer size alignment
+ * @return the size in bytes required for the image buffer, a negative
+ * error code in case of failure
+ */
+int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
+                   int w, int h, enum AVPixelFormat pix_fmt, int align);
+
+/**
+ * Copy image plane from src to dst.
+ * That is, copy "height" number of lines of "bytewidth" bytes each.
+ * The first byte of each successive line is separated by *_linesize
+ * bytes.
+ *
+ * bytewidth must be contained by both absolute values of dst_linesize
+ * and src_linesize, otherwise the function behavior is undefined.
+ *
+ * @param dst_linesize linesize for the image plane in dst
+ * @param src_linesize linesize for the image plane in src
+ */
+void av_image_copy_plane(uint8_t       *dst, int dst_linesize,
+                         const uint8_t *src, int src_linesize,
+                         int bytewidth, int height);
+
+/**
+ * Copy image in src_data to dst_data.
+ *
+ * @param dst_linesizes linesizes for the image in dst_data
+ * @param src_linesizes linesizes for the image in src_data
+ */
+void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4],
+                   const uint8_t *src_data[4], const int src_linesizes[4],
+                   enum AVPixelFormat pix_fmt, int width, int height);
+
+/**
+ * Copy image data located in uncacheable (e.g. GPU mapped) memory. Where
+ * available, this function will use special functionality for reading from such
+ * memory, which may result in greatly improved performance compared to plain
+ * av_image_copy().
+ *
+ * The data pointers and the linesizes must be aligned to the maximum required
+ * by the CPU architecture.
+ *
+ * @note The linesize parameters have the type ptrdiff_t here, while they are
+ *       int for av_image_copy().
+ * @note On x86, the linesizes currently need to be aligned to the cacheline
+ *       size (i.e. 64) to get improved performance.
+ */
+void av_image_copy_uc_from(uint8_t *dst_data[4],       const ptrdiff_t dst_linesizes[4],
+                           const uint8_t *src_data[4], const ptrdiff_t src_linesizes[4],
+                           enum AVPixelFormat pix_fmt, int width, int height);
+
+/**
+ * Setup the data pointers and linesizes based on the specified image
+ * parameters and the provided array.
+ *
+ * The fields of the given image are filled in by using the src
+ * address which points to the image data buffer. Depending on the
+ * specified pixel format, one or multiple image data pointers and
+ * line sizes will be set.  If a planar format is specified, several
+ * pointers will be set pointing to the different picture planes and
+ * the line sizes of the different planes will be stored in the
+ * lines_sizes array. Call with src == NULL to get the required
+ * size for the src buffer.
+ *
+ * To allocate the buffer and fill in the dst_data and dst_linesize in
+ * one call, use av_image_alloc().
+ *
+ * @param dst_data      data pointers to be filled in
+ * @param dst_linesize  linesizes for the image in dst_data to be filled in
+ * @param src           buffer which will contain or contains the actual image data, can be NULL
+ * @param pix_fmt       the pixel format of the image
+ * @param width         the width of the image in pixels
+ * @param height        the height of the image in pixels
+ * @param align         the value used in src for linesize alignment
+ * @return the size in bytes required for src, a negative error code
+ * in case of failure
+ */
+int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
+                         const uint8_t *src,
+                         enum AVPixelFormat pix_fmt, int width, int height, int align);
+
+/**
+ * Return the size in bytes of the amount of data required to store an
+ * image with the given parameters.
+ *
+ * @param pix_fmt  the pixel format of the image
+ * @param width    the width of the image in pixels
+ * @param height   the height of the image in pixels
+ * @param align    the assumed linesize alignment
+ * @return the buffer size in bytes, a negative error code in case of failure
+ */
+int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
+
+/**
+ * Copy image data from an image into a buffer.
+ *
+ * av_image_get_buffer_size() can be used to compute the required size
+ * for the buffer to fill.
+ *
+ * @param dst           a buffer into which picture data will be copied
+ * @param dst_size      the size in bytes of dst
+ * @param src_data      pointers containing the source image data
+ * @param src_linesize  linesizes for the image in src_data
+ * @param pix_fmt       the pixel format of the source image
+ * @param width         the width of the source image in pixels
+ * @param height        the height of the source image in pixels
+ * @param align         the assumed linesize alignment for dst
+ * @return the number of bytes written to dst, or a negative value
+ * (error code) on error
+ */
+int av_image_copy_to_buffer(uint8_t *dst, int dst_size,
+                            const uint8_t * const src_data[4], const int src_linesize[4],
+                            enum AVPixelFormat pix_fmt, int width, int height, int align);
+
+/**
+ * Check if the given dimension of an image is valid, meaning that all
+ * bytes of the image can be addressed with a signed int.
+ *
+ * @param w the width of the picture
+ * @param h the height of the picture
+ * @param log_offset the offset to sum to the log level for logging with log_ctx
+ * @param log_ctx the parent logging context, it may be NULL
+ * @return >= 0 if valid, a negative error code otherwise
+ */
+int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx);
+
+/**
+ * Check if the given dimension of an image is valid, meaning that all
+ * bytes of a plane of an image with the specified pix_fmt can be addressed
+ * with a signed int.
+ *
+ * @param w the width of the picture
+ * @param h the height of the picture
+ * @param max_pixels the maximum number of pixels the user wants to accept
+ * @param pix_fmt the pixel format, can be AV_PIX_FMT_NONE if unknown.
+ * @param log_offset the offset to sum to the log level for logging with log_ctx
+ * @param log_ctx the parent logging context, it may be NULL
+ * @return >= 0 if valid, a negative error code otherwise
+ */
+int av_image_check_size2(unsigned int w, unsigned int h, int64_t max_pixels, enum AVPixelFormat pix_fmt, int log_offset, void *log_ctx);
+
+/**
+ * Check if the given sample aspect ratio of an image is valid.
+ *
+ * It is considered invalid if the denominator is 0 or if applying the ratio
+ * to the image size would make the smaller dimension less than 1. If the
+ * sar numerator is 0, it is considered unknown and will return as valid.
+ *
+ * @param w width of the image
+ * @param h height of the image
+ * @param sar sample aspect ratio of the image
+ * @return 0 if valid, a negative AVERROR code otherwise
+ */
+int av_image_check_sar(unsigned int w, unsigned int h, AVRational sar);
+
+/**
+ * Overwrite the image data with black. This is suitable for filling a
+ * sub-rectangle of an image, meaning the padding between the right most pixel
+ * and the left most pixel on the next line will not be overwritten. For some
+ * formats, the image size might be rounded up due to inherent alignment.
+ *
+ * If the pixel format has alpha, the alpha is cleared to opaque.
+ *
+ * This can return an error if the pixel format is not supported. Normally, all
+ * non-hwaccel pixel formats should be supported.
+ *
+ * Passing NULL for dst_data is allowed. Then the function returns whether the
+ * operation would have succeeded. (It can return an error if the pix_fmt is
+ * not supported.)
+ *
+ * @param dst_data      data pointers to destination image
+ * @param dst_linesize  linesizes for the destination image
+ * @param pix_fmt       the pixel format of the image
+ * @param range         the color range of the image (important for colorspaces such as YUV)
+ * @param width         the width of the image in pixels
+ * @param height        the height of the image in pixels
+ * @return 0 if the image data was cleared, a negative AVERROR code otherwise
+ */
+int av_image_fill_black(uint8_t *dst_data[4], const ptrdiff_t dst_linesize[4],
+                        enum AVPixelFormat pix_fmt, enum AVColorRange range,
+                        int width, int height);
+
+/**
+ * @}
+ */
+
+
+#endif /* AVUTIL_IMGUTILS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intfloat.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intfloat.h
new file mode 100644
index 0000000..fe3d7ec
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intfloat.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2011 Mans Rullgard
+ *
+ * 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 AVUTIL_INTFLOAT_H
+#define AVUTIL_INTFLOAT_H
+
+#include <stdint.h>
+#include "attributes.h"
+
+union av_intfloat32 {
+    uint32_t i;
+    float    f;
+};
+
+union av_intfloat64 {
+    uint64_t i;
+    double   f;
+};
+
+/**
+ * Reinterpret a 32-bit integer as a float.
+ */
+static av_always_inline float av_int2float(uint32_t i)
+{
+    union av_intfloat32 v;
+    v.i = i;
+    return v.f;
+}
+
+/**
+ * Reinterpret a float as a 32-bit integer.
+ */
+static av_always_inline uint32_t av_float2int(float f)
+{
+    union av_intfloat32 v;
+    v.f = f;
+    return v.i;
+}
+
+/**
+ * Reinterpret a 64-bit integer as a double.
+ */
+static av_always_inline double av_int2double(uint64_t i)
+{
+    union av_intfloat64 v;
+    v.i = i;
+    return v.f;
+}
+
+/**
+ * Reinterpret a double as a 64-bit integer.
+ */
+static av_always_inline uint64_t av_double2int(double f)
+{
+    union av_intfloat64 v;
+    v.f = f;
+    return v.i;
+}
+
+#endif /* AVUTIL_INTFLOAT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intreadwrite.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intreadwrite.h
new file mode 100644
index 0000000..67c763b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/intreadwrite.h
@@ -0,0 +1,629 @@
+/*
+ * 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 AVUTIL_INTREADWRITE_H
+#define AVUTIL_INTREADWRITE_H
+
+#include <stdint.h>
+#include "libavutil/avconfig.h"
+#include "attributes.h"
+#include "bswap.h"
+
+typedef union {
+    uint64_t u64;
+    uint32_t u32[2];
+    uint16_t u16[4];
+    uint8_t  u8 [8];
+    double   f64;
+    float    f32[2];
+} av_alias av_alias64;
+
+typedef union {
+    uint32_t u32;
+    uint16_t u16[2];
+    uint8_t  u8 [4];
+    float    f32;
+} av_alias av_alias32;
+
+typedef union {
+    uint16_t u16;
+    uint8_t  u8 [2];
+} av_alias av_alias16;
+
+/*
+ * Arch-specific headers can provide any combination of
+ * AV_[RW][BLN](16|24|32|48|64) and AV_(COPY|SWAP|ZERO)(64|128) macros.
+ * Preprocessor symbols must be defined, even if these are implemented
+ * as inline functions.
+ *
+ * R/W means read/write, B/L/N means big/little/native endianness.
+ * The following macros require aligned access, compared to their
+ * unaligned variants: AV_(COPY|SWAP|ZERO)(64|128), AV_[RW]N[8-64]A.
+ * Incorrect usage may range from abysmal performance to crash
+ * depending on the platform.
+ *
+ * The unaligned variants are AV_[RW][BLN][8-64] and AV_COPY*U.
+ */
+
+#ifdef HAVE_AV_CONFIG_H
+
+#include "config.h"
+
+#if   ARCH_ARM
+#   include "arm/intreadwrite.h"
+#elif ARCH_AVR32
+#   include "avr32/intreadwrite.h"
+#elif ARCH_MIPS
+#   include "mips/intreadwrite.h"
+#elif ARCH_PPC
+#   include "ppc/intreadwrite.h"
+#elif ARCH_TOMI
+#   include "tomi/intreadwrite.h"
+#elif ARCH_X86
+#   include "x86/intreadwrite.h"
+#endif
+
+#endif /* HAVE_AV_CONFIG_H */
+
+/*
+ * Map AV_RNXX <-> AV_R[BL]XX for all variants provided by per-arch headers.
+ */
+
+#if AV_HAVE_BIGENDIAN
+
+#   if    defined(AV_RN16) && !defined(AV_RB16)
+#       define AV_RB16(p) AV_RN16(p)
+#   elif !defined(AV_RN16) &&  defined(AV_RB16)
+#       define AV_RN16(p) AV_RB16(p)
+#   endif
+
+#   if    defined(AV_WN16) && !defined(AV_WB16)
+#       define AV_WB16(p, v) AV_WN16(p, v)
+#   elif !defined(AV_WN16) &&  defined(AV_WB16)
+#       define AV_WN16(p, v) AV_WB16(p, v)
+#   endif
+
+#   if    defined(AV_RN24) && !defined(AV_RB24)
+#       define AV_RB24(p) AV_RN24(p)
+#   elif !defined(AV_RN24) &&  defined(AV_RB24)
+#       define AV_RN24(p) AV_RB24(p)
+#   endif
+
+#   if    defined(AV_WN24) && !defined(AV_WB24)
+#       define AV_WB24(p, v) AV_WN24(p, v)
+#   elif !defined(AV_WN24) &&  defined(AV_WB24)
+#       define AV_WN24(p, v) AV_WB24(p, v)
+#   endif
+
+#   if    defined(AV_RN32) && !defined(AV_RB32)
+#       define AV_RB32(p) AV_RN32(p)
+#   elif !defined(AV_RN32) &&  defined(AV_RB32)
+#       define AV_RN32(p) AV_RB32(p)
+#   endif
+
+#   if    defined(AV_WN32) && !defined(AV_WB32)
+#       define AV_WB32(p, v) AV_WN32(p, v)
+#   elif !defined(AV_WN32) &&  defined(AV_WB32)
+#       define AV_WN32(p, v) AV_WB32(p, v)
+#   endif
+
+#   if    defined(AV_RN48) && !defined(AV_RB48)
+#       define AV_RB48(p) AV_RN48(p)
+#   elif !defined(AV_RN48) &&  defined(AV_RB48)
+#       define AV_RN48(p) AV_RB48(p)
+#   endif
+
+#   if    defined(AV_WN48) && !defined(AV_WB48)
+#       define AV_WB48(p, v) AV_WN48(p, v)
+#   elif !defined(AV_WN48) &&  defined(AV_WB48)
+#       define AV_WN48(p, v) AV_WB48(p, v)
+#   endif
+
+#   if    defined(AV_RN64) && !defined(AV_RB64)
+#       define AV_RB64(p) AV_RN64(p)
+#   elif !defined(AV_RN64) &&  defined(AV_RB64)
+#       define AV_RN64(p) AV_RB64(p)
+#   endif
+
+#   if    defined(AV_WN64) && !defined(AV_WB64)
+#       define AV_WB64(p, v) AV_WN64(p, v)
+#   elif !defined(AV_WN64) &&  defined(AV_WB64)
+#       define AV_WN64(p, v) AV_WB64(p, v)
+#   endif
+
+#else /* AV_HAVE_BIGENDIAN */
+
+#   if    defined(AV_RN16) && !defined(AV_RL16)
+#       define AV_RL16(p) AV_RN16(p)
+#   elif !defined(AV_RN16) &&  defined(AV_RL16)
+#       define AV_RN16(p) AV_RL16(p)
+#   endif
+
+#   if    defined(AV_WN16) && !defined(AV_WL16)
+#       define AV_WL16(p, v) AV_WN16(p, v)
+#   elif !defined(AV_WN16) &&  defined(AV_WL16)
+#       define AV_WN16(p, v) AV_WL16(p, v)
+#   endif
+
+#   if    defined(AV_RN24) && !defined(AV_RL24)
+#       define AV_RL24(p) AV_RN24(p)
+#   elif !defined(AV_RN24) &&  defined(AV_RL24)
+#       define AV_RN24(p) AV_RL24(p)
+#   endif
+
+#   if    defined(AV_WN24) && !defined(AV_WL24)
+#       define AV_WL24(p, v) AV_WN24(p, v)
+#   elif !defined(AV_WN24) &&  defined(AV_WL24)
+#       define AV_WN24(p, v) AV_WL24(p, v)
+#   endif
+
+#   if    defined(AV_RN32) && !defined(AV_RL32)
+#       define AV_RL32(p) AV_RN32(p)
+#   elif !defined(AV_RN32) &&  defined(AV_RL32)
+#       define AV_RN32(p) AV_RL32(p)
+#   endif
+
+#   if    defined(AV_WN32) && !defined(AV_WL32)
+#       define AV_WL32(p, v) AV_WN32(p, v)
+#   elif !defined(AV_WN32) &&  defined(AV_WL32)
+#       define AV_WN32(p, v) AV_WL32(p, v)
+#   endif
+
+#   if    defined(AV_RN48) && !defined(AV_RL48)
+#       define AV_RL48(p) AV_RN48(p)
+#   elif !defined(AV_RN48) &&  defined(AV_RL48)
+#       define AV_RN48(p) AV_RL48(p)
+#   endif
+
+#   if    defined(AV_WN48) && !defined(AV_WL48)
+#       define AV_WL48(p, v) AV_WN48(p, v)
+#   elif !defined(AV_WN48) &&  defined(AV_WL48)
+#       define AV_WN48(p, v) AV_WL48(p, v)
+#   endif
+
+#   if    defined(AV_RN64) && !defined(AV_RL64)
+#       define AV_RL64(p) AV_RN64(p)
+#   elif !defined(AV_RN64) &&  defined(AV_RL64)
+#       define AV_RN64(p) AV_RL64(p)
+#   endif
+
+#   if    defined(AV_WN64) && !defined(AV_WL64)
+#       define AV_WL64(p, v) AV_WN64(p, v)
+#   elif !defined(AV_WN64) &&  defined(AV_WL64)
+#       define AV_WN64(p, v) AV_WL64(p, v)
+#   endif
+
+#endif /* !AV_HAVE_BIGENDIAN */
+
+/*
+ * Define AV_[RW]N helper macros to simplify definitions not provided
+ * by per-arch headers.
+ */
+
+#if defined(__GNUC__)
+
+union unaligned_64 { uint64_t l; } __attribute__((packed)) av_alias;
+union unaligned_32 { uint32_t l; } __attribute__((packed)) av_alias;
+union unaligned_16 { uint16_t l; } __attribute__((packed)) av_alias;
+
+#   define AV_RN(s, p) (((const union unaligned_##s *) (p))->l)
+#   define AV_WN(s, p, v) ((((union unaligned_##s *) (p))->l) = (v))
+
+#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_X64) || defined(_M_ARM64)) && AV_HAVE_FAST_UNALIGNED
+
+#   define AV_RN(s, p) (*((const __unaligned uint##s##_t*)(p)))
+#   define AV_WN(s, p, v) (*((__unaligned uint##s##_t*)(p)) = (v))
+
+#elif AV_HAVE_FAST_UNALIGNED
+
+#   define AV_RN(s, p) (((const av_alias##s*)(p))->u##s)
+#   define AV_WN(s, p, v) (((av_alias##s*)(p))->u##s = (v))
+
+#else
+
+#ifndef AV_RB16
+#   define AV_RB16(x)                           \
+    ((((const uint8_t*)(x))[0] << 8) |          \
+      ((const uint8_t*)(x))[1])
+#endif
+#ifndef AV_WB16
+#   define AV_WB16(p, val) do {                 \
+        uint16_t d = (val);                     \
+        ((uint8_t*)(p))[1] = (d);               \
+        ((uint8_t*)(p))[0] = (d)>>8;            \
+    } while(0)
+#endif
+
+#ifndef AV_RL16
+#   define AV_RL16(x)                           \
+    ((((const uint8_t*)(x))[1] << 8) |          \
+      ((const uint8_t*)(x))[0])
+#endif
+#ifndef AV_WL16
+#   define AV_WL16(p, val) do {                 \
+        uint16_t d = (val);                     \
+        ((uint8_t*)(p))[0] = (d);               \
+        ((uint8_t*)(p))[1] = (d)>>8;            \
+    } while(0)
+#endif
+
+#ifndef AV_RB32
+#   define AV_RB32(x)                                \
+    (((uint32_t)((const uint8_t*)(x))[0] << 24) |    \
+               (((const uint8_t*)(x))[1] << 16) |    \
+               (((const uint8_t*)(x))[2] <<  8) |    \
+                ((const uint8_t*)(x))[3])
+#endif
+#ifndef AV_WB32
+#   define AV_WB32(p, val) do {                 \
+        uint32_t d = (val);                     \
+        ((uint8_t*)(p))[3] = (d);               \
+        ((uint8_t*)(p))[2] = (d)>>8;            \
+        ((uint8_t*)(p))[1] = (d)>>16;           \
+        ((uint8_t*)(p))[0] = (d)>>24;           \
+    } while(0)
+#endif
+
+#ifndef AV_RL32
+#   define AV_RL32(x)                                \
+    (((uint32_t)((const uint8_t*)(x))[3] << 24) |    \
+               (((const uint8_t*)(x))[2] << 16) |    \
+               (((const uint8_t*)(x))[1] <<  8) |    \
+                ((const uint8_t*)(x))[0])
+#endif
+#ifndef AV_WL32
+#   define AV_WL32(p, val) do {                 \
+        uint32_t d = (val);                     \
+        ((uint8_t*)(p))[0] = (d);               \
+        ((uint8_t*)(p))[1] = (d)>>8;            \
+        ((uint8_t*)(p))[2] = (d)>>16;           \
+        ((uint8_t*)(p))[3] = (d)>>24;           \
+    } while(0)
+#endif
+
+#ifndef AV_RB64
+#   define AV_RB64(x)                                   \
+    (((uint64_t)((const uint8_t*)(x))[0] << 56) |       \
+     ((uint64_t)((const uint8_t*)(x))[1] << 48) |       \
+     ((uint64_t)((const uint8_t*)(x))[2] << 40) |       \
+     ((uint64_t)((const uint8_t*)(x))[3] << 32) |       \
+     ((uint64_t)((const uint8_t*)(x))[4] << 24) |       \
+     ((uint64_t)((const uint8_t*)(x))[5] << 16) |       \
+     ((uint64_t)((const uint8_t*)(x))[6] <<  8) |       \
+      (uint64_t)((const uint8_t*)(x))[7])
+#endif
+#ifndef AV_WB64
+#   define AV_WB64(p, val) do {                 \
+        uint64_t d = (val);                     \
+        ((uint8_t*)(p))[7] = (d);               \
+        ((uint8_t*)(p))[6] = (d)>>8;            \
+        ((uint8_t*)(p))[5] = (d)>>16;           \
+        ((uint8_t*)(p))[4] = (d)>>24;           \
+        ((uint8_t*)(p))[3] = (d)>>32;           \
+        ((uint8_t*)(p))[2] = (d)>>40;           \
+        ((uint8_t*)(p))[1] = (d)>>48;           \
+        ((uint8_t*)(p))[0] = (d)>>56;           \
+    } while(0)
+#endif
+
+#ifndef AV_RL64
+#   define AV_RL64(x)                                   \
+    (((uint64_t)((const uint8_t*)(x))[7] << 56) |       \
+     ((uint64_t)((const uint8_t*)(x))[6] << 48) |       \
+     ((uint64_t)((const uint8_t*)(x))[5] << 40) |       \
+     ((uint64_t)((const uint8_t*)(x))[4] << 32) |       \
+     ((uint64_t)((const uint8_t*)(x))[3] << 24) |       \
+     ((uint64_t)((const uint8_t*)(x))[2] << 16) |       \
+     ((uint64_t)((const uint8_t*)(x))[1] <<  8) |       \
+      (uint64_t)((const uint8_t*)(x))[0])
+#endif
+#ifndef AV_WL64
+#   define AV_WL64(p, val) do {                 \
+        uint64_t d = (val);                     \
+        ((uint8_t*)(p))[0] = (d);               \
+        ((uint8_t*)(p))[1] = (d)>>8;            \
+        ((uint8_t*)(p))[2] = (d)>>16;           \
+        ((uint8_t*)(p))[3] = (d)>>24;           \
+        ((uint8_t*)(p))[4] = (d)>>32;           \
+        ((uint8_t*)(p))[5] = (d)>>40;           \
+        ((uint8_t*)(p))[6] = (d)>>48;           \
+        ((uint8_t*)(p))[7] = (d)>>56;           \
+    } while(0)
+#endif
+
+#if AV_HAVE_BIGENDIAN
+#   define AV_RN(s, p)    AV_RB##s(p)
+#   define AV_WN(s, p, v) AV_WB##s(p, v)
+#else
+#   define AV_RN(s, p)    AV_RL##s(p)
+#   define AV_WN(s, p, v) AV_WL##s(p, v)
+#endif
+
+#endif /* HAVE_FAST_UNALIGNED */
+
+#ifndef AV_RN16
+#   define AV_RN16(p) AV_RN(16, p)
+#endif
+
+#ifndef AV_RN32
+#   define AV_RN32(p) AV_RN(32, p)
+#endif
+
+#ifndef AV_RN64
+#   define AV_RN64(p) AV_RN(64, p)
+#endif
+
+#ifndef AV_WN16
+#   define AV_WN16(p, v) AV_WN(16, p, v)
+#endif
+
+#ifndef AV_WN32
+#   define AV_WN32(p, v) AV_WN(32, p, v)
+#endif
+
+#ifndef AV_WN64
+#   define AV_WN64(p, v) AV_WN(64, p, v)
+#endif
+
+#if AV_HAVE_BIGENDIAN
+#   define AV_RB(s, p)    AV_RN##s(p)
+#   define AV_WB(s, p, v) AV_WN##s(p, v)
+#   define AV_RL(s, p)    av_bswap##s(AV_RN##s(p))
+#   define AV_WL(s, p, v) AV_WN##s(p, av_bswap##s(v))
+#else
+#   define AV_RB(s, p)    av_bswap##s(AV_RN##s(p))
+#   define AV_WB(s, p, v) AV_WN##s(p, av_bswap##s(v))
+#   define AV_RL(s, p)    AV_RN##s(p)
+#   define AV_WL(s, p, v) AV_WN##s(p, v)
+#endif
+
+#define AV_RB8(x)     (((const uint8_t*)(x))[0])
+#define AV_WB8(p, d)  do { ((uint8_t*)(p))[0] = (d); } while(0)
+
+#define AV_RL8(x)     AV_RB8(x)
+#define AV_WL8(p, d)  AV_WB8(p, d)
+
+#ifndef AV_RB16
+#   define AV_RB16(p)    AV_RB(16, p)
+#endif
+#ifndef AV_WB16
+#   define AV_WB16(p, v) AV_WB(16, p, v)
+#endif
+
+#ifndef AV_RL16
+#   define AV_RL16(p)    AV_RL(16, p)
+#endif
+#ifndef AV_WL16
+#   define AV_WL16(p, v) AV_WL(16, p, v)
+#endif
+
+#ifndef AV_RB32
+#   define AV_RB32(p)    AV_RB(32, p)
+#endif
+#ifndef AV_WB32
+#   define AV_WB32(p, v) AV_WB(32, p, v)
+#endif
+
+#ifndef AV_RL32
+#   define AV_RL32(p)    AV_RL(32, p)
+#endif
+#ifndef AV_WL32
+#   define AV_WL32(p, v) AV_WL(32, p, v)
+#endif
+
+#ifndef AV_RB64
+#   define AV_RB64(p)    AV_RB(64, p)
+#endif
+#ifndef AV_WB64
+#   define AV_WB64(p, v) AV_WB(64, p, v)
+#endif
+
+#ifndef AV_RL64
+#   define AV_RL64(p)    AV_RL(64, p)
+#endif
+#ifndef AV_WL64
+#   define AV_WL64(p, v) AV_WL(64, p, v)
+#endif
+
+#ifndef AV_RB24
+#   define AV_RB24(x)                           \
+    ((((const uint8_t*)(x))[0] << 16) |         \
+     (((const uint8_t*)(x))[1] <<  8) |         \
+      ((const uint8_t*)(x))[2])
+#endif
+#ifndef AV_WB24
+#   define AV_WB24(p, d) do {                   \
+        ((uint8_t*)(p))[2] = (d);               \
+        ((uint8_t*)(p))[1] = (d)>>8;            \
+        ((uint8_t*)(p))[0] = (d)>>16;           \
+    } while(0)
+#endif
+
+#ifndef AV_RL24
+#   define AV_RL24(x)                           \
+    ((((const uint8_t*)(x))[2] << 16) |         \
+     (((const uint8_t*)(x))[1] <<  8) |         \
+      ((const uint8_t*)(x))[0])
+#endif
+#ifndef AV_WL24
+#   define AV_WL24(p, d) do {                   \
+        ((uint8_t*)(p))[0] = (d);               \
+        ((uint8_t*)(p))[1] = (d)>>8;            \
+        ((uint8_t*)(p))[2] = (d)>>16;           \
+    } while(0)
+#endif
+
+#ifndef AV_RB48
+#   define AV_RB48(x)                                     \
+    (((uint64_t)((const uint8_t*)(x))[0] << 40) |         \
+     ((uint64_t)((const uint8_t*)(x))[1] << 32) |         \
+     ((uint64_t)((const uint8_t*)(x))[2] << 24) |         \
+     ((uint64_t)((const uint8_t*)(x))[3] << 16) |         \
+     ((uint64_t)((const uint8_t*)(x))[4] <<  8) |         \
+      (uint64_t)((const uint8_t*)(x))[5])
+#endif
+#ifndef AV_WB48
+#   define AV_WB48(p, darg) do {                \
+        uint64_t d = (darg);                    \
+        ((uint8_t*)(p))[5] = (d);               \
+        ((uint8_t*)(p))[4] = (d)>>8;            \
+        ((uint8_t*)(p))[3] = (d)>>16;           \
+        ((uint8_t*)(p))[2] = (d)>>24;           \
+        ((uint8_t*)(p))[1] = (d)>>32;           \
+        ((uint8_t*)(p))[0] = (d)>>40;           \
+    } while(0)
+#endif
+
+#ifndef AV_RL48
+#   define AV_RL48(x)                                     \
+    (((uint64_t)((const uint8_t*)(x))[5] << 40) |         \
+     ((uint64_t)((const uint8_t*)(x))[4] << 32) |         \
+     ((uint64_t)((const uint8_t*)(x))[3] << 24) |         \
+     ((uint64_t)((const uint8_t*)(x))[2] << 16) |         \
+     ((uint64_t)((const uint8_t*)(x))[1] <<  8) |         \
+      (uint64_t)((const uint8_t*)(x))[0])
+#endif
+#ifndef AV_WL48
+#   define AV_WL48(p, darg) do {                \
+        uint64_t d = (darg);                    \
+        ((uint8_t*)(p))[0] = (d);               \
+        ((uint8_t*)(p))[1] = (d)>>8;            \
+        ((uint8_t*)(p))[2] = (d)>>16;           \
+        ((uint8_t*)(p))[3] = (d)>>24;           \
+        ((uint8_t*)(p))[4] = (d)>>32;           \
+        ((uint8_t*)(p))[5] = (d)>>40;           \
+    } while(0)
+#endif
+
+/*
+ * The AV_[RW]NA macros access naturally aligned data
+ * in a type-safe way.
+ */
+
+#define AV_RNA(s, p)    (((const av_alias##s*)(p))->u##s)
+#define AV_WNA(s, p, v) (((av_alias##s*)(p))->u##s = (v))
+
+#ifndef AV_RN16A
+#   define AV_RN16A(p) AV_RNA(16, p)
+#endif
+
+#ifndef AV_RN32A
+#   define AV_RN32A(p) AV_RNA(32, p)
+#endif
+
+#ifndef AV_RN64A
+#   define AV_RN64A(p) AV_RNA(64, p)
+#endif
+
+#ifndef AV_WN16A
+#   define AV_WN16A(p, v) AV_WNA(16, p, v)
+#endif
+
+#ifndef AV_WN32A
+#   define AV_WN32A(p, v) AV_WNA(32, p, v)
+#endif
+
+#ifndef AV_WN64A
+#   define AV_WN64A(p, v) AV_WNA(64, p, v)
+#endif
+
+/*
+ * The AV_COPYxxU macros are suitable for copying data to/from unaligned
+ * memory locations.
+ */
+
+#define AV_COPYU(n, d, s) AV_WN##n(d, AV_RN##n(s));
+
+#ifndef AV_COPY16U
+#   define AV_COPY16U(d, s) AV_COPYU(16, d, s)
+#endif
+
+#ifndef AV_COPY32U
+#   define AV_COPY32U(d, s) AV_COPYU(32, d, s)
+#endif
+
+#ifndef AV_COPY64U
+#   define AV_COPY64U(d, s) AV_COPYU(64, d, s)
+#endif
+
+#ifndef AV_COPY128U
+#   define AV_COPY128U(d, s)                                    \
+    do {                                                        \
+        AV_COPY64U(d, s);                                       \
+        AV_COPY64U((char *)(d) + 8, (const char *)(s) + 8);     \
+    } while(0)
+#endif
+
+/* Parameters for AV_COPY*, AV_SWAP*, AV_ZERO* must be
+ * naturally aligned. They may be implemented using MMX,
+ * so emms_c() must be called before using any float code
+ * afterwards.
+ */
+
+#define AV_COPY(n, d, s) \
+    (((av_alias##n*)(d))->u##n = ((const av_alias##n*)(s))->u##n)
+
+#ifndef AV_COPY16
+#   define AV_COPY16(d, s) AV_COPY(16, d, s)
+#endif
+
+#ifndef AV_COPY32
+#   define AV_COPY32(d, s) AV_COPY(32, d, s)
+#endif
+
+#ifndef AV_COPY64
+#   define AV_COPY64(d, s) AV_COPY(64, d, s)
+#endif
+
+#ifndef AV_COPY128
+#   define AV_COPY128(d, s)                    \
+    do {                                       \
+        AV_COPY64(d, s);                       \
+        AV_COPY64((char*)(d)+8, (char*)(s)+8); \
+    } while(0)
+#endif
+
+#define AV_SWAP(n, a, b) FFSWAP(av_alias##n, *(av_alias##n*)(a), *(av_alias##n*)(b))
+
+#ifndef AV_SWAP64
+#   define AV_SWAP64(a, b) AV_SWAP(64, a, b)
+#endif
+
+#define AV_ZERO(n, d) (((av_alias##n*)(d))->u##n = 0)
+
+#ifndef AV_ZERO16
+#   define AV_ZERO16(d) AV_ZERO(16, d)
+#endif
+
+#ifndef AV_ZERO32
+#   define AV_ZERO32(d) AV_ZERO(32, d)
+#endif
+
+#ifndef AV_ZERO64
+#   define AV_ZERO64(d) AV_ZERO(64, d)
+#endif
+
+#ifndef AV_ZERO128
+#   define AV_ZERO128(d)         \
+    do {                         \
+        AV_ZERO64(d);            \
+        AV_ZERO64((char*)(d)+8); \
+    } while(0)
+#endif
+
+#endif /* AVUTIL_INTREADWRITE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lfg.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lfg.h
new file mode 100644
index 0000000..03f779a
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lfg.h
@@ -0,0 +1,71 @@
+/*
+ * Lagged Fibonacci PRNG
+ * Copyright (c) 2008 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 AVUTIL_LFG_H
+#define AVUTIL_LFG_H
+
+#include <stdint.h>
+
+typedef struct AVLFG {
+    unsigned int state[64];
+    int index;
+} AVLFG;
+
+void av_lfg_init(AVLFG *c, unsigned int seed);
+
+/**
+ * Seed the state of the ALFG using binary data.
+ *
+ * Return value: 0 on success, negative value (AVERROR) on failure.
+ */
+int av_lfg_init_from_data(AVLFG *c, const uint8_t *data, unsigned int length);
+
+/**
+ * Get the next random unsigned 32-bit number using an ALFG.
+ *
+ * Please also consider a simple LCG like state= state*1664525+1013904223,
+ * it may be good enough and faster for your specific use case.
+ */
+static inline unsigned int av_lfg_get(AVLFG *c){
+    c->state[c->index & 63] = c->state[(c->index-24) & 63] + c->state[(c->index-55) & 63];
+    return c->state[c->index++ & 63];
+}
+
+/**
+ * Get the next random unsigned 32-bit number using a MLFG.
+ *
+ * Please also consider av_lfg_get() above, it is faster.
+ */
+static inline unsigned int av_mlfg_get(AVLFG *c){
+    unsigned int a= c->state[(c->index-55) & 63];
+    unsigned int b= c->state[(c->index-24) & 63];
+    return c->state[c->index++ & 63] = 2*a*b+a+b;
+}
+
+/**
+ * Get the next two numbers generated by a Box-Muller Gaussian
+ * generator using the random numbers issued by lfg.
+ *
+ * @param out array where the two generated numbers are placed
+ */
+void av_bmg_get(AVLFG *lfg, double out[2]);
+
+#endif /* AVUTIL_LFG_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/log.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/log.h
new file mode 100644
index 0000000..d9554e6
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/log.h
@@ -0,0 +1,362 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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 AVUTIL_LOG_H
+#define AVUTIL_LOG_H
+
+#include <stdarg.h>
+#include "avutil.h"
+#include "attributes.h"
+#include "version.h"
+
+typedef enum {
+    AV_CLASS_CATEGORY_NA = 0,
+    AV_CLASS_CATEGORY_INPUT,
+    AV_CLASS_CATEGORY_OUTPUT,
+    AV_CLASS_CATEGORY_MUXER,
+    AV_CLASS_CATEGORY_DEMUXER,
+    AV_CLASS_CATEGORY_ENCODER,
+    AV_CLASS_CATEGORY_DECODER,
+    AV_CLASS_CATEGORY_FILTER,
+    AV_CLASS_CATEGORY_BITSTREAM_FILTER,
+    AV_CLASS_CATEGORY_SWSCALER,
+    AV_CLASS_CATEGORY_SWRESAMPLER,
+    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
+    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
+    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
+    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
+    AV_CLASS_CATEGORY_DEVICE_OUTPUT,
+    AV_CLASS_CATEGORY_DEVICE_INPUT,
+    AV_CLASS_CATEGORY_NB  ///< not part of ABI/API
+}AVClassCategory;
+
+#define AV_IS_INPUT_DEVICE(category) \
+    (((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT) || \
+     ((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT) || \
+     ((category) == AV_CLASS_CATEGORY_DEVICE_INPUT))
+
+#define AV_IS_OUTPUT_DEVICE(category) \
+    (((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT) || \
+     ((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT) || \
+     ((category) == AV_CLASS_CATEGORY_DEVICE_OUTPUT))
+
+struct AVOptionRanges;
+
+/**
+ * Describe the class of an AVClass context structure. That is an
+ * arbitrary struct of which the first field is a pointer to an
+ * AVClass struct (e.g. AVCodecContext, AVFormatContext etc.).
+ */
+typedef struct AVClass {
+    /**
+     * The name of the class; usually it is the same name as the
+     * context structure type to which the AVClass is associated.
+     */
+    const char* class_name;
+
+    /**
+     * A pointer to a function which returns the name of a context
+     * instance ctx associated with the class.
+     */
+    const char* (*item_name)(void* ctx);
+
+    /**
+     * a pointer to the first option specified in the class if any or NULL
+     *
+     * @see av_set_default_options()
+     */
+    const struct AVOption *option;
+
+    /**
+     * LIBAVUTIL_VERSION with which this structure was created.
+     * This is used to allow fields to be added without requiring major
+     * version bumps everywhere.
+     */
+
+    int version;
+
+    /**
+     * Offset in the structure where log_level_offset is stored.
+     * 0 means there is no such variable
+     */
+    int log_level_offset_offset;
+
+    /**
+     * Offset in the structure where a pointer to the parent context for
+     * logging is stored. For example a decoder could pass its AVCodecContext
+     * to eval as such a parent context, which an av_log() implementation
+     * could then leverage to display the parent context.
+     * The offset can be NULL.
+     */
+    int parent_log_context_offset;
+
+    /**
+     * Return next AVOptions-enabled child or NULL
+     */
+    void* (*child_next)(void *obj, void *prev);
+
+    /**
+     * Return an AVClass corresponding to the next potential
+     * AVOptions-enabled child.
+     *
+     * The difference between child_next and this is that
+     * child_next iterates over _already existing_ objects, while
+     * child_class_next iterates over _all possible_ children.
+     */
+    const struct AVClass* (*child_class_next)(const struct AVClass *prev);
+
+    /**
+     * Category used for visualization (like color)
+     * This is only set if the category is equal for all objects using this class.
+     * available since version (51 << 16 | 56 << 8 | 100)
+     */
+    AVClassCategory category;
+
+    /**
+     * Callback to return the category.
+     * available since version (51 << 16 | 59 << 8 | 100)
+     */
+    AVClassCategory (*get_category)(void* ctx);
+
+    /**
+     * Callback to return the supported/allowed ranges.
+     * available since version (52.12)
+     */
+    int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
+} AVClass;
+
+/**
+ * @addtogroup lavu_log
+ *
+ * @{
+ *
+ * @defgroup lavu_log_constants Logging Constants
+ *
+ * @{
+ */
+
+/**
+ * Print no output.
+ */
+#define AV_LOG_QUIET    -8
+
+/**
+ * Something went really wrong and we will crash now.
+ */
+#define AV_LOG_PANIC     0
+
+/**
+ * Something went wrong and recovery is not possible.
+ * For example, no header was found for a format which depends
+ * on headers or an illegal combination of parameters is used.
+ */
+#define AV_LOG_FATAL     8
+
+/**
+ * Something went wrong and cannot losslessly be recovered.
+ * However, not all future data is affected.
+ */
+#define AV_LOG_ERROR    16
+
+/**
+ * Something somehow does not look correct. This may or may not
+ * lead to problems. An example would be the use of '-vstrict -2'.
+ */
+#define AV_LOG_WARNING  24
+
+/**
+ * Standard information.
+ */
+#define AV_LOG_INFO     32
+
+/**
+ * Detailed information.
+ */
+#define AV_LOG_VERBOSE  40
+
+/**
+ * Stuff which is only useful for libav* developers.
+ */
+#define AV_LOG_DEBUG    48
+
+/**
+ * Extremely verbose debugging, useful for libav* development.
+ */
+#define AV_LOG_TRACE    56
+
+#define AV_LOG_MAX_OFFSET (AV_LOG_TRACE - AV_LOG_QUIET)
+
+/**
+ * @}
+ */
+
+/**
+ * Sets additional colors for extended debugging sessions.
+ * @code
+   av_log(ctx, AV_LOG_DEBUG|AV_LOG_C(134), "Message in purple\n");
+   @endcode
+ * Requires 256color terminal support. Uses outside debugging is not
+ * recommended.
+ */
+#define AV_LOG_C(x) ((x) << 8)
+
+/**
+ * Send the specified message to the log if the level is less than or equal
+ * to the current av_log_level. By default, all logging messages are sent to
+ * stderr. This behavior can be altered by setting a different logging callback
+ * function.
+ * @see av_log_set_callback
+ *
+ * @param avcl A pointer to an arbitrary struct of which the first field is a
+ *        pointer to an AVClass struct or NULL if general log.
+ * @param level The importance level of the message expressed using a @ref
+ *        lavu_log_constants "Logging Constant".
+ * @param fmt The format string (printf-compatible) that specifies how
+ *        subsequent arguments are converted to output.
+ */
+void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4);
+
+
+/**
+ * Send the specified message to the log if the level is less than or equal
+ * to the current av_log_level. By default, all logging messages are sent to
+ * stderr. This behavior can be altered by setting a different logging callback
+ * function.
+ * @see av_log_set_callback
+ *
+ * @param avcl A pointer to an arbitrary struct of which the first field is a
+ *        pointer to an AVClass struct.
+ * @param level The importance level of the message expressed using a @ref
+ *        lavu_log_constants "Logging Constant".
+ * @param fmt The format string (printf-compatible) that specifies how
+ *        subsequent arguments are converted to output.
+ * @param vl The arguments referenced by the format string.
+ */
+void av_vlog(void *avcl, int level, const char *fmt, va_list vl);
+
+/**
+ * Get the current log level
+ *
+ * @see lavu_log_constants
+ *
+ * @return Current log level
+ */
+int av_log_get_level(void);
+
+/**
+ * Set the log level
+ *
+ * @see lavu_log_constants
+ *
+ * @param level Logging level
+ */
+void av_log_set_level(int level);
+
+/**
+ * Set the logging callback
+ *
+ * @note The callback must be thread safe, even if the application does not use
+ *       threads itself as some codecs are multithreaded.
+ *
+ * @see av_log_default_callback
+ *
+ * @param callback A logging function with a compatible signature.
+ */
+void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));
+
+/**
+ * Default logging callback
+ *
+ * It prints the message to stderr, optionally colorizing it.
+ *
+ * @param avcl A pointer to an arbitrary struct of which the first field is a
+ *        pointer to an AVClass struct.
+ * @param level The importance level of the message expressed using a @ref
+ *        lavu_log_constants "Logging Constant".
+ * @param fmt The format string (printf-compatible) that specifies how
+ *        subsequent arguments are converted to output.
+ * @param vl The arguments referenced by the format string.
+ */
+void av_log_default_callback(void *avcl, int level, const char *fmt,
+                             va_list vl);
+
+/**
+ * Return the context name
+ *
+ * @param  ctx The AVClass context
+ *
+ * @return The AVClass class_name
+ */
+const char* av_default_item_name(void* ctx);
+AVClassCategory av_default_get_category(void *ptr);
+
+/**
+ * Format a line of log the same way as the default callback.
+ * @param line          buffer to receive the formatted line
+ * @param line_size     size of the buffer
+ * @param print_prefix  used to store whether the prefix must be printed;
+ *                      must point to a persistent integer initially set to 1
+ */
+void av_log_format_line(void *ptr, int level, const char *fmt, va_list vl,
+                        char *line, int line_size, int *print_prefix);
+
+/**
+ * Format a line of log the same way as the default callback.
+ * @param line          buffer to receive the formatted line;
+ *                      may be NULL if line_size is 0
+ * @param line_size     size of the buffer; at most line_size-1 characters will
+ *                      be written to the buffer, plus one null terminator
+ * @param print_prefix  used to store whether the prefix must be printed;
+ *                      must point to a persistent integer initially set to 1
+ * @return Returns a negative value if an error occurred, otherwise returns
+ *         the number of characters that would have been written for a
+ *         sufficiently large buffer, not including the terminating null
+ *         character. If the return value is not less than line_size, it means
+ *         that the log message was truncated to fit the buffer.
+ */
+int av_log_format_line2(void *ptr, int level, const char *fmt, va_list vl,
+                        char *line, int line_size, int *print_prefix);
+
+/**
+ * Skip repeated messages, this requires the user app to use av_log() instead of
+ * (f)printf as the 2 would otherwise interfere and lead to
+ * "Last message repeated x times" messages below (f)printf messages with some
+ * bad luck.
+ * Also to receive the last, "last repeated" line if any, the user app must
+ * call av_log(NULL, AV_LOG_QUIET, "%s", ""); at the end
+ */
+#define AV_LOG_SKIP_REPEATED 1
+
+/**
+ * Include the log severity in messages originating from codecs.
+ *
+ * Results in messages such as:
+ * [rawvideo @ 0xDEADBEEF] [error] encode did not produce valid pts
+ */
+#define AV_LOG_PRINT_LEVEL 2
+
+void av_log_set_flags(int arg);
+int av_log_get_flags(void);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_LOG_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lzo.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lzo.h
new file mode 100644
index 0000000..c034039
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/lzo.h
@@ -0,0 +1,66 @@
+/*
+ * LZO 1x decompression
+ * copyright (c) 2006 Reimar Doeffinger
+ *
+ * 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 AVUTIL_LZO_H
+#define AVUTIL_LZO_H
+
+/**
+ * @defgroup lavu_lzo LZO
+ * @ingroup lavu_crypto
+ *
+ * @{
+ */
+
+#include <stdint.h>
+
+/** @name Error flags returned by av_lzo1x_decode
+ * @{ */
+/// end of the input buffer reached before decoding finished
+#define AV_LZO_INPUT_DEPLETED  1
+/// decoded data did not fit into output buffer
+#define AV_LZO_OUTPUT_FULL     2
+/// a reference to previously decoded data was wrong
+#define AV_LZO_INVALID_BACKPTR 4
+/// a non-specific error in the compressed bitstream
+#define AV_LZO_ERROR           8
+/** @} */
+
+#define AV_LZO_INPUT_PADDING   8
+#define AV_LZO_OUTPUT_PADDING 12
+
+/**
+ * @brief Decodes LZO 1x compressed data.
+ * @param out output buffer
+ * @param outlen size of output buffer, number of bytes left are returned here
+ * @param in input buffer
+ * @param inlen size of input buffer, number of bytes left are returned here
+ * @return 0 on success, otherwise a combination of the error flags above
+ *
+ * Make sure all buffers are appropriately padded, in must provide
+ * AV_LZO_INPUT_PADDING, out must provide AV_LZO_OUTPUT_PADDING additional bytes.
+ */
+int av_lzo1x_decode(void *out, int *outlen, const void *in, int *inlen);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_LZO_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/macros.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/macros.h
new file mode 100644
index 0000000..2007ee5
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/macros.h
@@ -0,0 +1,50 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu
+ * Utility Preprocessor macros
+ */
+
+#ifndef AVUTIL_MACROS_H
+#define AVUTIL_MACROS_H
+
+/**
+ * @addtogroup preproc_misc Preprocessor String Macros
+ *
+ * String manipulation macros
+ *
+ * @{
+ */
+
+#define AV_STRINGIFY(s)         AV_TOSTRING(s)
+#define AV_TOSTRING(s) #s
+
+#define AV_GLUE(a, b) a ## b
+#define AV_JOIN(a, b) AV_GLUE(a, b)
+
+/**
+ * @}
+ */
+
+#define AV_PRAGMA(s) _Pragma(#s)
+
+#define FFALIGN(x, a) (((x)+(a)-1)&~((a)-1))
+
+#endif /* AVUTIL_MACROS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mastering_display_metadata.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mastering_display_metadata.h
new file mode 100644
index 0000000..c23b07c
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mastering_display_metadata.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2016 Neil Birkbeck <neil.birkbeck@gmail.com>
+ *
+ * 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 AVUTIL_MASTERING_DISPLAY_METADATA_H
+#define AVUTIL_MASTERING_DISPLAY_METADATA_H
+
+#include "frame.h"
+#include "rational.h"
+
+
+/**
+ * Mastering display metadata capable of representing the color volume of
+ * the display used to master the content (SMPTE 2086:2014).
+ *
+ * To be used as payload of a AVFrameSideData or AVPacketSideData with the
+ * appropriate type.
+ *
+ * @note The struct should be allocated with av_mastering_display_metadata_alloc()
+ *       and its size is not a part of the public ABI.
+ */
+typedef struct AVMasteringDisplayMetadata {
+    /**
+     * CIE 1931 xy chromaticity coords of color primaries (r, g, b order).
+     */
+    AVRational display_primaries[3][2];
+
+    /**
+     * CIE 1931 xy chromaticity coords of white point.
+     */
+    AVRational white_point[2];
+
+    /**
+     * Min luminance of mastering display (cd/m^2).
+     */
+    AVRational min_luminance;
+
+    /**
+     * Max luminance of mastering display (cd/m^2).
+     */
+    AVRational max_luminance;
+
+    /**
+     * Flag indicating whether the display primaries (and white point) are set.
+     */
+    int has_primaries;
+
+    /**
+     * Flag indicating whether the luminance (min_ and max_) have been set.
+     */
+    int has_luminance;
+
+} AVMasteringDisplayMetadata;
+
+/**
+ * Allocate an AVMasteringDisplayMetadata structure and set its fields to
+ * default values. The resulting struct can be freed using av_freep().
+ *
+ * @return An AVMasteringDisplayMetadata filled with default values or NULL
+ *         on failure.
+ */
+AVMasteringDisplayMetadata *av_mastering_display_metadata_alloc(void);
+
+/**
+ * Allocate a complete AVMasteringDisplayMetadata and add it to the frame.
+ *
+ * @param frame The frame which side data is added to.
+ *
+ * @return The AVMasteringDisplayMetadata structure to be filled by caller.
+ */
+AVMasteringDisplayMetadata *av_mastering_display_metadata_create_side_data(AVFrame *frame);
+
+/**
+ * Content light level needed by to transmit HDR over HDMI (CTA-861.3).
+ *
+ * To be used as payload of a AVFrameSideData or AVPacketSideData with the
+ * appropriate type.
+ *
+ * @note The struct should be allocated with av_content_light_metadata_alloc()
+ *       and its size is not a part of the public ABI.
+ */
+typedef struct AVContentLightMetadata {
+    /**
+     * Max content light level (cd/m^2).
+     */
+    unsigned MaxCLL;
+
+    /**
+     * Max average light level per frame (cd/m^2).
+     */
+    unsigned MaxFALL;
+} AVContentLightMetadata;
+
+/**
+ * Allocate an AVContentLightMetadata structure and set its fields to
+ * default values. The resulting struct can be freed using av_freep().
+ *
+ * @return An AVContentLightMetadata filled with default values or NULL
+ *         on failure.
+ */
+AVContentLightMetadata *av_content_light_metadata_alloc(size_t *size);
+
+/**
+ * Allocate a complete AVContentLightMetadata and add it to the frame.
+ *
+ * @param frame The frame which side data is added to.
+ *
+ * @return The AVContentLightMetadata structure to be filled by caller.
+ */
+AVContentLightMetadata *av_content_light_metadata_create_side_data(AVFrame *frame);
+
+#endif /* AVUTIL_MASTERING_DISPLAY_METADATA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mathematics.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mathematics.h
new file mode 100644
index 0000000..5490180
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mathematics.h
@@ -0,0 +1,242 @@
+/*
+ * copyright (c) 2005-2012 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @addtogroup lavu_math
+ * Mathematical utilities for working with timestamp and time base.
+ */
+
+#ifndef AVUTIL_MATHEMATICS_H
+#define AVUTIL_MATHEMATICS_H
+
+#include <stdint.h>
+#include <math.h>
+#include "attributes.h"
+#include "rational.h"
+#include "intfloat.h"
+
+#ifndef M_E
+#define M_E            2.7182818284590452354   /* e */
+#endif
+#ifndef M_LN2
+#define M_LN2          0.69314718055994530942  /* log_e 2 */
+#endif
+#ifndef M_LN10
+#define M_LN10         2.30258509299404568402  /* log_e 10 */
+#endif
+#ifndef M_LOG2_10
+#define M_LOG2_10      3.32192809488736234787  /* log_2 10 */
+#endif
+#ifndef M_PHI
+#define M_PHI          1.61803398874989484820   /* phi / golden ratio */
+#endif
+#ifndef M_PI
+#define M_PI           3.14159265358979323846  /* pi */
+#endif
+#ifndef M_PI_2
+#define M_PI_2         1.57079632679489661923  /* pi/2 */
+#endif
+#ifndef M_SQRT1_2
+#define M_SQRT1_2      0.70710678118654752440  /* 1/sqrt(2) */
+#endif
+#ifndef M_SQRT2
+#define M_SQRT2        1.41421356237309504880  /* sqrt(2) */
+#endif
+#ifndef NAN
+#define NAN            av_int2float(0x7fc00000)
+#endif
+#ifndef INFINITY
+#define INFINITY       av_int2float(0x7f800000)
+#endif
+
+/**
+ * @addtogroup lavu_math
+ *
+ * @{
+ */
+
+/**
+ * Rounding methods.
+ */
+enum AVRounding {
+    AV_ROUND_ZERO     = 0, ///< Round toward zero.
+    AV_ROUND_INF      = 1, ///< Round away from zero.
+    AV_ROUND_DOWN     = 2, ///< Round toward -infinity.
+    AV_ROUND_UP       = 3, ///< Round toward +infinity.
+    AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero.
+    /**
+     * Flag telling rescaling functions to pass `INT64_MIN`/`MAX` through
+     * unchanged, avoiding special cases for #AV_NOPTS_VALUE.
+     *
+     * Unlike other values of the enumeration AVRounding, this value is a
+     * bitmask that must be used in conjunction with another value of the
+     * enumeration through a bitwise OR, in order to set behavior for normal
+     * cases.
+     *
+     * @code{.c}
+     * av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
+     * // Rescaling 3:
+     * //     Calculating 3 * 1 / 2
+     * //     3 / 2 is rounded up to 2
+     * //     => 2
+     *
+     * av_rescale_rnd(AV_NOPTS_VALUE, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
+     * // Rescaling AV_NOPTS_VALUE:
+     * //     AV_NOPTS_VALUE == INT64_MIN
+     * //     AV_NOPTS_VALUE is passed through
+     * //     => AV_NOPTS_VALUE
+     * @endcode
+     */
+    AV_ROUND_PASS_MINMAX = 8192,
+};
+
+/**
+ * Compute the greatest common divisor of two integer operands.
+ *
+ * @param a,b Operands
+ * @return GCD of a and b up to sign; if a >= 0 and b >= 0, return value is >= 0;
+ * if a == 0 and b == 0, returns 0.
+ */
+int64_t av_const av_gcd(int64_t a, int64_t b);
+
+/**
+ * Rescale a 64-bit integer with rounding to nearest.
+ *
+ * The operation is mathematically equivalent to `a * b / c`, but writing that
+ * directly can overflow.
+ *
+ * This function is equivalent to av_rescale_rnd() with #AV_ROUND_NEAR_INF.
+ *
+ * @see av_rescale_rnd(), av_rescale_q(), av_rescale_q_rnd()
+ */
+int64_t av_rescale(int64_t a, int64_t b, int64_t c) av_const;
+
+/**
+ * Rescale a 64-bit integer with specified rounding.
+ *
+ * The operation is mathematically equivalent to `a * b / c`, but writing that
+ * directly can overflow, and does not support different rounding methods.
+ *
+ * @see av_rescale(), av_rescale_q(), av_rescale_q_rnd()
+ */
+int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const;
+
+/**
+ * Rescale a 64-bit integer by 2 rational numbers.
+ *
+ * The operation is mathematically equivalent to `a * bq / cq`.
+ *
+ * This function is equivalent to av_rescale_q_rnd() with #AV_ROUND_NEAR_INF.
+ *
+ * @see av_rescale(), av_rescale_rnd(), av_rescale_q_rnd()
+ */
+int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const;
+
+/**
+ * Rescale a 64-bit integer by 2 rational numbers with specified rounding.
+ *
+ * The operation is mathematically equivalent to `a * bq / cq`.
+ *
+ * @see av_rescale(), av_rescale_rnd(), av_rescale_q()
+ */
+int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,
+                         enum AVRounding rnd) av_const;
+
+/**
+ * Compare two timestamps each in its own time base.
+ *
+ * @return One of the following values:
+ *         - -1 if `ts_a` is before `ts_b`
+ *         - 1 if `ts_a` is after `ts_b`
+ *         - 0 if they represent the same position
+ *
+ * @warning
+ * The result of the function is undefined if one of the timestamps is outside
+ * the `int64_t` range when represented in the other's timebase.
+ */
+int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b);
+
+/**
+ * Compare the remainders of two integer operands divided by a common divisor.
+ *
+ * In other words, compare the least significant `log2(mod)` bits of integers
+ * `a` and `b`.
+ *
+ * @code{.c}
+ * av_compare_mod(0x11, 0x02, 0x10) < 0 // since 0x11 % 0x10  (0x1) < 0x02 % 0x10  (0x2)
+ * av_compare_mod(0x11, 0x02, 0x20) > 0 // since 0x11 % 0x20 (0x11) > 0x02 % 0x20 (0x02)
+ * @endcode
+ *
+ * @param a,b Operands
+ * @param mod Divisor; must be a power of 2
+ * @return
+ *         - a negative value if `a % mod < b % mod`
+ *         - a positive value if `a % mod > b % mod`
+ *         - zero             if `a % mod == b % mod`
+ */
+int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod);
+
+/**
+ * Rescale a timestamp while preserving known durations.
+ *
+ * This function is designed to be called per audio packet to scale the input
+ * timestamp to a different time base. Compared to a simple av_rescale_q()
+ * call, this function is robust against possible inconsistent frame durations.
+ *
+ * The `last` parameter is a state variable that must be preserved for all
+ * subsequent calls for the same stream. For the first call, `*last` should be
+ * initialized to #AV_NOPTS_VALUE.
+ *
+ * @param[in]     in_tb    Input time base
+ * @param[in]     in_ts    Input timestamp
+ * @param[in]     fs_tb    Duration time base; typically this is finer-grained
+ *                         (greater) than `in_tb` and `out_tb`
+ * @param[in]     duration Duration till the next call to this function (i.e.
+ *                         duration of the current packet/frame)
+ * @param[in,out] last     Pointer to a timestamp expressed in terms of
+ *                         `fs_tb`, acting as a state variable
+ * @param[in]     out_tb   Output timebase
+ * @return        Timestamp expressed in terms of `out_tb`
+ *
+ * @note In the context of this function, "duration" is in term of samples, not
+ *       seconds.
+ */
+int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts,  AVRational fs_tb, int duration, int64_t *last, AVRational out_tb);
+
+/**
+ * Add a value to a timestamp.
+ *
+ * This function guarantees that when the same value is repeatly added that
+ * no accumulation of rounding errors occurs.
+ *
+ * @param[in] ts     Input timestamp
+ * @param[in] ts_tb  Input timestamp time base
+ * @param[in] inc    Value to be added
+ * @param[in] inc_tb Time base of `inc`
+ */
+int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t inc);
+
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_MATHEMATICS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/md5.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/md5.h
new file mode 100644
index 0000000..ca72ccb
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/md5.h
@@ -0,0 +1,98 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_md5
+ * Public header for MD5 hash function implementation.
+ */
+
+#ifndef AVUTIL_MD5_H
+#define AVUTIL_MD5_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "attributes.h"
+#include "version.h"
+
+/**
+ * @defgroup lavu_md5 MD5
+ * @ingroup lavu_hash
+ * MD5 hash function implementation.
+ *
+ * @{
+ */
+
+extern const int av_md5_size;
+
+struct AVMD5;
+
+/**
+ * Allocate an AVMD5 context.
+ */
+struct AVMD5 *av_md5_alloc(void);
+
+/**
+ * Initialize MD5 hashing.
+ *
+ * @param ctx pointer to the function context (of size av_md5_size)
+ */
+void av_md5_init(struct AVMD5 *ctx);
+
+/**
+ * Update hash value.
+ *
+ * @param ctx hash function context
+ * @param src input data to update hash with
+ * @param len input data length
+ */
+#if FF_API_CRYPTO_SIZE_T
+void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, int len);
+#else
+void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, size_t len);
+#endif
+
+/**
+ * Finish hashing and output digest value.
+ *
+ * @param ctx hash function context
+ * @param dst buffer where output digest value is stored
+ */
+void av_md5_final(struct AVMD5 *ctx, uint8_t *dst);
+
+/**
+ * Hash an array of data.
+ *
+ * @param dst The output buffer to write the digest into
+ * @param src The data to hash
+ * @param len The length of the data, in bytes
+ */
+#if FF_API_CRYPTO_SIZE_T
+void av_md5_sum(uint8_t *dst, const uint8_t *src, const int len);
+#else
+void av_md5_sum(uint8_t *dst, const uint8_t *src, size_t len);
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_MD5_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mem.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mem.h
new file mode 100644
index 0000000..7e0b12a
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/mem.h
@@ -0,0 +1,700 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_mem
+ * Memory handling functions
+ */
+
+#ifndef AVUTIL_MEM_H
+#define AVUTIL_MEM_H
+
+#include <limits.h>
+#include <stdint.h>
+
+#include "attributes.h"
+#include "error.h"
+#include "avutil.h"
+
+/**
+ * @addtogroup lavu_mem
+ * Utilities for manipulating memory.
+ *
+ * FFmpeg has several applications of memory that are not required of a typical
+ * program. For example, the computing-heavy components like video decoding and
+ * encoding can be sped up significantly through the use of aligned memory.
+ *
+ * However, for each of FFmpeg's applications of memory, there might not be a
+ * recognized or standardized API for that specific use. Memory alignment, for
+ * instance, varies wildly depending on operating systems, architectures, and
+ * compilers. Hence, this component of @ref libavutil is created to make
+ * dealing with memory consistently possible on all platforms.
+ *
+ * @{
+ *
+ * @defgroup lavu_mem_macros Alignment Macros
+ * Helper macros for declaring aligned variables.
+ * @{
+ */
+
+/**
+ * @def DECLARE_ALIGNED(n,t,v)
+ * Declare a variable that is aligned in memory.
+ *
+ * @code{.c}
+ * DECLARE_ALIGNED(16, uint16_t, aligned_int) = 42;
+ * DECLARE_ALIGNED(32, uint8_t, aligned_array)[128];
+ *
+ * // The default-alignment equivalent would be
+ * uint16_t aligned_int = 42;
+ * uint8_t aligned_array[128];
+ * @endcode
+ *
+ * @param n Minimum alignment in bytes
+ * @param t Type of the variable (or array element)
+ * @param v Name of the variable
+ */
+
+/**
+ * @def DECLARE_ASM_ALIGNED(n,t,v)
+ * Declare an aligned variable appropriate for use in inline assembly code.
+ *
+ * @code{.c}
+ * DECLARE_ASM_ALIGNED(16, uint64_t, pw_08) = UINT64_C(0x0008000800080008);
+ * @endcode
+ *
+ * @param n Minimum alignment in bytes
+ * @param t Type of the variable (or array element)
+ * @param v Name of the variable
+ */
+
+/**
+ * @def DECLARE_ASM_CONST(n,t,v)
+ * Declare a static constant aligned variable appropriate for use in inline
+ * assembly code.
+ *
+ * @code{.c}
+ * DECLARE_ASM_CONST(16, uint64_t, pw_08) = UINT64_C(0x0008000800080008);
+ * @endcode
+ *
+ * @param n Minimum alignment in bytes
+ * @param t Type of the variable (or array element)
+ * @param v Name of the variable
+ */
+
+#if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1110 || defined(__SUNPRO_C)
+    #define DECLARE_ALIGNED(n,t,v)      t __attribute__ ((aligned (n))) v
+    #define DECLARE_ASM_ALIGNED(n,t,v)  t __attribute__ ((aligned (n))) v
+    #define DECLARE_ASM_CONST(n,t,v)    const t __attribute__ ((aligned (n))) v
+#elif defined(__DJGPP__)
+    #define DECLARE_ALIGNED(n,t,v)      t __attribute__ ((aligned (FFMIN(n, 16)))) v
+    #define DECLARE_ASM_ALIGNED(n,t,v)  t av_used __attribute__ ((aligned (FFMIN(n, 16)))) v
+    #define DECLARE_ASM_CONST(n,t,v)    static const t av_used __attribute__ ((aligned (FFMIN(n, 16)))) v
+#elif defined(__GNUC__) || defined(__clang__)
+    #define DECLARE_ALIGNED(n,t,v)      t __attribute__ ((aligned (n))) v
+    #define DECLARE_ASM_ALIGNED(n,t,v)  t av_used __attribute__ ((aligned (n))) v
+    #define DECLARE_ASM_CONST(n,t,v)    static const t av_used __attribute__ ((aligned (n))) v
+#elif defined(_MSC_VER)
+    #define DECLARE_ALIGNED(n,t,v)      __declspec(align(n)) t v
+    #define DECLARE_ASM_ALIGNED(n,t,v)  __declspec(align(n)) t v
+    #define DECLARE_ASM_CONST(n,t,v)    __declspec(align(n)) static const t v
+#else
+    #define DECLARE_ALIGNED(n,t,v)      t v
+    #define DECLARE_ASM_ALIGNED(n,t,v)  t v
+    #define DECLARE_ASM_CONST(n,t,v)    static const t v
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lavu_mem_attrs Function Attributes
+ * Function attributes applicable to memory handling functions.
+ *
+ * These function attributes can help compilers emit more useful warnings, or
+ * generate better code.
+ * @{
+ */
+
+/**
+ * @def av_malloc_attrib
+ * Function attribute denoting a malloc-like function.
+ *
+ * @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bmalloc_007d-function-attribute-3251">Function attribute `malloc` in GCC's documentation</a>
+ */
+
+#if AV_GCC_VERSION_AT_LEAST(3,1)
+    #define av_malloc_attrib __attribute__((__malloc__))
+#else
+    #define av_malloc_attrib
+#endif
+
+/**
+ * @def av_alloc_size(...)
+ * Function attribute used on a function that allocates memory, whose size is
+ * given by the specified parameter(s).
+ *
+ * @code{.c}
+ * void *av_malloc(size_t size) av_alloc_size(1);
+ * void *av_calloc(size_t nmemb, size_t size) av_alloc_size(1, 2);
+ * @endcode
+ *
+ * @param ... One or two parameter indexes, separated by a comma
+ *
+ * @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007balloc_005fsize_007d-function-attribute-3220">Function attribute `alloc_size` in GCC's documentation</a>
+ */
+
+#if AV_GCC_VERSION_AT_LEAST(4,3)
+    #define av_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__)))
+#else
+    #define av_alloc_size(...)
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lavu_mem_funcs Heap Management
+ * Functions responsible for allocating, freeing, and copying memory.
+ *
+ * All memory allocation functions have a built-in upper limit of `INT_MAX`
+ * bytes. This may be changed with av_max_alloc(), although exercise extreme
+ * caution when doing so.
+ *
+ * @{
+ */
+
+/**
+ * Allocate a memory block with alignment suitable for all memory accesses
+ * (including vectors if available on the CPU).
+ *
+ * @param size Size in bytes for the memory block to be allocated
+ * @return Pointer to the allocated block, or `NULL` if the block cannot
+ *         be allocated
+ * @see av_mallocz()
+ */
+void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
+
+/**
+ * Allocate a memory block with alignment suitable for all memory accesses
+ * (including vectors if available on the CPU) and zero all the bytes of the
+ * block.
+ *
+ * @param size Size in bytes for the memory block to be allocated
+ * @return Pointer to the allocated block, or `NULL` if it cannot be allocated
+ * @see av_malloc()
+ */
+void *av_mallocz(size_t size) av_malloc_attrib av_alloc_size(1);
+
+/**
+ * Allocate a memory block for an array with av_malloc().
+ *
+ * The allocated memory will have size `size * nmemb` bytes.
+ *
+ * @param nmemb Number of element
+ * @param size  Size of a single element
+ * @return Pointer to the allocated block, or `NULL` if the block cannot
+ *         be allocated
+ * @see av_malloc()
+ */
+av_alloc_size(1, 2) void *av_malloc_array(size_t nmemb, size_t size);
+
+/**
+ * Allocate a memory block for an array with av_mallocz().
+ *
+ * The allocated memory will have size `size * nmemb` bytes.
+ *
+ * @param nmemb Number of elements
+ * @param size  Size of the single element
+ * @return Pointer to the allocated block, or `NULL` if the block cannot
+ *         be allocated
+ *
+ * @see av_mallocz()
+ * @see av_malloc_array()
+ */
+av_alloc_size(1, 2) void *av_mallocz_array(size_t nmemb, size_t size);
+
+/**
+ * Non-inlined equivalent of av_mallocz_array().
+ *
+ * Created for symmetry with the calloc() C function.
+ */
+void *av_calloc(size_t nmemb, size_t size) av_malloc_attrib;
+
+/**
+ * Allocate, reallocate, or free a block of memory.
+ *
+ * If `ptr` is `NULL` and `size` > 0, allocate a new block. If `size` is
+ * zero, free the memory block pointed to by `ptr`. Otherwise, expand or
+ * shrink that block of memory according to `size`.
+ *
+ * @param ptr  Pointer to a memory block already allocated with
+ *             av_realloc() or `NULL`
+ * @param size Size in bytes of the memory block to be allocated or
+ *             reallocated
+ *
+ * @return Pointer to a newly-reallocated block or `NULL` if the block
+ *         cannot be reallocated or the function is used to free the memory block
+ *
+ * @warning Unlike av_malloc(), the returned pointer is not guaranteed to be
+ *          correctly aligned.
+ * @see av_fast_realloc()
+ * @see av_reallocp()
+ */
+void *av_realloc(void *ptr, size_t size) av_alloc_size(2);
+
+/**
+ * Allocate, reallocate, or free a block of memory through a pointer to a
+ * pointer.
+ *
+ * If `*ptr` is `NULL` and `size` > 0, allocate a new block. If `size` is
+ * zero, free the memory block pointed to by `*ptr`. Otherwise, expand or
+ * shrink that block of memory according to `size`.
+ *
+ * @param[in,out] ptr  Pointer to a pointer to a memory block already allocated
+ *                     with av_realloc(), or a pointer to `NULL`. The pointer
+ *                     is updated on success, or freed on failure.
+ * @param[in]     size Size in bytes for the memory block to be allocated or
+ *                     reallocated
+ *
+ * @return Zero on success, an AVERROR error code on failure
+ *
+ * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be
+ *          correctly aligned.
+ */
+av_warn_unused_result
+int av_reallocp(void *ptr, size_t size);
+
+/**
+ * Allocate, reallocate, or free a block of memory.
+ *
+ * This function does the same thing as av_realloc(), except:
+ * - It takes two size arguments and allocates `nelem * elsize` bytes,
+ *   after checking the result of the multiplication for integer overflow.
+ * - It frees the input block in case of failure, thus avoiding the memory
+ *   leak with the classic
+ *   @code{.c}
+ *   buf = realloc(buf);
+ *   if (!buf)
+ *       return -1;
+ *   @endcode
+ *   pattern.
+ */
+void *av_realloc_f(void *ptr, size_t nelem, size_t elsize);
+
+/**
+ * Allocate, reallocate, or free an array.
+ *
+ * If `ptr` is `NULL` and `nmemb` > 0, allocate a new block. If
+ * `nmemb` is zero, free the memory block pointed to by `ptr`.
+ *
+ * @param ptr   Pointer to a memory block already allocated with
+ *              av_realloc() or `NULL`
+ * @param nmemb Number of elements in the array
+ * @param size  Size of the single element of the array
+ *
+ * @return Pointer to a newly-reallocated block or NULL if the block
+ *         cannot be reallocated or the function is used to free the memory block
+ *
+ * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be
+ *          correctly aligned.
+ * @see av_reallocp_array()
+ */
+av_alloc_size(2, 3) void *av_realloc_array(void *ptr, size_t nmemb, size_t size);
+
+/**
+ * Allocate, reallocate, or free an array through a pointer to a pointer.
+ *
+ * If `*ptr` is `NULL` and `nmemb` > 0, allocate a new block. If `nmemb` is
+ * zero, free the memory block pointed to by `*ptr`.
+ *
+ * @param[in,out] ptr   Pointer to a pointer to a memory block already
+ *                      allocated with av_realloc(), or a pointer to `NULL`.
+ *                      The pointer is updated on success, or freed on failure.
+ * @param[in]     nmemb Number of elements
+ * @param[in]     size  Size of the single element
+ *
+ * @return Zero on success, an AVERROR error code on failure
+ *
+ * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be
+ *          correctly aligned.
+ */
+av_alloc_size(2, 3) int av_reallocp_array(void *ptr, size_t nmemb, size_t size);
+
+/**
+ * Reallocate the given buffer if it is not large enough, otherwise do nothing.
+ *
+ * If the given buffer is `NULL`, then a new uninitialized buffer is allocated.
+ *
+ * If the given buffer is not large enough, and reallocation fails, `NULL` is
+ * returned and `*size` is set to 0, but the original buffer is not changed or
+ * freed.
+ *
+ * A typical use pattern follows:
+ *
+ * @code{.c}
+ * uint8_t *buf = ...;
+ * uint8_t *new_buf = av_fast_realloc(buf, &current_size, size_needed);
+ * if (!new_buf) {
+ *     // Allocation failed; clean up original buffer
+ *     av_freep(&buf);
+ *     return AVERROR(ENOMEM);
+ * }
+ * @endcode
+ *
+ * @param[in,out] ptr      Already allocated buffer, or `NULL`
+ * @param[in,out] size     Pointer to current size of buffer `ptr`. `*size` is
+ *                         changed to `min_size` in case of success or 0 in
+ *                         case of failure
+ * @param[in]     min_size New size of buffer `ptr`
+ * @return `ptr` if the buffer is large enough, a pointer to newly reallocated
+ *         buffer if the buffer was not large enough, or `NULL` in case of
+ *         error
+ * @see av_realloc()
+ * @see av_fast_malloc()
+ */
+void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size);
+
+/**
+ * Allocate a buffer, reusing the given one if large enough.
+ *
+ * Contrary to av_fast_realloc(), the current buffer contents might not be
+ * preserved and on error the old buffer is freed, thus no special handling to
+ * avoid memleaks is necessary.
+ *
+ * `*ptr` is allowed to be `NULL`, in which case allocation always happens if
+ * `size_needed` is greater than 0.
+ *
+ * @code{.c}
+ * uint8_t *buf = ...;
+ * av_fast_malloc(&buf, &current_size, size_needed);
+ * if (!buf) {
+ *     // Allocation failed; buf already freed
+ *     return AVERROR(ENOMEM);
+ * }
+ * @endcode
+ *
+ * @param[in,out] ptr      Pointer to pointer to an already allocated buffer.
+ *                         `*ptr` will be overwritten with pointer to new
+ *                         buffer on success or `NULL` on failure
+ * @param[in,out] size     Pointer to current size of buffer `*ptr`. `*size` is
+ *                         changed to `min_size` in case of success or 0 in
+ *                         case of failure
+ * @param[in]     min_size New size of buffer `*ptr`
+ * @see av_realloc()
+ * @see av_fast_mallocz()
+ */
+void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size);
+
+/**
+ * Allocate and clear a buffer, reusing the given one if large enough.
+ *
+ * Like av_fast_malloc(), but all newly allocated space is initially cleared.
+ * Reused buffer is not cleared.
+ *
+ * `*ptr` is allowed to be `NULL`, in which case allocation always happens if
+ * `size_needed` is greater than 0.
+ *
+ * @param[in,out] ptr      Pointer to pointer to an already allocated buffer.
+ *                         `*ptr` will be overwritten with pointer to new
+ *                         buffer on success or `NULL` on failure
+ * @param[in,out] size     Pointer to current size of buffer `*ptr`. `*size` is
+ *                         changed to `min_size` in case of success or 0 in
+ *                         case of failure
+ * @param[in]     min_size New size of buffer `*ptr`
+ * @see av_fast_malloc()
+ */
+void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size);
+
+/**
+ * Free a memory block which has been allocated with a function of av_malloc()
+ * or av_realloc() family.
+ *
+ * @param ptr Pointer to the memory block which should be freed.
+ *
+ * @note `ptr = NULL` is explicitly allowed.
+ * @note It is recommended that you use av_freep() instead, to prevent leaving
+ *       behind dangling pointers.
+ * @see av_freep()
+ */
+void av_free(void *ptr);
+
+/**
+ * Free a memory block which has been allocated with a function of av_malloc()
+ * or av_realloc() family, and set the pointer pointing to it to `NULL`.
+ *
+ * @code{.c}
+ * uint8_t *buf = av_malloc(16);
+ * av_free(buf);
+ * // buf now contains a dangling pointer to freed memory, and accidental
+ * // dereference of buf will result in a use-after-free, which may be a
+ * // security risk.
+ *
+ * uint8_t *buf = av_malloc(16);
+ * av_freep(&buf);
+ * // buf is now NULL, and accidental dereference will only result in a
+ * // NULL-pointer dereference.
+ * @endcode
+ *
+ * @param ptr Pointer to the pointer to the memory block which should be freed
+ * @note `*ptr = NULL` is safe and leads to no action.
+ * @see av_free()
+ */
+void av_freep(void *ptr);
+
+/**
+ * Duplicate a string.
+ *
+ * @param s String to be duplicated
+ * @return Pointer to a newly-allocated string containing a
+ *         copy of `s` or `NULL` if the string cannot be allocated
+ * @see av_strndup()
+ */
+char *av_strdup(const char *s) av_malloc_attrib;
+
+/**
+ * Duplicate a substring of a string.
+ *
+ * @param s   String to be duplicated
+ * @param len Maximum length of the resulting string (not counting the
+ *            terminating byte)
+ * @return Pointer to a newly-allocated string containing a
+ *         substring of `s` or `NULL` if the string cannot be allocated
+ */
+char *av_strndup(const char *s, size_t len) av_malloc_attrib;
+
+/**
+ * Duplicate a buffer with av_malloc().
+ *
+ * @param p    Buffer to be duplicated
+ * @param size Size in bytes of the buffer copied
+ * @return Pointer to a newly allocated buffer containing a
+ *         copy of `p` or `NULL` if the buffer cannot be allocated
+ */
+void *av_memdup(const void *p, size_t size);
+
+/**
+ * Overlapping memcpy() implementation.
+ *
+ * @param dst  Destination buffer
+ * @param back Number of bytes back to start copying (i.e. the initial size of
+ *             the overlapping window); must be > 0
+ * @param cnt  Number of bytes to copy; must be >= 0
+ *
+ * @note `cnt > back` is valid, this will copy the bytes we just copied,
+ *       thus creating a repeating pattern with a period length of `back`.
+ */
+void av_memcpy_backptr(uint8_t *dst, int back, int cnt);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lavu_mem_dynarray Dynamic Array
+ *
+ * Utilities to make an array grow when needed.
+ *
+ * Sometimes, the programmer would want to have an array that can grow when
+ * needed. The libavutil dynamic array utilities fill that need.
+ *
+ * libavutil supports two systems of appending elements onto a dynamically
+ * allocated array, the first one storing the pointer to the value in the
+ * array, and the second storing the value directly. In both systems, the
+ * caller is responsible for maintaining a variable containing the length of
+ * the array, as well as freeing of the array after use.
+ *
+ * The first system stores pointers to values in a block of dynamically
+ * allocated memory. Since only pointers are stored, the function does not need
+ * to know the size of the type. Both av_dynarray_add() and
+ * av_dynarray_add_nofree() implement this system.
+ *
+ * @code
+ * type **array = NULL; //< an array of pointers to values
+ * int    nb    = 0;    //< a variable to keep track of the length of the array
+ *
+ * type to_be_added  = ...;
+ * type to_be_added2 = ...;
+ *
+ * av_dynarray_add(&array, &nb, &to_be_added);
+ * if (nb == 0)
+ *     return AVERROR(ENOMEM);
+ *
+ * av_dynarray_add(&array, &nb, &to_be_added2);
+ * if (nb == 0)
+ *     return AVERROR(ENOMEM);
+ *
+ * // Now:
+ * //  nb           == 2
+ * // &to_be_added  == array[0]
+ * // &to_be_added2 == array[1]
+ *
+ * av_freep(&array);
+ * @endcode
+ *
+ * The second system stores the value directly in a block of memory. As a
+ * result, the function has to know the size of the type. av_dynarray2_add()
+ * implements this mechanism.
+ *
+ * @code
+ * type *array = NULL; //< an array of values
+ * int   nb    = 0;    //< a variable to keep track of the length of the array
+ *
+ * type to_be_added  = ...;
+ * type to_be_added2 = ...;
+ *
+ * type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array), NULL);
+ * if (!addr)
+ *     return AVERROR(ENOMEM);
+ * memcpy(addr, &to_be_added, sizeof(to_be_added));
+ *
+ * // Shortcut of the above.
+ * type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array),
+ *                               (const void *)&to_be_added2);
+ * if (!addr)
+ *     return AVERROR(ENOMEM);
+ *
+ * // Now:
+ * //  nb           == 2
+ * //  to_be_added  == array[0]
+ * //  to_be_added2 == array[1]
+ *
+ * av_freep(&array);
+ * @endcode
+ *
+ * @{
+ */
+
+/**
+ * Add the pointer to an element to a dynamic array.
+ *
+ * The array to grow is supposed to be an array of pointers to
+ * structures, and the element to add must be a pointer to an already
+ * allocated structure.
+ *
+ * The array is reallocated when its size reaches powers of 2.
+ * Therefore, the amortized cost of adding an element is constant.
+ *
+ * In case of success, the pointer to the array is updated in order to
+ * point to the new grown array, and the number pointed to by `nb_ptr`
+ * is incremented.
+ * In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and
+ * `*nb_ptr` is set to 0.
+ *
+ * @param[in,out] tab_ptr Pointer to the array to grow
+ * @param[in,out] nb_ptr  Pointer to the number of elements in the array
+ * @param[in]     elem    Element to add
+ * @see av_dynarray_add_nofree(), av_dynarray2_add()
+ */
+void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem);
+
+/**
+ * Add an element to a dynamic array.
+ *
+ * Function has the same functionality as av_dynarray_add(),
+ * but it doesn't free memory on fails. It returns error code
+ * instead and leave current buffer untouched.
+ *
+ * @return >=0 on success, negative otherwise
+ * @see av_dynarray_add(), av_dynarray2_add()
+ */
+av_warn_unused_result
+int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem);
+
+/**
+ * Add an element of size `elem_size` to a dynamic array.
+ *
+ * The array is reallocated when its number of elements reaches powers of 2.
+ * Therefore, the amortized cost of adding an element is constant.
+ *
+ * In case of success, the pointer to the array is updated in order to
+ * point to the new grown array, and the number pointed to by `nb_ptr`
+ * is incremented.
+ * In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and
+ * `*nb_ptr` is set to 0.
+ *
+ * @param[in,out] tab_ptr   Pointer to the array to grow
+ * @param[in,out] nb_ptr    Pointer to the number of elements in the array
+ * @param[in]     elem_size Size in bytes of an element in the array
+ * @param[in]     elem_data Pointer to the data of the element to add. If
+ *                          `NULL`, the space of the newly added element is
+ *                          allocated but left uninitialized.
+ *
+ * @return Pointer to the data of the element to copy in the newly allocated
+ *         space
+ * @see av_dynarray_add(), av_dynarray_add_nofree()
+ */
+void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size,
+                       const uint8_t *elem_data);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lavu_mem_misc Miscellaneous Functions
+ *
+ * Other functions related to memory allocation.
+ *
+ * @{
+ */
+
+/**
+ * Multiply two `size_t` values checking for overflow.
+ *
+ * @param[in]  a,b Operands of multiplication
+ * @param[out] r   Pointer to the result of the operation
+ * @return 0 on success, AVERROR(EINVAL) on overflow
+ */
+static inline int av_size_mult(size_t a, size_t b, size_t *r)
+{
+    size_t t = a * b;
+    /* Hack inspired from glibc: don't try the division if nelem and elsize
+     * are both less than sqrt(SIZE_MAX). */
+    if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b)
+        return AVERROR(EINVAL);
+    *r = t;
+    return 0;
+}
+
+/**
+ * Set the maximum size that may be allocated in one block.
+ *
+ * The value specified with this function is effective for all libavutil's @ref
+ * lavu_mem_funcs "heap management functions."
+ *
+ * By default, the max value is defined as `INT_MAX`.
+ *
+ * @param max Value to be set as the new maximum size
+ *
+ * @warning Exercise extreme caution when using this function. Don't touch
+ *          this if you do not understand the full consequence of doing so.
+ */
+void av_max_alloc(size_t max);
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_MEM_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/motion_vector.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/motion_vector.h
new file mode 100644
index 0000000..ec29556
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/motion_vector.h
@@ -0,0 +1,57 @@
+/*
+ * 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 AVUTIL_MOTION_VECTOR_H
+#define AVUTIL_MOTION_VECTOR_H
+
+#include <stdint.h>
+
+typedef struct AVMotionVector {
+    /**
+     * Where the current macroblock comes from; negative value when it comes
+     * from the past, positive value when it comes from the future.
+     * XXX: set exact relative ref frame reference instead of a +/- 1 "direction".
+     */
+    int32_t source;
+    /**
+     * Width and height of the block.
+     */
+    uint8_t w, h;
+    /**
+     * Absolute source position. Can be outside the frame area.
+     */
+    int16_t src_x, src_y;
+    /**
+     * Absolute destination position. Can be outside the frame area.
+     */
+    int16_t dst_x, dst_y;
+    /**
+     * Extra flag information.
+     * Currently unused.
+     */
+    uint64_t flags;
+    /**
+     * Motion vector
+     * src_x = dst_x + motion_x / motion_scale
+     * src_y = dst_y + motion_y / motion_scale
+     */
+    int32_t motion_x, motion_y;
+    uint16_t motion_scale;
+} AVMotionVector;
+
+#endif /* AVUTIL_MOTION_VECTOR_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/murmur3.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/murmur3.h
new file mode 100644
index 0000000..1b09175
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/murmur3.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_murmur3
+ * Public header for MurmurHash3 hash function implementation.
+ */
+
+#ifndef AVUTIL_MURMUR3_H
+#define AVUTIL_MURMUR3_H
+
+#include <stdint.h>
+
+#include "version.h"
+
+/**
+ * @defgroup lavu_murmur3 Murmur3
+ * @ingroup lavu_hash
+ * MurmurHash3 hash function implementation.
+ *
+ * MurmurHash3 is a non-cryptographic hash function, of which three
+ * incompatible versions were created by its inventor Austin Appleby:
+ *
+ * - 32-bit output
+ * - 128-bit output for 32-bit platforms
+ * - 128-bit output for 64-bit platforms
+ *
+ * FFmpeg only implements the last variant: 128-bit output designed for 64-bit
+ * platforms. Even though the hash function was designed for 64-bit platforms,
+ * the function in reality works on 32-bit systems too, only with reduced
+ * performance.
+ *
+ * @anchor lavu_murmur3_seedinfo
+ * By design, MurmurHash3 requires a seed to operate. In response to this,
+ * libavutil provides two functions for hash initiation, one that requires a
+ * seed (av_murmur3_init_seeded()) and one that uses a fixed arbitrary integer
+ * as the seed, and therefore does not (av_murmur3_init()).
+ *
+ * To make hashes comparable, you should provide the same seed for all calls to
+ * this hash function -- if you are supplying one yourself, that is.
+ *
+ * @{
+ */
+
+/**
+ * Allocate an AVMurMur3 hash context.
+ *
+ * @return Uninitialized hash context or `NULL` in case of error
+ */
+struct AVMurMur3 *av_murmur3_alloc(void);
+
+/**
+ * Initialize or reinitialize an AVMurMur3 hash context with a seed.
+ *
+ * @param[out] c    Hash context
+ * @param[in]  seed Random seed
+ *
+ * @see av_murmur3_init()
+ * @see @ref lavu_murmur3_seedinfo "Detailed description" on a discussion of
+ * seeds for MurmurHash3.
+ */
+void av_murmur3_init_seeded(struct AVMurMur3 *c, uint64_t seed);
+
+/**
+ * Initialize or reinitialize an AVMurMur3 hash context.
+ *
+ * Equivalent to av_murmur3_init_seeded() with a built-in seed.
+ *
+ * @param[out] c    Hash context
+ *
+ * @see av_murmur3_init_seeded()
+ * @see @ref lavu_murmur3_seedinfo "Detailed description" on a discussion of
+ * seeds for MurmurHash3.
+ */
+void av_murmur3_init(struct AVMurMur3 *c);
+
+/**
+ * Update hash context with new data.
+ *
+ * @param[out] c    Hash context
+ * @param[in]  src  Input data to update hash with
+ * @param[in]  len  Number of bytes to read from `src`
+ */
+#if FF_API_CRYPTO_SIZE_T
+void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, int len);
+#else
+void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, size_t len);
+#endif
+
+/**
+ * Finish hashing and output digest value.
+ *
+ * @param[in,out] c    Hash context
+ * @param[out]    dst  Buffer where output digest value is stored
+ */
+void av_murmur3_final(struct AVMurMur3 *c, uint8_t dst[16]);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_MURMUR3_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/opt.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/opt.h
new file mode 100644
index 0000000..39f4a8d
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/opt.h
@@ -0,0 +1,865 @@
+/*
+ * AVOptions
+ * copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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 AVUTIL_OPT_H
+#define AVUTIL_OPT_H
+
+/**
+ * @file
+ * AVOptions
+ */
+
+#include "rational.h"
+#include "avutil.h"
+#include "dict.h"
+#include "log.h"
+#include "pixfmt.h"
+#include "samplefmt.h"
+#include "version.h"
+
+/**
+ * @defgroup avoptions AVOptions
+ * @ingroup lavu_data
+ * @{
+ * AVOptions provide a generic system to declare options on arbitrary structs
+ * ("objects"). An option can have a help text, a type and a range of possible
+ * values. Options may then be enumerated, read and written to.
+ *
+ * @section avoptions_implement Implementing AVOptions
+ * This section describes how to add AVOptions capabilities to a struct.
+ *
+ * All AVOptions-related information is stored in an AVClass. Therefore
+ * the first member of the struct should be a pointer to an AVClass describing it.
+ * The option field of the AVClass must be set to a NULL-terminated static array
+ * of AVOptions. Each AVOption must have a non-empty name, a type, a default
+ * value and for number-type AVOptions also a range of allowed values. It must
+ * also declare an offset in bytes from the start of the struct, where the field
+ * associated with this AVOption is located. Other fields in the AVOption struct
+ * should also be set when applicable, but are not required.
+ *
+ * The following example illustrates an AVOptions-enabled struct:
+ * @code
+ * typedef struct test_struct {
+ *     const AVClass *class;
+ *     int      int_opt;
+ *     char    *str_opt;
+ *     uint8_t *bin_opt;
+ *     int      bin_len;
+ * } test_struct;
+ *
+ * static const AVOption test_options[] = {
+ *   { "test_int", "This is a test option of int type.", offsetof(test_struct, int_opt),
+ *     AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX },
+ *   { "test_str", "This is a test option of string type.", offsetof(test_struct, str_opt),
+ *     AV_OPT_TYPE_STRING },
+ *   { "test_bin", "This is a test option of binary type.", offsetof(test_struct, bin_opt),
+ *     AV_OPT_TYPE_BINARY },
+ *   { NULL },
+ * };
+ *
+ * static const AVClass test_class = {
+ *     .class_name = "test class",
+ *     .item_name  = av_default_item_name,
+ *     .option     = test_options,
+ *     .version    = LIBAVUTIL_VERSION_INT,
+ * };
+ * @endcode
+ *
+ * Next, when allocating your struct, you must ensure that the AVClass pointer
+ * is set to the correct value. Then, av_opt_set_defaults() can be called to
+ * initialize defaults. After that the struct is ready to be used with the
+ * AVOptions API.
+ *
+ * When cleaning up, you may use the av_opt_free() function to automatically
+ * free all the allocated string and binary options.
+ *
+ * Continuing with the above example:
+ *
+ * @code
+ * test_struct *alloc_test_struct(void)
+ * {
+ *     test_struct *ret = av_mallocz(sizeof(*ret));
+ *     ret->class = &test_class;
+ *     av_opt_set_defaults(ret);
+ *     return ret;
+ * }
+ * void free_test_struct(test_struct **foo)
+ * {
+ *     av_opt_free(*foo);
+ *     av_freep(foo);
+ * }
+ * @endcode
+ *
+ * @subsection avoptions_implement_nesting Nesting
+ *      It may happen that an AVOptions-enabled struct contains another
+ *      AVOptions-enabled struct as a member (e.g. AVCodecContext in
+ *      libavcodec exports generic options, while its priv_data field exports
+ *      codec-specific options). In such a case, it is possible to set up the
+ *      parent struct to export a child's options. To do that, simply
+ *      implement AVClass.child_next() and AVClass.child_class_next() in the
+ *      parent struct's AVClass.
+ *      Assuming that the test_struct from above now also contains a
+ *      child_struct field:
+ *
+ *      @code
+ *      typedef struct child_struct {
+ *          AVClass *class;
+ *          int flags_opt;
+ *      } child_struct;
+ *      static const AVOption child_opts[] = {
+ *          { "test_flags", "This is a test option of flags type.",
+ *            offsetof(child_struct, flags_opt), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX },
+ *          { NULL },
+ *      };
+ *      static const AVClass child_class = {
+ *          .class_name = "child class",
+ *          .item_name  = av_default_item_name,
+ *          .option     = child_opts,
+ *          .version    = LIBAVUTIL_VERSION_INT,
+ *      };
+ *
+ *      void *child_next(void *obj, void *prev)
+ *      {
+ *          test_struct *t = obj;
+ *          if (!prev && t->child_struct)
+ *              return t->child_struct;
+ *          return NULL
+ *      }
+ *      const AVClass child_class_next(const AVClass *prev)
+ *      {
+ *          return prev ? NULL : &child_class;
+ *      }
+ *      @endcode
+ *      Putting child_next() and child_class_next() as defined above into
+ *      test_class will now make child_struct's options accessible through
+ *      test_struct (again, proper setup as described above needs to be done on
+ *      child_struct right after it is created).
+ *
+ *      From the above example it might not be clear why both child_next()
+ *      and child_class_next() are needed. The distinction is that child_next()
+ *      iterates over actually existing objects, while child_class_next()
+ *      iterates over all possible child classes. E.g. if an AVCodecContext
+ *      was initialized to use a codec which has private options, then its
+ *      child_next() will return AVCodecContext.priv_data and finish
+ *      iterating. OTOH child_class_next() on AVCodecContext.av_class will
+ *      iterate over all available codecs with private options.
+ *
+ * @subsection avoptions_implement_named_constants Named constants
+ *      It is possible to create named constants for options. Simply set the unit
+ *      field of the option the constants should apply to a string and
+ *      create the constants themselves as options of type AV_OPT_TYPE_CONST
+ *      with their unit field set to the same string.
+ *      Their default_val field should contain the value of the named
+ *      constant.
+ *      For example, to add some named constants for the test_flags option
+ *      above, put the following into the child_opts array:
+ *      @code
+ *      { "test_flags", "This is a test option of flags type.",
+ *        offsetof(child_struct, flags_opt), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX, "test_unit" },
+ *      { "flag1", "This is a flag with value 16", 0, AV_OPT_TYPE_CONST, { .i64 = 16 }, 0, 0, "test_unit" },
+ *      @endcode
+ *
+ * @section avoptions_use Using AVOptions
+ * This section deals with accessing options in an AVOptions-enabled struct.
+ * Such structs in FFmpeg are e.g. AVCodecContext in libavcodec or
+ * AVFormatContext in libavformat.
+ *
+ * @subsection avoptions_use_examine Examining AVOptions
+ * The basic functions for examining options are av_opt_next(), which iterates
+ * over all options defined for one object, and av_opt_find(), which searches
+ * for an option with the given name.
+ *
+ * The situation is more complicated with nesting. An AVOptions-enabled struct
+ * may have AVOptions-enabled children. Passing the AV_OPT_SEARCH_CHILDREN flag
+ * to av_opt_find() will make the function search children recursively.
+ *
+ * For enumerating there are basically two cases. The first is when you want to
+ * get all options that may potentially exist on the struct and its children
+ * (e.g.  when constructing documentation). In that case you should call
+ * av_opt_child_class_next() recursively on the parent struct's AVClass.  The
+ * second case is when you have an already initialized struct with all its
+ * children and you want to get all options that can be actually written or read
+ * from it. In that case you should call av_opt_child_next() recursively (and
+ * av_opt_next() on each result).
+ *
+ * @subsection avoptions_use_get_set Reading and writing AVOptions
+ * When setting options, you often have a string read directly from the
+ * user. In such a case, simply passing it to av_opt_set() is enough. For
+ * non-string type options, av_opt_set() will parse the string according to the
+ * option type.
+ *
+ * Similarly av_opt_get() will read any option type and convert it to a string
+ * which will be returned. Do not forget that the string is allocated, so you
+ * have to free it with av_free().
+ *
+ * In some cases it may be more convenient to put all options into an
+ * AVDictionary and call av_opt_set_dict() on it. A specific case of this
+ * are the format/codec open functions in lavf/lavc which take a dictionary
+ * filled with option as a parameter. This makes it possible to set some options
+ * that cannot be set otherwise, since e.g. the input file format is not known
+ * before the file is actually opened.
+ */
+
+enum AVOptionType{
+    AV_OPT_TYPE_FLAGS,
+    AV_OPT_TYPE_INT,
+    AV_OPT_TYPE_INT64,
+    AV_OPT_TYPE_DOUBLE,
+    AV_OPT_TYPE_FLOAT,
+    AV_OPT_TYPE_STRING,
+    AV_OPT_TYPE_RATIONAL,
+    AV_OPT_TYPE_BINARY,  ///< offset must point to a pointer immediately followed by an int for the length
+    AV_OPT_TYPE_DICT,
+    AV_OPT_TYPE_UINT64,
+    AV_OPT_TYPE_CONST,
+    AV_OPT_TYPE_IMAGE_SIZE, ///< offset must point to two consecutive integers
+    AV_OPT_TYPE_PIXEL_FMT,
+    AV_OPT_TYPE_SAMPLE_FMT,
+    AV_OPT_TYPE_VIDEO_RATE, ///< offset must point to AVRational
+    AV_OPT_TYPE_DURATION,
+    AV_OPT_TYPE_COLOR,
+    AV_OPT_TYPE_CHANNEL_LAYOUT,
+    AV_OPT_TYPE_BOOL,
+};
+
+/**
+ * AVOption
+ */
+typedef struct AVOption {
+    const char *name;
+
+    /**
+     * short English help text
+     * @todo What about other languages?
+     */
+    const char *help;
+
+    /**
+     * The offset relative to the context structure where the option
+     * value is stored. It should be 0 for named constants.
+     */
+    int offset;
+    enum AVOptionType type;
+
+    /**
+     * the default value for scalar options
+     */
+    union {
+        int64_t i64;
+        double dbl;
+        const char *str;
+        /* TODO those are unused now */
+        AVRational q;
+    } default_val;
+    double min;                 ///< minimum valid value for the option
+    double max;                 ///< maximum valid value for the option
+
+    int flags;
+#define AV_OPT_FLAG_ENCODING_PARAM  1   ///< a generic parameter which can be set by the user for muxing or encoding
+#define AV_OPT_FLAG_DECODING_PARAM  2   ///< a generic parameter which can be set by the user for demuxing or decoding
+#define AV_OPT_FLAG_AUDIO_PARAM     8
+#define AV_OPT_FLAG_VIDEO_PARAM     16
+#define AV_OPT_FLAG_SUBTITLE_PARAM  32
+/**
+ * The option is intended for exporting values to the caller.
+ */
+#define AV_OPT_FLAG_EXPORT          64
+/**
+ * The option may not be set through the AVOptions API, only read.
+ * This flag only makes sense when AV_OPT_FLAG_EXPORT is also set.
+ */
+#define AV_OPT_FLAG_READONLY        128
+#define AV_OPT_FLAG_BSF_PARAM       (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering
+#define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering
+#define AV_OPT_FLAG_DEPRECATED      (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information
+//FIXME think about enc-audio, ... style flags
+
+    /**
+     * The logical unit to which the option belongs. Non-constant
+     * options and corresponding named constants share the same
+     * unit. May be NULL.
+     */
+    const char *unit;
+} AVOption;
+
+/**
+ * A single allowed range of values, or a single allowed value.
+ */
+typedef struct AVOptionRange {
+    const char *str;
+    /**
+     * Value range.
+     * For string ranges this represents the min/max length.
+     * For dimensions this represents the min/max pixel count or width/height in multi-component case.
+     */
+    double value_min, value_max;
+    /**
+     * Value's component range.
+     * For string this represents the unicode range for chars, 0-127 limits to ASCII.
+     */
+    double component_min, component_max;
+    /**
+     * Range flag.
+     * If set to 1 the struct encodes a range, if set to 0 a single value.
+     */
+    int is_range;
+} AVOptionRange;
+
+/**
+ * List of AVOptionRange structs.
+ */
+typedef struct AVOptionRanges {
+    /**
+     * Array of option ranges.
+     *
+     * Most of option types use just one component.
+     * Following describes multi-component option types:
+     *
+     * AV_OPT_TYPE_IMAGE_SIZE:
+     * component index 0: range of pixel count (width * height).
+     * component index 1: range of width.
+     * component index 2: range of height.
+     *
+     * @note To obtain multi-component version of this structure, user must
+     *       provide AV_OPT_MULTI_COMPONENT_RANGE to av_opt_query_ranges or
+     *       av_opt_query_ranges_default function.
+     *
+     * Multi-component range can be read as in following example:
+     *
+     * @code
+     * int range_index, component_index;
+     * AVOptionRanges *ranges;
+     * AVOptionRange *range[3]; //may require more than 3 in the future.
+     * av_opt_query_ranges(&ranges, obj, key, AV_OPT_MULTI_COMPONENT_RANGE);
+     * for (range_index = 0; range_index < ranges->nb_ranges; range_index++) {
+     *     for (component_index = 0; component_index < ranges->nb_components; component_index++)
+     *         range[component_index] = ranges->range[ranges->nb_ranges * component_index + range_index];
+     *     //do something with range here.
+     * }
+     * av_opt_freep_ranges(&ranges);
+     * @endcode
+     */
+    AVOptionRange **range;
+    /**
+     * Number of ranges per component.
+     */
+    int nb_ranges;
+    /**
+     * Number of componentes.
+     */
+    int nb_components;
+} AVOptionRanges;
+
+/**
+ * Show the obj options.
+ *
+ * @param req_flags requested flags for the options to show. Show only the
+ * options for which it is opt->flags & req_flags.
+ * @param rej_flags rejected flags for the options to show. Show only the
+ * options for which it is !(opt->flags & req_flags).
+ * @param av_log_obj log context to use for showing the options
+ */
+int av_opt_show2(void *obj, void *av_log_obj, int req_flags, int rej_flags);
+
+/**
+ * Set the values of all AVOption fields to their default values.
+ *
+ * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass)
+ */
+void av_opt_set_defaults(void *s);
+
+/**
+ * Set the values of all AVOption fields to their default values. Only these
+ * AVOption fields for which (opt->flags & mask) == flags will have their
+ * default applied to s.
+ *
+ * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass)
+ * @param mask combination of AV_OPT_FLAG_*
+ * @param flags combination of AV_OPT_FLAG_*
+ */
+void av_opt_set_defaults2(void *s, int mask, int flags);
+
+/**
+ * Parse the key/value pairs list in opts. For each key/value pair
+ * found, stores the value in the field in ctx that is named like the
+ * key. ctx must be an AVClass context, storing is done using
+ * AVOptions.
+ *
+ * @param opts options string to parse, may be NULL
+ * @param key_val_sep a 0-terminated list of characters used to
+ * separate key from value
+ * @param pairs_sep a 0-terminated list of characters used to separate
+ * two pairs from each other
+ * @return the number of successfully set key/value pairs, or a negative
+ * value corresponding to an AVERROR code in case of error:
+ * AVERROR(EINVAL) if opts cannot be parsed,
+ * the error code issued by av_opt_set() if a key/value pair
+ * cannot be set
+ */
+int av_set_options_string(void *ctx, const char *opts,
+                          const char *key_val_sep, const char *pairs_sep);
+
+/**
+ * Parse the key-value pairs list in opts. For each key=value pair found,
+ * set the value of the corresponding option in ctx.
+ *
+ * @param ctx          the AVClass object to set options on
+ * @param opts         the options string, key-value pairs separated by a
+ *                     delimiter
+ * @param shorthand    a NULL-terminated array of options names for shorthand
+ *                     notation: if the first field in opts has no key part,
+ *                     the key is taken from the first element of shorthand;
+ *                     then again for the second, etc., until either opts is
+ *                     finished, shorthand is finished or a named option is
+ *                     found; after that, all options must be named
+ * @param key_val_sep  a 0-terminated list of characters used to separate
+ *                     key from value, for example '='
+ * @param pairs_sep    a 0-terminated list of characters used to separate
+ *                     two pairs from each other, for example ':' or ','
+ * @return  the number of successfully set key=value pairs, or a negative
+ *          value corresponding to an AVERROR code in case of error:
+ *          AVERROR(EINVAL) if opts cannot be parsed,
+ *          the error code issued by av_set_string3() if a key/value pair
+ *          cannot be set
+ *
+ * Options names must use only the following characters: a-z A-Z 0-9 - . / _
+ * Separators must use characters distinct from option names and from each
+ * other.
+ */
+int av_opt_set_from_string(void *ctx, const char *opts,
+                           const char *const *shorthand,
+                           const char *key_val_sep, const char *pairs_sep);
+/**
+ * Free all allocated objects in obj.
+ */
+void av_opt_free(void *obj);
+
+/**
+ * Check whether a particular flag is set in a flags field.
+ *
+ * @param field_name the name of the flag field option
+ * @param flag_name the name of the flag to check
+ * @return non-zero if the flag is set, zero if the flag isn't set,
+ *         isn't of the right type, or the flags field doesn't exist.
+ */
+int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name);
+
+/**
+ * Set all the options from a given dictionary on an object.
+ *
+ * @param obj a struct whose first element is a pointer to AVClass
+ * @param options options to process. This dictionary will be freed and replaced
+ *                by a new one containing all options not found in obj.
+ *                Of course this new dictionary needs to be freed by caller
+ *                with av_dict_free().
+ *
+ * @return 0 on success, a negative AVERROR if some option was found in obj,
+ *         but could not be set.
+ *
+ * @see av_dict_copy()
+ */
+int av_opt_set_dict(void *obj, struct AVDictionary **options);
+
+
+/**
+ * Set all the options from a given dictionary on an object.
+ *
+ * @param obj a struct whose first element is a pointer to AVClass
+ * @param options options to process. This dictionary will be freed and replaced
+ *                by a new one containing all options not found in obj.
+ *                Of course this new dictionary needs to be freed by caller
+ *                with av_dict_free().
+ * @param search_flags A combination of AV_OPT_SEARCH_*.
+ *
+ * @return 0 on success, a negative AVERROR if some option was found in obj,
+ *         but could not be set.
+ *
+ * @see av_dict_copy()
+ */
+int av_opt_set_dict2(void *obj, struct AVDictionary **options, int search_flags);
+
+/**
+ * Extract a key-value pair from the beginning of a string.
+ *
+ * @param ropts        pointer to the options string, will be updated to
+ *                     point to the rest of the string (one of the pairs_sep
+ *                     or the final NUL)
+ * @param key_val_sep  a 0-terminated list of characters used to separate
+ *                     key from value, for example '='
+ * @param pairs_sep    a 0-terminated list of characters used to separate
+ *                     two pairs from each other, for example ':' or ','
+ * @param flags        flags; see the AV_OPT_FLAG_* values below
+ * @param rkey         parsed key; must be freed using av_free()
+ * @param rval         parsed value; must be freed using av_free()
+ *
+ * @return  >=0 for success, or a negative value corresponding to an
+ *          AVERROR code in case of error; in particular:
+ *          AVERROR(EINVAL) if no key is present
+ *
+ */
+int av_opt_get_key_value(const char **ropts,
+                         const char *key_val_sep, const char *pairs_sep,
+                         unsigned flags,
+                         char **rkey, char **rval);
+
+enum {
+
+    /**
+     * Accept to parse a value without a key; the key will then be returned
+     * as NULL.
+     */
+    AV_OPT_FLAG_IMPLICIT_KEY = 1,
+};
+
+/**
+ * @defgroup opt_eval_funcs Evaluating option strings
+ * @{
+ * This group of functions can be used to evaluate option strings
+ * and get numbers out of them. They do the same thing as av_opt_set(),
+ * except the result is written into the caller-supplied pointer.
+ *
+ * @param obj a struct whose first element is a pointer to AVClass.
+ * @param o an option for which the string is to be evaluated.
+ * @param val string to be evaluated.
+ * @param *_out value of the string will be written here.
+ *
+ * @return 0 on success, a negative number on failure.
+ */
+int av_opt_eval_flags (void *obj, const AVOption *o, const char *val, int        *flags_out);
+int av_opt_eval_int   (void *obj, const AVOption *o, const char *val, int        *int_out);
+int av_opt_eval_int64 (void *obj, const AVOption *o, const char *val, int64_t    *int64_out);
+int av_opt_eval_float (void *obj, const AVOption *o, const char *val, float      *float_out);
+int av_opt_eval_double(void *obj, const AVOption *o, const char *val, double     *double_out);
+int av_opt_eval_q     (void *obj, const AVOption *o, const char *val, AVRational *q_out);
+/**
+ * @}
+ */
+
+#define AV_OPT_SEARCH_CHILDREN   (1 << 0) /**< Search in possible children of the
+                                               given object first. */
+/**
+ *  The obj passed to av_opt_find() is fake -- only a double pointer to AVClass
+ *  instead of a required pointer to a struct containing AVClass. This is
+ *  useful for searching for options without needing to allocate the corresponding
+ *  object.
+ */
+#define AV_OPT_SEARCH_FAKE_OBJ   (1 << 1)
+
+/**
+ *  In av_opt_get, return NULL if the option has a pointer type and is set to NULL,
+ *  rather than returning an empty string.
+ */
+#define AV_OPT_ALLOW_NULL (1 << 2)
+
+/**
+ *  Allows av_opt_query_ranges and av_opt_query_ranges_default to return more than
+ *  one component for certain option types.
+ *  @see AVOptionRanges for details.
+ */
+#define AV_OPT_MULTI_COMPONENT_RANGE (1 << 12)
+
+/**
+ * Look for an option in an object. Consider only options which
+ * have all the specified flags set.
+ *
+ * @param[in] obj A pointer to a struct whose first element is a
+ *                pointer to an AVClass.
+ *                Alternatively a double pointer to an AVClass, if
+ *                AV_OPT_SEARCH_FAKE_OBJ search flag is set.
+ * @param[in] name The name of the option to look for.
+ * @param[in] unit When searching for named constants, name of the unit
+ *                 it belongs to.
+ * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG).
+ * @param search_flags A combination of AV_OPT_SEARCH_*.
+ *
+ * @return A pointer to the option found, or NULL if no option
+ *         was found.
+ *
+ * @note Options found with AV_OPT_SEARCH_CHILDREN flag may not be settable
+ * directly with av_opt_set(). Use special calls which take an options
+ * AVDictionary (e.g. avformat_open_input()) to set options found with this
+ * flag.
+ */
+const AVOption *av_opt_find(void *obj, const char *name, const char *unit,
+                            int opt_flags, int search_flags);
+
+/**
+ * Look for an option in an object. Consider only options which
+ * have all the specified flags set.
+ *
+ * @param[in] obj A pointer to a struct whose first element is a
+ *                pointer to an AVClass.
+ *                Alternatively a double pointer to an AVClass, if
+ *                AV_OPT_SEARCH_FAKE_OBJ search flag is set.
+ * @param[in] name The name of the option to look for.
+ * @param[in] unit When searching for named constants, name of the unit
+ *                 it belongs to.
+ * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG).
+ * @param search_flags A combination of AV_OPT_SEARCH_*.
+ * @param[out] target_obj if non-NULL, an object to which the option belongs will be
+ * written here. It may be different from obj if AV_OPT_SEARCH_CHILDREN is present
+ * in search_flags. This parameter is ignored if search_flags contain
+ * AV_OPT_SEARCH_FAKE_OBJ.
+ *
+ * @return A pointer to the option found, or NULL if no option
+ *         was found.
+ */
+const AVOption *av_opt_find2(void *obj, const char *name, const char *unit,
+                             int opt_flags, int search_flags, void **target_obj);
+
+/**
+ * Iterate over all AVOptions belonging to obj.
+ *
+ * @param obj an AVOptions-enabled struct or a double pointer to an
+ *            AVClass describing it.
+ * @param prev result of the previous call to av_opt_next() on this object
+ *             or NULL
+ * @return next AVOption or NULL
+ */
+const AVOption *av_opt_next(const void *obj, const AVOption *prev);
+
+/**
+ * Iterate over AVOptions-enabled children of obj.
+ *
+ * @param prev result of a previous call to this function or NULL
+ * @return next AVOptions-enabled child or NULL
+ */
+void *av_opt_child_next(void *obj, void *prev);
+
+/**
+ * Iterate over potential AVOptions-enabled children of parent.
+ *
+ * @param prev result of a previous call to this function or NULL
+ * @return AVClass corresponding to next potential child or NULL
+ */
+const AVClass *av_opt_child_class_next(const AVClass *parent, const AVClass *prev);
+
+/**
+ * @defgroup opt_set_funcs Option setting functions
+ * @{
+ * Those functions set the field of obj with the given name to value.
+ *
+ * @param[in] obj A struct whose first element is a pointer to an AVClass.
+ * @param[in] name the name of the field to set
+ * @param[in] val The value to set. In case of av_opt_set() if the field is not
+ * of a string type, then the given string is parsed.
+ * SI postfixes and some named scalars are supported.
+ * If the field is of a numeric type, it has to be a numeric or named
+ * scalar. Behavior with more than one scalar and +- infix operators
+ * is undefined.
+ * If the field is of a flags type, it has to be a sequence of numeric
+ * scalars or named flags separated by '+' or '-'. Prefixing a flag
+ * with '+' causes it to be set without affecting the other flags;
+ * similarly, '-' unsets a flag.
+ * @param search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN
+ * is passed here, then the option may be set on a child of obj.
+ *
+ * @return 0 if the value has been set, or an AVERROR code in case of
+ * error:
+ * AVERROR_OPTION_NOT_FOUND if no matching option exists
+ * AVERROR(ERANGE) if the value is out of range
+ * AVERROR(EINVAL) if the value is not valid
+ */
+int av_opt_set         (void *obj, const char *name, const char *val, int search_flags);
+int av_opt_set_int     (void *obj, const char *name, int64_t     val, int search_flags);
+int av_opt_set_double  (void *obj, const char *name, double      val, int search_flags);
+int av_opt_set_q       (void *obj, const char *name, AVRational  val, int search_flags);
+int av_opt_set_bin     (void *obj, const char *name, const uint8_t *val, int size, int search_flags);
+int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags);
+int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, int search_flags);
+int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags);
+int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags);
+int av_opt_set_channel_layout(void *obj, const char *name, int64_t ch_layout, int search_flags);
+/**
+ * @note Any old dictionary present is discarded and replaced with a copy of the new one. The
+ * caller still owns val is and responsible for freeing it.
+ */
+int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val, int search_flags);
+
+/**
+ * Set a binary option to an integer list.
+ *
+ * @param obj    AVClass object to set options on
+ * @param name   name of the binary option
+ * @param val    pointer to an integer list (must have the correct type with
+ *               regard to the contents of the list)
+ * @param term   list terminator (usually 0 or -1)
+ * @param flags  search flags
+ */
+#define av_opt_set_int_list(obj, name, val, term, flags) \
+    (av_int_list_length(val, term) > INT_MAX / sizeof(*(val)) ? \
+     AVERROR(EINVAL) : \
+     av_opt_set_bin(obj, name, (const uint8_t *)(val), \
+                    av_int_list_length(val, term) * sizeof(*(val)), flags))
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup opt_get_funcs Option getting functions
+ * @{
+ * Those functions get a value of the option with the given name from an object.
+ *
+ * @param[in] obj a struct whose first element is a pointer to an AVClass.
+ * @param[in] name name of the option to get.
+ * @param[in] search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN
+ * is passed here, then the option may be found in a child of obj.
+ * @param[out] out_val value of the option will be written here
+ * @return >=0 on success, a negative error code otherwise
+ */
+/**
+ * @note the returned string will be av_malloc()ed and must be av_free()ed by the caller
+ *
+ * @note if AV_OPT_ALLOW_NULL is set in search_flags in av_opt_get, and the option has
+ * AV_OPT_TYPE_STRING or AV_OPT_TYPE_BINARY and is set to NULL, *out_val will be set
+ * to NULL instead of an allocated empty string.
+ */
+int av_opt_get         (void *obj, const char *name, int search_flags, uint8_t   **out_val);
+int av_opt_get_int     (void *obj, const char *name, int search_flags, int64_t    *out_val);
+int av_opt_get_double  (void *obj, const char *name, int search_flags, double     *out_val);
+int av_opt_get_q       (void *obj, const char *name, int search_flags, AVRational *out_val);
+int av_opt_get_image_size(void *obj, const char *name, int search_flags, int *w_out, int *h_out);
+int av_opt_get_pixel_fmt (void *obj, const char *name, int search_flags, enum AVPixelFormat *out_fmt);
+int av_opt_get_sample_fmt(void *obj, const char *name, int search_flags, enum AVSampleFormat *out_fmt);
+int av_opt_get_video_rate(void *obj, const char *name, int search_flags, AVRational *out_val);
+int av_opt_get_channel_layout(void *obj, const char *name, int search_flags, int64_t *ch_layout);
+/**
+ * @param[out] out_val The returned dictionary is a copy of the actual value and must
+ * be freed with av_dict_free() by the caller
+ */
+int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val);
+/**
+ * @}
+ */
+/**
+ * Gets a pointer to the requested field in a struct.
+ * This function allows accessing a struct even when its fields are moved or
+ * renamed since the application making the access has been compiled,
+ *
+ * @returns a pointer to the field, it can be cast to the correct type and read
+ *          or written to.
+ */
+void *av_opt_ptr(const AVClass *avclass, void *obj, const char *name);
+
+/**
+ * Free an AVOptionRanges struct and set it to NULL.
+ */
+void av_opt_freep_ranges(AVOptionRanges **ranges);
+
+/**
+ * Get a list of allowed ranges for the given option.
+ *
+ * The returned list may depend on other fields in obj like for example profile.
+ *
+ * @param flags is a bitmask of flags, undefined flags should not be set and should be ignored
+ *              AV_OPT_SEARCH_FAKE_OBJ indicates that the obj is a double pointer to a AVClass instead of a full instance
+ *              AV_OPT_MULTI_COMPONENT_RANGE indicates that function may return more than one component, @see AVOptionRanges
+ *
+ * The result must be freed with av_opt_freep_ranges.
+ *
+ * @return number of compontents returned on success, a negative errro code otherwise
+ */
+int av_opt_query_ranges(AVOptionRanges **, void *obj, const char *key, int flags);
+
+/**
+ * Copy options from src object into dest object.
+ *
+ * Options that require memory allocation (e.g. string or binary) are malloc'ed in dest object.
+ * Original memory allocated for such options is freed unless both src and dest options points to the same memory.
+ *
+ * @param dest Object to copy from
+ * @param src  Object to copy into
+ * @return 0 on success, negative on error
+ */
+int av_opt_copy(void *dest, const void *src);
+
+/**
+ * Get a default list of allowed ranges for the given option.
+ *
+ * This list is constructed without using the AVClass.query_ranges() callback
+ * and can be used as fallback from within the callback.
+ *
+ * @param flags is a bitmask of flags, undefined flags should not be set and should be ignored
+ *              AV_OPT_SEARCH_FAKE_OBJ indicates that the obj is a double pointer to a AVClass instead of a full instance
+ *              AV_OPT_MULTI_COMPONENT_RANGE indicates that function may return more than one component, @see AVOptionRanges
+ *
+ * The result must be freed with av_opt_free_ranges.
+ *
+ * @return number of compontents returned on success, a negative errro code otherwise
+ */
+int av_opt_query_ranges_default(AVOptionRanges **, void *obj, const char *key, int flags);
+
+/**
+ * Check if given option is set to its default value.
+ *
+ * Options o must belong to the obj. This function must not be called to check child's options state.
+ * @see av_opt_is_set_to_default_by_name().
+ *
+ * @param obj  AVClass object to check option on
+ * @param o    option to be checked
+ * @return     >0 when option is set to its default,
+ *              0 when option is not set its default,
+ *             <0 on error
+ */
+int av_opt_is_set_to_default(void *obj, const AVOption *o);
+
+/**
+ * Check if given option is set to its default value.
+ *
+ * @param obj          AVClass object to check option on
+ * @param name         option name
+ * @param search_flags combination of AV_OPT_SEARCH_*
+ * @return             >0 when option is set to its default,
+ *                     0 when option is not set its default,
+ *                     <0 on error
+ */
+int av_opt_is_set_to_default_by_name(void *obj, const char *name, int search_flags);
+
+
+#define AV_OPT_SERIALIZE_SKIP_DEFAULTS              0x00000001  ///< Serialize options that are not set to default values only.
+#define AV_OPT_SERIALIZE_OPT_FLAGS_EXACT            0x00000002  ///< Serialize options that exactly match opt_flags only.
+
+/**
+ * Serialize object's options.
+ *
+ * Create a string containing object's serialized options.
+ * Such string may be passed back to av_opt_set_from_string() in order to restore option values.
+ * A key/value or pairs separator occurring in the serialized value or
+ * name string are escaped through the av_escape() function.
+ *
+ * @param[in]  obj           AVClass object to serialize
+ * @param[in]  opt_flags     serialize options with all the specified flags set (AV_OPT_FLAG)
+ * @param[in]  flags         combination of AV_OPT_SERIALIZE_* flags
+ * @param[out] buffer        Pointer to buffer that will be allocated with string containg serialized options.
+ *                           Buffer must be freed by the caller when is no longer needed.
+ * @param[in]  key_val_sep   character used to separate key from value
+ * @param[in]  pairs_sep     character used to separate two pairs from each other
+ * @return                   >= 0 on success, negative on error
+ * @warning Separators cannot be neither '\\' nor '\0'. They also cannot be the same.
+ */
+int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer,
+                     const char key_val_sep, const char pairs_sep);
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_OPT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/parseutils.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/parseutils.h
new file mode 100644
index 0000000..e66d24b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/parseutils.h
@@ -0,0 +1,193 @@
+/*
+ * 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 AVUTIL_PARSEUTILS_H
+#define AVUTIL_PARSEUTILS_H
+
+#include <time.h>
+
+#include "rational.h"
+
+/**
+ * @file
+ * misc parsing utilities
+ */
+
+/**
+ * Parse str and store the parsed ratio in q.
+ *
+ * Note that a ratio with infinite (1/0) or negative value is
+ * considered valid, so you should check on the returned value if you
+ * want to exclude those values.
+ *
+ * The undefined value can be expressed using the "0:0" string.
+ *
+ * @param[in,out] q pointer to the AVRational which will contain the ratio
+ * @param[in] str the string to parse: it has to be a string in the format
+ * num:den, a float number or an expression
+ * @param[in] max the maximum allowed numerator and denominator
+ * @param[in] log_offset log level offset which is applied to the log
+ * level of log_ctx
+ * @param[in] log_ctx parent logging context
+ * @return >= 0 on success, a negative error code otherwise
+ */
+int av_parse_ratio(AVRational *q, const char *str, int max,
+                   int log_offset, void *log_ctx);
+
+#define av_parse_ratio_quiet(rate, str, max) \
+    av_parse_ratio(rate, str, max, AV_LOG_MAX_OFFSET, NULL)
+
+/**
+ * Parse str and put in width_ptr and height_ptr the detected values.
+ *
+ * @param[in,out] width_ptr pointer to the variable which will contain the detected
+ * width value
+ * @param[in,out] height_ptr pointer to the variable which will contain the detected
+ * height value
+ * @param[in] str the string to parse: it has to be a string in the format
+ * width x height or a valid video size abbreviation.
+ * @return >= 0 on success, a negative error code otherwise
+ */
+int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str);
+
+/**
+ * Parse str and store the detected values in *rate.
+ *
+ * @param[in,out] rate pointer to the AVRational which will contain the detected
+ * frame rate
+ * @param[in] str the string to parse: it has to be a string in the format
+ * rate_num / rate_den, a float number or a valid video rate abbreviation
+ * @return >= 0 on success, a negative error code otherwise
+ */
+int av_parse_video_rate(AVRational *rate, const char *str);
+
+/**
+ * Put the RGBA values that correspond to color_string in rgba_color.
+ *
+ * @param color_string a string specifying a color. It can be the name of
+ * a color (case insensitive match) or a [0x|#]RRGGBB[AA] sequence,
+ * possibly followed by "@" and a string representing the alpha
+ * component.
+ * The alpha component may be a string composed by "0x" followed by an
+ * hexadecimal number or a decimal number between 0.0 and 1.0, which
+ * represents the opacity value (0x00/0.0 means completely transparent,
+ * 0xff/1.0 completely opaque).
+ * If the alpha component is not specified then 0xff is assumed.
+ * The string "random" will result in a random color.
+ * @param slen length of the initial part of color_string containing the
+ * color. It can be set to -1 if color_string is a null terminated string
+ * containing nothing else than the color.
+ * @return >= 0 in case of success, a negative value in case of
+ * failure (for example if color_string cannot be parsed).
+ */
+int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen,
+                   void *log_ctx);
+
+/**
+ * Get the name of a color from the internal table of hard-coded named
+ * colors.
+ *
+ * This function is meant to enumerate the color names recognized by
+ * av_parse_color().
+ *
+ * @param color_idx index of the requested color, starting from 0
+ * @param rgbp      if not NULL, will point to a 3-elements array with the color value in RGB
+ * @return the color name string or NULL if color_idx is not in the array
+ */
+const char *av_get_known_color_name(int color_idx, const uint8_t **rgb);
+
+/**
+ * Parse timestr and return in *time a corresponding number of
+ * microseconds.
+ *
+ * @param timeval puts here the number of microseconds corresponding
+ * to the string in timestr. If the string represents a duration, it
+ * is the number of microseconds contained in the time interval.  If
+ * the string is a date, is the number of microseconds since 1st of
+ * January, 1970 up to the time of the parsed date.  If timestr cannot
+ * be successfully parsed, set *time to INT64_MIN.
+
+ * @param timestr a string representing a date or a duration.
+ * - If a date the syntax is:
+ * @code
+ * [{YYYY-MM-DD|YYYYMMDD}[T|t| ]]{{HH:MM:SS[.m...]]]}|{HHMMSS[.m...]]]}}[Z]
+ * now
+ * @endcode
+ * If the value is "now" it takes the current time.
+ * Time is local time unless Z is appended, in which case it is
+ * interpreted as UTC.
+ * If the year-month-day part is not specified it takes the current
+ * year-month-day.
+ * - If a duration the syntax is:
+ * @code
+ * [-][HH:]MM:SS[.m...]
+ * [-]S+[.m...]
+ * @endcode
+ * @param duration flag which tells how to interpret timestr, if not
+ * zero timestr is interpreted as a duration, otherwise as a date
+ * @return >= 0 in case of success, a negative value corresponding to an
+ * AVERROR code otherwise
+ */
+int av_parse_time(int64_t *timeval, const char *timestr, int duration);
+
+/**
+ * Attempt to find a specific tag in a URL.
+ *
+ * syntax: '?tag1=val1&tag2=val2...'. Little URL decoding is done.
+ * Return 1 if found.
+ */
+int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info);
+
+/**
+ * Simplified version of strptime
+ *
+ * Parse the input string p according to the format string fmt and
+ * store its results in the structure dt.
+ * This implementation supports only a subset of the formats supported
+ * by the standard strptime().
+ *
+ * The supported input field descriptors are listed below.
+ * - %H: the hour as a decimal number, using a 24-hour clock, in the
+ *   range '00' through '23'
+ * - %J: hours as a decimal number, in the range '0' through INT_MAX
+ * - %M: the minute as a decimal number, using a 24-hour clock, in the
+ *   range '00' through '59'
+ * - %S: the second as a decimal number, using a 24-hour clock, in the
+ *   range '00' through '59'
+ * - %Y: the year as a decimal number, using the Gregorian calendar
+ * - %m: the month as a decimal number, in the range '1' through '12'
+ * - %d: the day of the month as a decimal number, in the range '1'
+ *   through '31'
+ * - %T: alias for '%H:%M:%S'
+ * - %%: a literal '%'
+ *
+ * @return a pointer to the first character not processed in this function
+ *         call. In case the input string contains more characters than
+ *         required by the format string the return value points right after
+ *         the last consumed input character. In case the whole input string
+ *         is consumed the return value points to the null byte at the end of
+ *         the string. On failure NULL is returned.
+ */
+char *av_small_strptime(const char *p, const char *fmt, struct tm *dt);
+
+/**
+ * Convert the decomposed UTC time in tm to a time_t value.
+ */
+time_t av_timegm(struct tm *tm);
+
+#endif /* AVUTIL_PARSEUTILS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixdesc.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixdesc.h
new file mode 100644
index 0000000..c055810
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixdesc.h
@@ -0,0 +1,440 @@
+/*
+ * pixel format descriptor
+ * Copyright (c) 2009 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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 AVUTIL_PIXDESC_H
+#define AVUTIL_PIXDESC_H
+
+#include <inttypes.h>
+
+#include "attributes.h"
+#include "pixfmt.h"
+#include "version.h"
+
+typedef struct AVComponentDescriptor {
+    /**
+     * Which of the 4 planes contains the component.
+     */
+    int plane;
+
+    /**
+     * Number of elements between 2 horizontally consecutive pixels.
+     * Elements are bits for bitstream formats, bytes otherwise.
+     */
+    int step;
+
+    /**
+     * Number of elements before the component of the first pixel.
+     * Elements are bits for bitstream formats, bytes otherwise.
+     */
+    int offset;
+
+    /**
+     * Number of least significant bits that must be shifted away
+     * to get the value.
+     */
+    int shift;
+
+    /**
+     * Number of bits in the component.
+     */
+    int depth;
+
+#if FF_API_PLUS1_MINUS1
+    /** deprecated, use step instead */
+    attribute_deprecated int step_minus1;
+
+    /** deprecated, use depth instead */
+    attribute_deprecated int depth_minus1;
+
+    /** deprecated, use offset instead */
+    attribute_deprecated int offset_plus1;
+#endif
+} AVComponentDescriptor;
+
+/**
+ * Descriptor that unambiguously describes how the bits of a pixel are
+ * stored in the up to 4 data planes of an image. It also stores the
+ * subsampling factors and number of components.
+ *
+ * @note This is separate of the colorspace (RGB, YCbCr, YPbPr, JPEG-style YUV
+ *       and all the YUV variants) AVPixFmtDescriptor just stores how values
+ *       are stored not what these values represent.
+ */
+typedef struct AVPixFmtDescriptor {
+    const char *name;
+    uint8_t nb_components;  ///< The number of components each pixel has, (1-4)
+
+    /**
+     * Amount to shift the luma width right to find the chroma width.
+     * For YV12 this is 1 for example.
+     * chroma_width = AV_CEIL_RSHIFT(luma_width, log2_chroma_w)
+     * The note above is needed to ensure rounding up.
+     * This value only refers to the chroma components.
+     */
+    uint8_t log2_chroma_w;
+
+    /**
+     * Amount to shift the luma height right to find the chroma height.
+     * For YV12 this is 1 for example.
+     * chroma_height= AV_CEIL_RSHIFT(luma_height, log2_chroma_h)
+     * The note above is needed to ensure rounding up.
+     * This value only refers to the chroma components.
+     */
+    uint8_t log2_chroma_h;
+
+    /**
+     * Combination of AV_PIX_FMT_FLAG_... flags.
+     */
+    uint64_t flags;
+
+    /**
+     * Parameters that describe how pixels are packed.
+     * If the format has 1 or 2 components, then luma is 0.
+     * If the format has 3 or 4 components:
+     *   if the RGB flag is set then 0 is red, 1 is green and 2 is blue;
+     *   otherwise 0 is luma, 1 is chroma-U and 2 is chroma-V.
+     *
+     * If present, the Alpha channel is always the last component.
+     */
+    AVComponentDescriptor comp[4];
+
+    /**
+     * Alternative comma-separated names.
+     */
+    const char *alias;
+} AVPixFmtDescriptor;
+
+/**
+ * Pixel format is big-endian.
+ */
+#define AV_PIX_FMT_FLAG_BE           (1 << 0)
+/**
+ * Pixel format has a palette in data[1], values are indexes in this palette.
+ */
+#define AV_PIX_FMT_FLAG_PAL          (1 << 1)
+/**
+ * All values of a component are bit-wise packed end to end.
+ */
+#define AV_PIX_FMT_FLAG_BITSTREAM    (1 << 2)
+/**
+ * Pixel format is an HW accelerated format.
+ */
+#define AV_PIX_FMT_FLAG_HWACCEL      (1 << 3)
+/**
+ * At least one pixel component is not in the first data plane.
+ */
+#define AV_PIX_FMT_FLAG_PLANAR       (1 << 4)
+/**
+ * The pixel format contains RGB-like data (as opposed to YUV/grayscale).
+ */
+#define AV_PIX_FMT_FLAG_RGB          (1 << 5)
+
+/**
+ * The pixel format is "pseudo-paletted". This means that it contains a
+ * fixed palette in the 2nd plane but the palette is fixed/constant for each
+ * PIX_FMT. This allows interpreting the data as if it was PAL8, which can
+ * in some cases be simpler. Or the data can be interpreted purely based on
+ * the pixel format without using the palette.
+ * An example of a pseudo-paletted format is AV_PIX_FMT_GRAY8
+ *
+ * @deprecated This flag is deprecated, and will be removed. When it is removed,
+ * the extra palette allocation in AVFrame.data[1] is removed as well. Only
+ * actual paletted formats (as indicated by AV_PIX_FMT_FLAG_PAL) will have a
+ * palette. Starting with FFmpeg versions which have this flag deprecated, the
+ * extra "pseudo" palette is already ignored, and API users are not required to
+ * allocate a palette for AV_PIX_FMT_FLAG_PSEUDOPAL formats (it was required
+ * before the deprecation, though).
+ */
+#define AV_PIX_FMT_FLAG_PSEUDOPAL    (1 << 6)
+
+/**
+ * The pixel format has an alpha channel. This is set on all formats that
+ * support alpha in some way, including AV_PIX_FMT_PAL8. The alpha is always
+ * straight, never pre-multiplied.
+ *
+ * If a codec or a filter does not support alpha, it should set all alpha to
+ * opaque, or use the equivalent pixel formats without alpha component, e.g.
+ * AV_PIX_FMT_RGB0 (or AV_PIX_FMT_RGB24 etc.) instead of AV_PIX_FMT_RGBA.
+ */
+#define AV_PIX_FMT_FLAG_ALPHA        (1 << 7)
+
+/**
+ * The pixel format is following a Bayer pattern
+ */
+#define AV_PIX_FMT_FLAG_BAYER        (1 << 8)
+
+/**
+ * The pixel format contains IEEE-754 floating point values. Precision (double,
+ * single, or half) should be determined by the pixel size (64, 32, or 16 bits).
+ */
+#define AV_PIX_FMT_FLAG_FLOAT        (1 << 9)
+
+/**
+ * Return the number of bits per pixel used by the pixel format
+ * described by pixdesc. Note that this is not the same as the number
+ * of bits per sample.
+ *
+ * The returned number of bits refers to the number of bits actually
+ * used for storing the pixel information, that is padding bits are
+ * not counted.
+ */
+int av_get_bits_per_pixel(const AVPixFmtDescriptor *pixdesc);
+
+/**
+ * Return the number of bits per pixel for the pixel format
+ * described by pixdesc, including any padding or unused bits.
+ */
+int av_get_padded_bits_per_pixel(const AVPixFmtDescriptor *pixdesc);
+
+/**
+ * @return a pixel format descriptor for provided pixel format or NULL if
+ * this pixel format is unknown.
+ */
+const AVPixFmtDescriptor *av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt);
+
+/**
+ * Iterate over all pixel format descriptors known to libavutil.
+ *
+ * @param prev previous descriptor. NULL to get the first descriptor.
+ *
+ * @return next descriptor or NULL after the last descriptor
+ */
+const AVPixFmtDescriptor *av_pix_fmt_desc_next(const AVPixFmtDescriptor *prev);
+
+/**
+ * @return an AVPixelFormat id described by desc, or AV_PIX_FMT_NONE if desc
+ * is not a valid pointer to a pixel format descriptor.
+ */
+enum AVPixelFormat av_pix_fmt_desc_get_id(const AVPixFmtDescriptor *desc);
+
+/**
+ * Utility function to access log2_chroma_w log2_chroma_h from
+ * the pixel format AVPixFmtDescriptor.
+ *
+ * @param[in]  pix_fmt the pixel format
+ * @param[out] h_shift store log2_chroma_w (horizontal/width shift)
+ * @param[out] v_shift store log2_chroma_h (vertical/height shift)
+ *
+ * @return 0 on success, AVERROR(ENOSYS) on invalid or unknown pixel format
+ */
+int av_pix_fmt_get_chroma_sub_sample(enum AVPixelFormat pix_fmt,
+                                     int *h_shift, int *v_shift);
+
+/**
+ * @return number of planes in pix_fmt, a negative AVERROR if pix_fmt is not a
+ * valid pixel format.
+ */
+int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt);
+
+/**
+ * @return the name for provided color range or NULL if unknown.
+ */
+const char *av_color_range_name(enum AVColorRange range);
+
+/**
+ * @return the AVColorRange value for name or an AVError if not found.
+ */
+int av_color_range_from_name(const char *name);
+
+/**
+ * @return the name for provided color primaries or NULL if unknown.
+ */
+const char *av_color_primaries_name(enum AVColorPrimaries primaries);
+
+/**
+ * @return the AVColorPrimaries value for name or an AVError if not found.
+ */
+int av_color_primaries_from_name(const char *name);
+
+/**
+ * @return the name for provided color transfer or NULL if unknown.
+ */
+const char *av_color_transfer_name(enum AVColorTransferCharacteristic transfer);
+
+/**
+ * @return the AVColorTransferCharacteristic value for name or an AVError if not found.
+ */
+int av_color_transfer_from_name(const char *name);
+
+/**
+ * @return the name for provided color space or NULL if unknown.
+ */
+const char *av_color_space_name(enum AVColorSpace space);
+
+/**
+ * @return the AVColorSpace value for name or an AVError if not found.
+ */
+int av_color_space_from_name(const char *name);
+
+/**
+ * @return the name for provided chroma location or NULL if unknown.
+ */
+const char *av_chroma_location_name(enum AVChromaLocation location);
+
+/**
+ * @return the AVChromaLocation value for name or an AVError if not found.
+ */
+int av_chroma_location_from_name(const char *name);
+
+/**
+ * Return the pixel format corresponding to name.
+ *
+ * If there is no pixel format with name name, then looks for a
+ * pixel format with the name corresponding to the native endian
+ * format of name.
+ * For example in a little-endian system, first looks for "gray16",
+ * then for "gray16le".
+ *
+ * Finally if no pixel format has been found, returns AV_PIX_FMT_NONE.
+ */
+enum AVPixelFormat av_get_pix_fmt(const char *name);
+
+/**
+ * Return the short name for a pixel format, NULL in case pix_fmt is
+ * unknown.
+ *
+ * @see av_get_pix_fmt(), av_get_pix_fmt_string()
+ */
+const char *av_get_pix_fmt_name(enum AVPixelFormat pix_fmt);
+
+/**
+ * Print in buf the string corresponding to the pixel format with
+ * number pix_fmt, or a header if pix_fmt is negative.
+ *
+ * @param buf the buffer where to write the string
+ * @param buf_size the size of buf
+ * @param pix_fmt the number of the pixel format to print the
+ * corresponding info string, or a negative value to print the
+ * corresponding header.
+ */
+char *av_get_pix_fmt_string(char *buf, int buf_size,
+                            enum AVPixelFormat pix_fmt);
+
+/**
+ * Read a line from an image, and write the values of the
+ * pixel format component c to dst.
+ *
+ * @param data the array containing the pointers to the planes of the image
+ * @param linesize the array containing the linesizes of the image
+ * @param desc the pixel format descriptor for the image
+ * @param x the horizontal coordinate of the first pixel to read
+ * @param y the vertical coordinate of the first pixel to read
+ * @param w the width of the line to read, that is the number of
+ * values to write to dst
+ * @param read_pal_component if not zero and the format is a paletted
+ * format writes the values corresponding to the palette
+ * component c in data[1] to dst, rather than the palette indexes in
+ * data[0]. The behavior is undefined if the format is not paletted.
+ * @param dst_element_size size of elements in dst array (2 or 4 byte)
+ */
+void av_read_image_line2(void *dst, const uint8_t *data[4],
+                        const int linesize[4], const AVPixFmtDescriptor *desc,
+                        int x, int y, int c, int w, int read_pal_component,
+                        int dst_element_size);
+
+void av_read_image_line(uint16_t *dst, const uint8_t *data[4],
+                        const int linesize[4], const AVPixFmtDescriptor *desc,
+                        int x, int y, int c, int w, int read_pal_component);
+
+/**
+ * Write the values from src to the pixel format component c of an
+ * image line.
+ *
+ * @param src array containing the values to write
+ * @param data the array containing the pointers to the planes of the
+ * image to write into. It is supposed to be zeroed.
+ * @param linesize the array containing the linesizes of the image
+ * @param desc the pixel format descriptor for the image
+ * @param x the horizontal coordinate of the first pixel to write
+ * @param y the vertical coordinate of the first pixel to write
+ * @param w the width of the line to write, that is the number of
+ * values to write to the image line
+ * @param src_element_size size of elements in src array (2 or 4 byte)
+ */
+void av_write_image_line2(const void *src, uint8_t *data[4],
+                         const int linesize[4], const AVPixFmtDescriptor *desc,
+                         int x, int y, int c, int w, int src_element_size);
+
+void av_write_image_line(const uint16_t *src, uint8_t *data[4],
+                         const int linesize[4], const AVPixFmtDescriptor *desc,
+                         int x, int y, int c, int w);
+
+/**
+ * Utility function to swap the endianness of a pixel format.
+ *
+ * @param[in]  pix_fmt the pixel format
+ *
+ * @return pixel format with swapped endianness if it exists,
+ * otherwise AV_PIX_FMT_NONE
+ */
+enum AVPixelFormat av_pix_fmt_swap_endianness(enum AVPixelFormat pix_fmt);
+
+#define FF_LOSS_RESOLUTION  0x0001 /**< loss due to resolution change */
+#define FF_LOSS_DEPTH       0x0002 /**< loss due to color depth change */
+#define FF_LOSS_COLORSPACE  0x0004 /**< loss due to color space conversion */
+#define FF_LOSS_ALPHA       0x0008 /**< loss of alpha bits */
+#define FF_LOSS_COLORQUANT  0x0010 /**< loss due to color quantization */
+#define FF_LOSS_CHROMA      0x0020 /**< loss of chroma (e.g. RGB to gray conversion) */
+
+/**
+ * Compute what kind of losses will occur when converting from one specific
+ * pixel format to another.
+ * When converting from one pixel format to another, information loss may occur.
+ * For example, when converting from RGB24 to GRAY, the color information will
+ * be lost. Similarly, other losses occur when converting from some formats to
+ * other formats. These losses can involve loss of chroma, but also loss of
+ * resolution, loss of color depth, loss due to the color space conversion, loss
+ * of the alpha bits or loss due to color quantization.
+ * av_get_fix_fmt_loss() informs you about the various types of losses
+ * which will occur when converting from one pixel format to another.
+ *
+ * @param[in] dst_pix_fmt destination pixel format
+ * @param[in] src_pix_fmt source pixel format
+ * @param[in] has_alpha Whether the source pixel format alpha channel is used.
+ * @return Combination of flags informing you what kind of losses will occur
+ * (maximum loss for an invalid dst_pix_fmt).
+ */
+int av_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt,
+                        enum AVPixelFormat src_pix_fmt,
+                        int has_alpha);
+
+/**
+ * Compute what kind of losses will occur when converting from one specific
+ * pixel format to another.
+ * When converting from one pixel format to another, information loss may occur.
+ * For example, when converting from RGB24 to GRAY, the color information will
+ * be lost. Similarly, other losses occur when converting from some formats to
+ * other formats. These losses can involve loss of chroma, but also loss of
+ * resolution, loss of color depth, loss due to the color space conversion, loss
+ * of the alpha bits or loss due to color quantization.
+ * av_get_fix_fmt_loss() informs you about the various types of losses
+ * which will occur when converting from one pixel format to another.
+ *
+ * @param[in] dst_pix_fmt destination pixel format
+ * @param[in] src_pix_fmt source pixel format
+ * @param[in] has_alpha Whether the source pixel format alpha channel is used.
+ * @return Combination of flags informing you what kind of losses will occur
+ * (maximum loss for an invalid dst_pix_fmt).
+ */
+enum AVPixelFormat av_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2,
+                                             enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr);
+
+#endif /* AVUTIL_PIXDESC_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixelutils.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixelutils.h
new file mode 100644
index 0000000..a8dbc15
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixelutils.h
@@ -0,0 +1,52 @@
+/*
+ * 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 AVUTIL_PIXELUTILS_H
+#define AVUTIL_PIXELUTILS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "common.h"
+
+/**
+ * Sum of abs(src1[x] - src2[x])
+ */
+typedef int (*av_pixelutils_sad_fn)(const uint8_t *src1, ptrdiff_t stride1,
+                                    const uint8_t *src2, ptrdiff_t stride2);
+
+/**
+ * Get a potentially optimized pointer to a Sum-of-absolute-differences
+ * function (see the av_pixelutils_sad_fn prototype).
+ *
+ * @param w_bits  1<<w_bits is the requested width of the block size
+ * @param h_bits  1<<h_bits is the requested height of the block size
+ * @param aligned If set to 2, the returned sad function will assume src1 and
+ *                src2 addresses are aligned on the block size.
+ *                If set to 1, the returned sad function will assume src1 is
+ *                aligned on the block size.
+ *                If set to 0, the returned sad function assume no particular
+ *                alignment.
+ * @param log_ctx context used for logging, can be NULL
+ *
+ * @return a pointer to the SAD function or NULL in case of error (because of
+ *         invalid parameters)
+ */
+av_pixelutils_sad_fn av_pixelutils_get_sad_fn(int w_bits, int h_bits,
+                                              int aligned, void *log_ctx);
+
+#endif /* AVUTIL_PIXELUTILS_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixfmt.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixfmt.h
new file mode 100644
index 0000000..6815f8d
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/pixfmt.h
@@ -0,0 +1,542 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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 AVUTIL_PIXFMT_H
+#define AVUTIL_PIXFMT_H
+
+/**
+ * @file
+ * pixel format definitions
+ */
+
+#include "libavutil/avconfig.h"
+#include "version.h"
+
+#define AVPALETTE_SIZE 1024
+#define AVPALETTE_COUNT 256
+
+/**
+ * Pixel format.
+ *
+ * @note
+ * AV_PIX_FMT_RGB32 is handled in an endian-specific manner. An RGBA
+ * color is put together as:
+ *  (A << 24) | (R << 16) | (G << 8) | B
+ * This is stored as BGRA on little-endian CPU architectures and ARGB on
+ * big-endian CPUs.
+ *
+ * @note
+ * If the resolution is not a multiple of the chroma subsampling factor
+ * then the chroma plane resolution must be rounded up.
+ *
+ * @par
+ * When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized
+ * image data is stored in AVFrame.data[0]. The palette is transported in
+ * AVFrame.data[1], is 1024 bytes long (256 4-byte entries) and is
+ * formatted the same as in AV_PIX_FMT_RGB32 described above (i.e., it is
+ * also endian-specific). Note also that the individual RGB32 palette
+ * components stored in AVFrame.data[1] should be in the range 0..255.
+ * This is important as many custom PAL8 video codecs that were designed
+ * to run on the IBM VGA graphics adapter use 6-bit palette components.
+ *
+ * @par
+ * For all the 8 bits per pixel formats, an RGB32 palette is in data[1] like
+ * for pal8. This palette is filled in automatically by the function
+ * allocating the picture.
+ */
+enum AVPixelFormat {
+    AV_PIX_FMT_NONE = -1,
+    AV_PIX_FMT_YUV420P,   ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
+    AV_PIX_FMT_YUYV422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
+    AV_PIX_FMT_RGB24,     ///< packed RGB 8:8:8, 24bpp, RGBRGB...
+    AV_PIX_FMT_BGR24,     ///< packed RGB 8:8:8, 24bpp, BGRBGR...
+    AV_PIX_FMT_YUV422P,   ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
+    AV_PIX_FMT_YUV444P,   ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
+    AV_PIX_FMT_YUV410P,   ///< planar YUV 4:1:0,  9bpp, (1 Cr & Cb sample per 4x4 Y samples)
+    AV_PIX_FMT_YUV411P,   ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
+    AV_PIX_FMT_GRAY8,     ///<        Y        ,  8bpp
+    AV_PIX_FMT_MONOWHITE, ///<        Y        ,  1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb
+    AV_PIX_FMT_MONOBLACK, ///<        Y        ,  1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb
+    AV_PIX_FMT_PAL8,      ///< 8 bits with AV_PIX_FMT_RGB32 palette
+    AV_PIX_FMT_YUVJ420P,  ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range
+    AV_PIX_FMT_YUVJ422P,  ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting color_range
+    AV_PIX_FMT_YUVJ444P,  ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting color_range
+    AV_PIX_FMT_UYVY422,   ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
+    AV_PIX_FMT_UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3
+    AV_PIX_FMT_BGR8,      ///< packed RGB 3:3:2,  8bpp, (msb)2B 3G 3R(lsb)
+    AV_PIX_FMT_BGR4,      ///< packed RGB 1:2:1 bitstream,  4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits
+    AV_PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1,  8bpp, (msb)1B 2G 1R(lsb)
+    AV_PIX_FMT_RGB8,      ///< packed RGB 3:3:2,  8bpp, (msb)2R 3G 3B(lsb)
+    AV_PIX_FMT_RGB4,      ///< packed RGB 1:2:1 bitstream,  4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits
+    AV_PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1,  8bpp, (msb)1R 2G 1B(lsb)
+    AV_PIX_FMT_NV12,      ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
+    AV_PIX_FMT_NV21,      ///< as above, but U and V bytes are swapped
+
+    AV_PIX_FMT_ARGB,      ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
+    AV_PIX_FMT_RGBA,      ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
+    AV_PIX_FMT_ABGR,      ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
+    AV_PIX_FMT_BGRA,      ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
+
+    AV_PIX_FMT_GRAY16BE,  ///<        Y        , 16bpp, big-endian
+    AV_PIX_FMT_GRAY16LE,  ///<        Y        , 16bpp, little-endian
+    AV_PIX_FMT_YUV440P,   ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
+    AV_PIX_FMT_YUVJ440P,  ///< planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
+    AV_PIX_FMT_YUVA420P,  ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
+    AV_PIX_FMT_RGB48BE,   ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian
+    AV_PIX_FMT_RGB48LE,   ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian
+
+    AV_PIX_FMT_RGB565BE,  ///< packed RGB 5:6:5, 16bpp, (msb)   5R 6G 5B(lsb), big-endian
+    AV_PIX_FMT_RGB565LE,  ///< packed RGB 5:6:5, 16bpp, (msb)   5R 6G 5B(lsb), little-endian
+    AV_PIX_FMT_RGB555BE,  ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), big-endian   , X=unused/undefined
+    AV_PIX_FMT_RGB555LE,  ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), little-endian, X=unused/undefined
+
+    AV_PIX_FMT_BGR565BE,  ///< packed BGR 5:6:5, 16bpp, (msb)   5B 6G 5R(lsb), big-endian
+    AV_PIX_FMT_BGR565LE,  ///< packed BGR 5:6:5, 16bpp, (msb)   5B 6G 5R(lsb), little-endian
+    AV_PIX_FMT_BGR555BE,  ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), big-endian   , X=unused/undefined
+    AV_PIX_FMT_BGR555LE,  ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), little-endian, X=unused/undefined
+
+#if FF_API_VAAPI
+    /** @name Deprecated pixel formats */
+    /**@{*/
+    AV_PIX_FMT_VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers
+    AV_PIX_FMT_VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers
+    AV_PIX_FMT_VAAPI_VLD,  ///< HW decoding through VA API, Picture.data[3] contains a VASurfaceID
+    /**@}*/
+    AV_PIX_FMT_VAAPI = AV_PIX_FMT_VAAPI_VLD,
+#else
+    /**
+     *  Hardware acceleration through VA-API, data[3] contains a
+     *  VASurfaceID.
+     */
+    AV_PIX_FMT_VAAPI,
+#endif
+
+    AV_PIX_FMT_YUV420P16LE,  ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
+    AV_PIX_FMT_YUV420P16BE,  ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
+    AV_PIX_FMT_YUV422P16LE,  ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
+    AV_PIX_FMT_YUV422P16BE,  ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
+    AV_PIX_FMT_YUV444P16LE,  ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
+    AV_PIX_FMT_YUV444P16BE,  ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
+    AV_PIX_FMT_DXVA2_VLD,    ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer
+
+    AV_PIX_FMT_RGB444LE,  ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), little-endian, X=unused/undefined
+    AV_PIX_FMT_RGB444BE,  ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), big-endian,    X=unused/undefined
+    AV_PIX_FMT_BGR444LE,  ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), little-endian, X=unused/undefined
+    AV_PIX_FMT_BGR444BE,  ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), big-endian,    X=unused/undefined
+    AV_PIX_FMT_YA8,       ///< 8 bits gray, 8 bits alpha
+
+    AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8
+    AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8
+
+    AV_PIX_FMT_BGR48BE,   ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian
+    AV_PIX_FMT_BGR48LE,   ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian
+
+    /**
+     * The following 12 formats have the disadvantage of needing 1 format for each bit depth.
+     * Notice that each 9/10 bits sample is stored in 16 bits with extra padding.
+     * If you want to support multiple bit depths, then using AV_PIX_FMT_YUV420P16* with the bpp stored separately is better.
+     */
+    AV_PIX_FMT_YUV420P9BE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
+    AV_PIX_FMT_YUV420P9LE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
+    AV_PIX_FMT_YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
+    AV_PIX_FMT_YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
+    AV_PIX_FMT_YUV422P10BE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
+    AV_PIX_FMT_YUV422P10LE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
+    AV_PIX_FMT_YUV444P9BE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
+    AV_PIX_FMT_YUV444P9LE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
+    AV_PIX_FMT_YUV444P10BE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
+    AV_PIX_FMT_YUV444P10LE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
+    AV_PIX_FMT_YUV422P9BE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
+    AV_PIX_FMT_YUV422P9LE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
+    AV_PIX_FMT_GBRP,      ///< planar GBR 4:4:4 24bpp
+    AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP, // alias for #AV_PIX_FMT_GBRP
+    AV_PIX_FMT_GBRP9BE,   ///< planar GBR 4:4:4 27bpp, big-endian
+    AV_PIX_FMT_GBRP9LE,   ///< planar GBR 4:4:4 27bpp, little-endian
+    AV_PIX_FMT_GBRP10BE,  ///< planar GBR 4:4:4 30bpp, big-endian
+    AV_PIX_FMT_GBRP10LE,  ///< planar GBR 4:4:4 30bpp, little-endian
+    AV_PIX_FMT_GBRP16BE,  ///< planar GBR 4:4:4 48bpp, big-endian
+    AV_PIX_FMT_GBRP16LE,  ///< planar GBR 4:4:4 48bpp, little-endian
+    AV_PIX_FMT_YUVA422P,  ///< planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
+    AV_PIX_FMT_YUVA444P,  ///< planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
+    AV_PIX_FMT_YUVA420P9BE,  ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), big-endian
+    AV_PIX_FMT_YUVA420P9LE,  ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), little-endian
+    AV_PIX_FMT_YUVA422P9BE,  ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), big-endian
+    AV_PIX_FMT_YUVA422P9LE,  ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), little-endian
+    AV_PIX_FMT_YUVA444P9BE,  ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), big-endian
+    AV_PIX_FMT_YUVA444P9LE,  ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), little-endian
+    AV_PIX_FMT_YUVA420P10BE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian)
+    AV_PIX_FMT_YUVA420P10LE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian)
+    AV_PIX_FMT_YUVA422P10BE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian)
+    AV_PIX_FMT_YUVA422P10LE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian)
+    AV_PIX_FMT_YUVA444P10BE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian)
+    AV_PIX_FMT_YUVA444P10LE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian)
+    AV_PIX_FMT_YUVA420P16BE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian)
+    AV_PIX_FMT_YUVA420P16LE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian)
+    AV_PIX_FMT_YUVA422P16BE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian)
+    AV_PIX_FMT_YUVA422P16LE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian)
+    AV_PIX_FMT_YUVA444P16BE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian)
+    AV_PIX_FMT_YUVA444P16LE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian)
+
+    AV_PIX_FMT_VDPAU,     ///< HW acceleration through VDPAU, Picture.data[3] contains a VdpVideoSurface
+
+    AV_PIX_FMT_XYZ12LE,      ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as little-endian, the 4 lower bits are set to 0
+    AV_PIX_FMT_XYZ12BE,      ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as big-endian, the 4 lower bits are set to 0
+    AV_PIX_FMT_NV16,         ///< interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
+    AV_PIX_FMT_NV20LE,       ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
+    AV_PIX_FMT_NV20BE,       ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
+
+    AV_PIX_FMT_RGBA64BE,     ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian
+    AV_PIX_FMT_RGBA64LE,     ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian
+    AV_PIX_FMT_BGRA64BE,     ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian
+    AV_PIX_FMT_BGRA64LE,     ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian
+
+    AV_PIX_FMT_YVYU422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cr Y1 Cb
+
+    AV_PIX_FMT_YA16BE,       ///< 16 bits gray, 16 bits alpha (big-endian)
+    AV_PIX_FMT_YA16LE,       ///< 16 bits gray, 16 bits alpha (little-endian)
+
+    AV_PIX_FMT_GBRAP,        ///< planar GBRA 4:4:4:4 32bpp
+    AV_PIX_FMT_GBRAP16BE,    ///< planar GBRA 4:4:4:4 64bpp, big-endian
+    AV_PIX_FMT_GBRAP16LE,    ///< planar GBRA 4:4:4:4 64bpp, little-endian
+    /**
+     *  HW acceleration through QSV, data[3] contains a pointer to the
+     *  mfxFrameSurface1 structure.
+     */
+    AV_PIX_FMT_QSV,
+    /**
+     * HW acceleration though MMAL, data[3] contains a pointer to the
+     * MMAL_BUFFER_HEADER_T structure.
+     */
+    AV_PIX_FMT_MMAL,
+
+    AV_PIX_FMT_D3D11VA_VLD,  ///< HW decoding through Direct3D11 via old API, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer
+
+    /**
+     * HW acceleration through CUDA. data[i] contain CUdeviceptr pointers
+     * exactly as for system memory frames.
+     */
+    AV_PIX_FMT_CUDA,
+
+    AV_PIX_FMT_0RGB,        ///< packed RGB 8:8:8, 32bpp, XRGBXRGB...   X=unused/undefined
+    AV_PIX_FMT_RGB0,        ///< packed RGB 8:8:8, 32bpp, RGBXRGBX...   X=unused/undefined
+    AV_PIX_FMT_0BGR,        ///< packed BGR 8:8:8, 32bpp, XBGRXBGR...   X=unused/undefined
+    AV_PIX_FMT_BGR0,        ///< packed BGR 8:8:8, 32bpp, BGRXBGRX...   X=unused/undefined
+
+    AV_PIX_FMT_YUV420P12BE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
+    AV_PIX_FMT_YUV420P12LE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
+    AV_PIX_FMT_YUV420P14BE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
+    AV_PIX_FMT_YUV420P14LE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
+    AV_PIX_FMT_YUV422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
+    AV_PIX_FMT_YUV422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
+    AV_PIX_FMT_YUV422P14BE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
+    AV_PIX_FMT_YUV422P14LE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
+    AV_PIX_FMT_YUV444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
+    AV_PIX_FMT_YUV444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
+    AV_PIX_FMT_YUV444P14BE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
+    AV_PIX_FMT_YUV444P14LE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
+    AV_PIX_FMT_GBRP12BE,    ///< planar GBR 4:4:4 36bpp, big-endian
+    AV_PIX_FMT_GBRP12LE,    ///< planar GBR 4:4:4 36bpp, little-endian
+    AV_PIX_FMT_GBRP14BE,    ///< planar GBR 4:4:4 42bpp, big-endian
+    AV_PIX_FMT_GBRP14LE,    ///< planar GBR 4:4:4 42bpp, little-endian
+    AV_PIX_FMT_YUVJ411P,    ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV411P and setting color_range
+
+    AV_PIX_FMT_BAYER_BGGR8,    ///< bayer, BGBG..(odd line), GRGR..(even line), 8-bit samples */
+    AV_PIX_FMT_BAYER_RGGB8,    ///< bayer, RGRG..(odd line), GBGB..(even line), 8-bit samples */
+    AV_PIX_FMT_BAYER_GBRG8,    ///< bayer, GBGB..(odd line), RGRG..(even line), 8-bit samples */
+    AV_PIX_FMT_BAYER_GRBG8,    ///< bayer, GRGR..(odd line), BGBG..(even line), 8-bit samples */
+    AV_PIX_FMT_BAYER_BGGR16LE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, little-endian */
+    AV_PIX_FMT_BAYER_BGGR16BE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, big-endian */
+    AV_PIX_FMT_BAYER_RGGB16LE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, little-endian */
+    AV_PIX_FMT_BAYER_RGGB16BE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, big-endian */
+    AV_PIX_FMT_BAYER_GBRG16LE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, little-endian */
+    AV_PIX_FMT_BAYER_GBRG16BE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, big-endian */
+    AV_PIX_FMT_BAYER_GRBG16LE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, little-endian */
+    AV_PIX_FMT_BAYER_GRBG16BE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, big-endian */
+
+    AV_PIX_FMT_XVMC,///< XVideo Motion Acceleration via common packet passing
+
+    AV_PIX_FMT_YUV440P10LE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian
+    AV_PIX_FMT_YUV440P10BE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian
+    AV_PIX_FMT_YUV440P12LE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian
+    AV_PIX_FMT_YUV440P12BE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian
+    AV_PIX_FMT_AYUV64LE,    ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), little-endian
+    AV_PIX_FMT_AYUV64BE,    ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), big-endian
+
+    AV_PIX_FMT_VIDEOTOOLBOX, ///< hardware decoding through Videotoolbox
+
+    AV_PIX_FMT_P010LE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, little-endian
+    AV_PIX_FMT_P010BE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, big-endian
+
+    AV_PIX_FMT_GBRAP12BE,  ///< planar GBR 4:4:4:4 48bpp, big-endian
+    AV_PIX_FMT_GBRAP12LE,  ///< planar GBR 4:4:4:4 48bpp, little-endian
+
+    AV_PIX_FMT_GBRAP10BE,  ///< planar GBR 4:4:4:4 40bpp, big-endian
+    AV_PIX_FMT_GBRAP10LE,  ///< planar GBR 4:4:4:4 40bpp, little-endian
+
+    AV_PIX_FMT_MEDIACODEC, ///< hardware decoding through MediaCodec
+
+    AV_PIX_FMT_GRAY12BE,   ///<        Y        , 12bpp, big-endian
+    AV_PIX_FMT_GRAY12LE,   ///<        Y        , 12bpp, little-endian
+    AV_PIX_FMT_GRAY10BE,   ///<        Y        , 10bpp, big-endian
+    AV_PIX_FMT_GRAY10LE,   ///<        Y        , 10bpp, little-endian
+
+    AV_PIX_FMT_P016LE, ///< like NV12, with 16bpp per component, little-endian
+    AV_PIX_FMT_P016BE, ///< like NV12, with 16bpp per component, big-endian
+
+    /**
+     * Hardware surfaces for Direct3D11.
+     *
+     * This is preferred over the legacy AV_PIX_FMT_D3D11VA_VLD. The new D3D11
+     * hwaccel API and filtering support AV_PIX_FMT_D3D11 only.
+     *
+     * data[0] contains a ID3D11Texture2D pointer, and data[1] contains the
+     * texture array index of the frame as intptr_t if the ID3D11Texture2D is
+     * an array texture (or always 0 if it's a normal texture).
+     */
+    AV_PIX_FMT_D3D11,
+
+    AV_PIX_FMT_GRAY9BE,   ///<        Y        , 9bpp, big-endian
+    AV_PIX_FMT_GRAY9LE,   ///<        Y        , 9bpp, little-endian
+
+    AV_PIX_FMT_GBRPF32BE,  ///< IEEE-754 single precision planar GBR 4:4:4,     96bpp, big-endian
+    AV_PIX_FMT_GBRPF32LE,  ///< IEEE-754 single precision planar GBR 4:4:4,     96bpp, little-endian
+    AV_PIX_FMT_GBRAPF32BE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, big-endian
+    AV_PIX_FMT_GBRAPF32LE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, little-endian
+
+    /**
+     * DRM-managed buffers exposed through PRIME buffer sharing.
+     *
+     * data[0] points to an AVDRMFrameDescriptor.
+     */
+    AV_PIX_FMT_DRM_PRIME,
+    /**
+     * Hardware surfaces for OpenCL.
+     *
+     * data[i] contain 2D image objects (typed in C as cl_mem, used
+     * in OpenCL as image2d_t) for each plane of the surface.
+     */
+    AV_PIX_FMT_OPENCL,
+
+    AV_PIX_FMT_GRAY14BE,   ///<        Y        , 14bpp, big-endian
+    AV_PIX_FMT_GRAY14LE,   ///<        Y        , 14bpp, little-endian
+
+    AV_PIX_FMT_GRAYF32BE,  ///< IEEE-754 single precision Y, 32bpp, big-endian
+    AV_PIX_FMT_GRAYF32LE,  ///< IEEE-754 single precision Y, 32bpp, little-endian
+
+    AV_PIX_FMT_NB         ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
+};
+
+#if AV_HAVE_BIGENDIAN
+#   define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##be
+#else
+#   define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##le
+#endif
+
+#define AV_PIX_FMT_RGB32   AV_PIX_FMT_NE(ARGB, BGRA)
+#define AV_PIX_FMT_RGB32_1 AV_PIX_FMT_NE(RGBA, ABGR)
+#define AV_PIX_FMT_BGR32   AV_PIX_FMT_NE(ABGR, RGBA)
+#define AV_PIX_FMT_BGR32_1 AV_PIX_FMT_NE(BGRA, ARGB)
+#define AV_PIX_FMT_0RGB32  AV_PIX_FMT_NE(0RGB, BGR0)
+#define AV_PIX_FMT_0BGR32  AV_PIX_FMT_NE(0BGR, RGB0)
+
+#define AV_PIX_FMT_GRAY9  AV_PIX_FMT_NE(GRAY9BE,  GRAY9LE)
+#define AV_PIX_FMT_GRAY10 AV_PIX_FMT_NE(GRAY10BE, GRAY10LE)
+#define AV_PIX_FMT_GRAY12 AV_PIX_FMT_NE(GRAY12BE, GRAY12LE)
+#define AV_PIX_FMT_GRAY14 AV_PIX_FMT_NE(GRAY14BE, GRAY14LE)
+#define AV_PIX_FMT_GRAY16 AV_PIX_FMT_NE(GRAY16BE, GRAY16LE)
+#define AV_PIX_FMT_YA16   AV_PIX_FMT_NE(YA16BE,   YA16LE)
+#define AV_PIX_FMT_RGB48  AV_PIX_FMT_NE(RGB48BE,  RGB48LE)
+#define AV_PIX_FMT_RGB565 AV_PIX_FMT_NE(RGB565BE, RGB565LE)
+#define AV_PIX_FMT_RGB555 AV_PIX_FMT_NE(RGB555BE, RGB555LE)
+#define AV_PIX_FMT_RGB444 AV_PIX_FMT_NE(RGB444BE, RGB444LE)
+#define AV_PIX_FMT_RGBA64 AV_PIX_FMT_NE(RGBA64BE, RGBA64LE)
+#define AV_PIX_FMT_BGR48  AV_PIX_FMT_NE(BGR48BE,  BGR48LE)
+#define AV_PIX_FMT_BGR565 AV_PIX_FMT_NE(BGR565BE, BGR565LE)
+#define AV_PIX_FMT_BGR555 AV_PIX_FMT_NE(BGR555BE, BGR555LE)
+#define AV_PIX_FMT_BGR444 AV_PIX_FMT_NE(BGR444BE, BGR444LE)
+#define AV_PIX_FMT_BGRA64 AV_PIX_FMT_NE(BGRA64BE, BGRA64LE)
+
+#define AV_PIX_FMT_YUV420P9  AV_PIX_FMT_NE(YUV420P9BE , YUV420P9LE)
+#define AV_PIX_FMT_YUV422P9  AV_PIX_FMT_NE(YUV422P9BE , YUV422P9LE)
+#define AV_PIX_FMT_YUV444P9  AV_PIX_FMT_NE(YUV444P9BE , YUV444P9LE)
+#define AV_PIX_FMT_YUV420P10 AV_PIX_FMT_NE(YUV420P10BE, YUV420P10LE)
+#define AV_PIX_FMT_YUV422P10 AV_PIX_FMT_NE(YUV422P10BE, YUV422P10LE)
+#define AV_PIX_FMT_YUV440P10 AV_PIX_FMT_NE(YUV440P10BE, YUV440P10LE)
+#define AV_PIX_FMT_YUV444P10 AV_PIX_FMT_NE(YUV444P10BE, YUV444P10LE)
+#define AV_PIX_FMT_YUV420P12 AV_PIX_FMT_NE(YUV420P12BE, YUV420P12LE)
+#define AV_PIX_FMT_YUV422P12 AV_PIX_FMT_NE(YUV422P12BE, YUV422P12LE)
+#define AV_PIX_FMT_YUV440P12 AV_PIX_FMT_NE(YUV440P12BE, YUV440P12LE)
+#define AV_PIX_FMT_YUV444P12 AV_PIX_FMT_NE(YUV444P12BE, YUV444P12LE)
+#define AV_PIX_FMT_YUV420P14 AV_PIX_FMT_NE(YUV420P14BE, YUV420P14LE)
+#define AV_PIX_FMT_YUV422P14 AV_PIX_FMT_NE(YUV422P14BE, YUV422P14LE)
+#define AV_PIX_FMT_YUV444P14 AV_PIX_FMT_NE(YUV444P14BE, YUV444P14LE)
+#define AV_PIX_FMT_YUV420P16 AV_PIX_FMT_NE(YUV420P16BE, YUV420P16LE)
+#define AV_PIX_FMT_YUV422P16 AV_PIX_FMT_NE(YUV422P16BE, YUV422P16LE)
+#define AV_PIX_FMT_YUV444P16 AV_PIX_FMT_NE(YUV444P16BE, YUV444P16LE)
+
+#define AV_PIX_FMT_GBRP9     AV_PIX_FMT_NE(GBRP9BE ,    GBRP9LE)
+#define AV_PIX_FMT_GBRP10    AV_PIX_FMT_NE(GBRP10BE,    GBRP10LE)
+#define AV_PIX_FMT_GBRP12    AV_PIX_FMT_NE(GBRP12BE,    GBRP12LE)
+#define AV_PIX_FMT_GBRP14    AV_PIX_FMT_NE(GBRP14BE,    GBRP14LE)
+#define AV_PIX_FMT_GBRP16    AV_PIX_FMT_NE(GBRP16BE,    GBRP16LE)
+#define AV_PIX_FMT_GBRAP10   AV_PIX_FMT_NE(GBRAP10BE,   GBRAP10LE)
+#define AV_PIX_FMT_GBRAP12   AV_PIX_FMT_NE(GBRAP12BE,   GBRAP12LE)
+#define AV_PIX_FMT_GBRAP16   AV_PIX_FMT_NE(GBRAP16BE,   GBRAP16LE)
+
+#define AV_PIX_FMT_BAYER_BGGR16 AV_PIX_FMT_NE(BAYER_BGGR16BE,    BAYER_BGGR16LE)
+#define AV_PIX_FMT_BAYER_RGGB16 AV_PIX_FMT_NE(BAYER_RGGB16BE,    BAYER_RGGB16LE)
+#define AV_PIX_FMT_BAYER_GBRG16 AV_PIX_FMT_NE(BAYER_GBRG16BE,    BAYER_GBRG16LE)
+#define AV_PIX_FMT_BAYER_GRBG16 AV_PIX_FMT_NE(BAYER_GRBG16BE,    BAYER_GRBG16LE)
+
+#define AV_PIX_FMT_GBRPF32    AV_PIX_FMT_NE(GBRPF32BE,  GBRPF32LE)
+#define AV_PIX_FMT_GBRAPF32   AV_PIX_FMT_NE(GBRAPF32BE, GBRAPF32LE)
+
+#define AV_PIX_FMT_GRAYF32    AV_PIX_FMT_NE(GRAYF32BE, GRAYF32LE)
+
+#define AV_PIX_FMT_YUVA420P9  AV_PIX_FMT_NE(YUVA420P9BE , YUVA420P9LE)
+#define AV_PIX_FMT_YUVA422P9  AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE)
+#define AV_PIX_FMT_YUVA444P9  AV_PIX_FMT_NE(YUVA444P9BE , YUVA444P9LE)
+#define AV_PIX_FMT_YUVA420P10 AV_PIX_FMT_NE(YUVA420P10BE, YUVA420P10LE)
+#define AV_PIX_FMT_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE)
+#define AV_PIX_FMT_YUVA444P10 AV_PIX_FMT_NE(YUVA444P10BE, YUVA444P10LE)
+#define AV_PIX_FMT_YUVA420P16 AV_PIX_FMT_NE(YUVA420P16BE, YUVA420P16LE)
+#define AV_PIX_FMT_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE)
+#define AV_PIX_FMT_YUVA444P16 AV_PIX_FMT_NE(YUVA444P16BE, YUVA444P16LE)
+
+#define AV_PIX_FMT_XYZ12      AV_PIX_FMT_NE(XYZ12BE, XYZ12LE)
+#define AV_PIX_FMT_NV20       AV_PIX_FMT_NE(NV20BE,  NV20LE)
+#define AV_PIX_FMT_AYUV64     AV_PIX_FMT_NE(AYUV64BE, AYUV64LE)
+#define AV_PIX_FMT_P010       AV_PIX_FMT_NE(P010BE,  P010LE)
+#define AV_PIX_FMT_P016       AV_PIX_FMT_NE(P016BE,  P016LE)
+
+/**
+  * Chromaticity coordinates of the source primaries.
+  * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.1.
+  */
+enum AVColorPrimaries {
+    AVCOL_PRI_RESERVED0   = 0,
+    AVCOL_PRI_BT709       = 1,  ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B
+    AVCOL_PRI_UNSPECIFIED = 2,
+    AVCOL_PRI_RESERVED    = 3,
+    AVCOL_PRI_BT470M      = 4,  ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
+
+    AVCOL_PRI_BT470BG     = 5,  ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM
+    AVCOL_PRI_SMPTE170M   = 6,  ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC
+    AVCOL_PRI_SMPTE240M   = 7,  ///< functionally identical to above
+    AVCOL_PRI_FILM        = 8,  ///< colour filters using Illuminant C
+    AVCOL_PRI_BT2020      = 9,  ///< ITU-R BT2020
+    AVCOL_PRI_SMPTE428    = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ)
+    AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
+    AVCOL_PRI_SMPTE431    = 11, ///< SMPTE ST 431-2 (2011) / DCI P3
+    AVCOL_PRI_SMPTE432    = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3
+    AVCOL_PRI_JEDEC_P22   = 22, ///< JEDEC P22 phosphors
+    AVCOL_PRI_NB                ///< Not part of ABI
+};
+
+/**
+ * Color Transfer Characteristic.
+ * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.2.
+ */
+enum AVColorTransferCharacteristic {
+    AVCOL_TRC_RESERVED0    = 0,
+    AVCOL_TRC_BT709        = 1,  ///< also ITU-R BT1361
+    AVCOL_TRC_UNSPECIFIED  = 2,
+    AVCOL_TRC_RESERVED     = 3,
+    AVCOL_TRC_GAMMA22      = 4,  ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM
+    AVCOL_TRC_GAMMA28      = 5,  ///< also ITU-R BT470BG
+    AVCOL_TRC_SMPTE170M    = 6,  ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC
+    AVCOL_TRC_SMPTE240M    = 7,
+    AVCOL_TRC_LINEAR       = 8,  ///< "Linear transfer characteristics"
+    AVCOL_TRC_LOG          = 9,  ///< "Logarithmic transfer characteristic (100:1 range)"
+    AVCOL_TRC_LOG_SQRT     = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)"
+    AVCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4
+    AVCOL_TRC_BT1361_ECG   = 12, ///< ITU-R BT1361 Extended Colour Gamut
+    AVCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC)
+    AVCOL_TRC_BT2020_10    = 14, ///< ITU-R BT2020 for 10-bit system
+    AVCOL_TRC_BT2020_12    = 15, ///< ITU-R BT2020 for 12-bit system
+    AVCOL_TRC_SMPTE2084    = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems
+    AVCOL_TRC_SMPTEST2084  = AVCOL_TRC_SMPTE2084,
+    AVCOL_TRC_SMPTE428     = 17, ///< SMPTE ST 428-1
+    AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
+    AVCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma"
+    AVCOL_TRC_NB                 ///< Not part of ABI
+};
+
+/**
+ * YUV colorspace type.
+ * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.3.
+ */
+enum AVColorSpace {
+    AVCOL_SPC_RGB         = 0,  ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB)
+    AVCOL_SPC_BT709       = 1,  ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B
+    AVCOL_SPC_UNSPECIFIED = 2,
+    AVCOL_SPC_RESERVED    = 3,
+    AVCOL_SPC_FCC         = 4,  ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
+    AVCOL_SPC_BT470BG     = 5,  ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601
+    AVCOL_SPC_SMPTE170M   = 6,  ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC
+    AVCOL_SPC_SMPTE240M   = 7,  ///< functionally identical to above
+    AVCOL_SPC_YCGCO       = 8,  ///< Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16
+    AVCOL_SPC_YCOCG       = AVCOL_SPC_YCGCO,
+    AVCOL_SPC_BT2020_NCL  = 9,  ///< ITU-R BT2020 non-constant luminance system
+    AVCOL_SPC_BT2020_CL   = 10, ///< ITU-R BT2020 constant luminance system
+    AVCOL_SPC_SMPTE2085   = 11, ///< SMPTE 2085, Y'D'zD'x
+    AVCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system
+    AVCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system
+    AVCOL_SPC_ICTCP       = 14, ///< ITU-R BT.2100-0, ICtCp
+    AVCOL_SPC_NB                ///< Not part of ABI
+};
+
+/**
+ * MPEG vs JPEG YUV range.
+ */
+enum AVColorRange {
+    AVCOL_RANGE_UNSPECIFIED = 0,
+    AVCOL_RANGE_MPEG        = 1, ///< the normal 219*2^(n-8) "MPEG" YUV ranges
+    AVCOL_RANGE_JPEG        = 2, ///< the normal     2^n-1   "JPEG" YUV ranges
+    AVCOL_RANGE_NB               ///< Not part of ABI
+};
+
+/**
+ * Location of chroma samples.
+ *
+ * Illustration showing the location of the first (top left) chroma sample of the
+ * image, the left shows only luma, the right
+ * shows the location of the chroma sample, the 2 could be imagined to overlay
+ * each other but are drawn separately due to limitations of ASCII
+ *
+ *                1st 2nd       1st 2nd horizontal luma sample positions
+ *                 v   v         v   v
+ *                 ______        ______
+ *1st luma line > |X   X ...    |3 4 X ...     X are luma samples,
+ *                |             |1 2           1-6 are possible chroma positions
+ *2nd luma line > |X   X ...    |5 6 X ...     0 is undefined/unknown position
+ */
+enum AVChromaLocation {
+    AVCHROMA_LOC_UNSPECIFIED = 0,
+    AVCHROMA_LOC_LEFT        = 1, ///< MPEG-2/4 4:2:0, H.264 default for 4:2:0
+    AVCHROMA_LOC_CENTER      = 2, ///< MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0
+    AVCHROMA_LOC_TOPLEFT     = 3, ///< ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2
+    AVCHROMA_LOC_TOP         = 4,
+    AVCHROMA_LOC_BOTTOMLEFT  = 5,
+    AVCHROMA_LOC_BOTTOM      = 6,
+    AVCHROMA_LOC_NB               ///< Not part of ABI
+};
+
+#endif /* AVUTIL_PIXFMT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/random_seed.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/random_seed.h
new file mode 100644
index 0000000..0462a04
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/random_seed.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009 Baptiste Coudurier <baptiste.coudurier@gmail.com>
+ *
+ * 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 AVUTIL_RANDOM_SEED_H
+#define AVUTIL_RANDOM_SEED_H
+
+#include <stdint.h>
+/**
+ * @addtogroup lavu_crypto
+ * @{
+ */
+
+/**
+ * Get a seed to use in conjunction with random functions.
+ * This function tries to provide a good seed at a best effort bases.
+ * Its possible to call this function multiple times if more bits are needed.
+ * It can be quite slow, which is why it should only be used as seed for a faster
+ * PRNG. The quality of the seed depends on the platform.
+ */
+uint32_t av_get_random_seed(void);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_RANDOM_SEED_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rational.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rational.h
new file mode 100644
index 0000000..5c6b67b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rational.h
@@ -0,0 +1,214 @@
+/*
+ * rational numbers
+ * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_math_rational
+ * Utilties for rational number calculation.
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#ifndef AVUTIL_RATIONAL_H
+#define AVUTIL_RATIONAL_H
+
+#include <stdint.h>
+#include <limits.h>
+#include "attributes.h"
+
+/**
+ * @defgroup lavu_math_rational AVRational
+ * @ingroup lavu_math
+ * Rational number calculation.
+ *
+ * While rational numbers can be expressed as floating-point numbers, the
+ * conversion process is a lossy one, so are floating-point operations. On the
+ * other hand, the nature of FFmpeg demands highly accurate calculation of
+ * timestamps. This set of rational number utilities serves as a generic
+ * interface for manipulating rational numbers as pairs of numerators and
+ * denominators.
+ *
+ * Many of the functions that operate on AVRational's have the suffix `_q`, in
+ * reference to the mathematical symbol "ℚ" (Q) which denotes the set of all
+ * rational numbers.
+ *
+ * @{
+ */
+
+/**
+ * Rational number (pair of numerator and denominator).
+ */
+typedef struct AVRational{
+    int num; ///< Numerator
+    int den; ///< Denominator
+} AVRational;
+
+/**
+ * Create an AVRational.
+ *
+ * Useful for compilers that do not support compound literals.
+ *
+ * @note The return value is not reduced.
+ * @see av_reduce()
+ */
+static inline AVRational av_make_q(int num, int den)
+{
+    AVRational r = { num, den };
+    return r;
+}
+
+/**
+ * Compare two rationals.
+ *
+ * @param a First rational
+ * @param b Second rational
+ *
+ * @return One of the following values:
+ *         - 0 if `a == b`
+ *         - 1 if `a > b`
+ *         - -1 if `a < b`
+ *         - `INT_MIN` if one of the values is of the form `0 / 0`
+ */
+static inline int av_cmp_q(AVRational a, AVRational b){
+    const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den;
+
+    if(tmp) return (int)((tmp ^ a.den ^ b.den)>>63)|1;
+    else if(b.den && a.den) return 0;
+    else if(a.num && b.num) return (a.num>>31) - (b.num>>31);
+    else                    return INT_MIN;
+}
+
+/**
+ * Convert an AVRational to a `double`.
+ * @param a AVRational to convert
+ * @return `a` in floating-point form
+ * @see av_d2q()
+ */
+static inline double av_q2d(AVRational a){
+    return a.num / (double) a.den;
+}
+
+/**
+ * Reduce a fraction.
+ *
+ * This is useful for framerate calculations.
+ *
+ * @param[out] dst_num Destination numerator
+ * @param[out] dst_den Destination denominator
+ * @param[in]      num Source numerator
+ * @param[in]      den Source denominator
+ * @param[in]      max Maximum allowed values for `dst_num` & `dst_den`
+ * @return 1 if the operation is exact, 0 otherwise
+ */
+int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max);
+
+/**
+ * Multiply two rationals.
+ * @param b First rational
+ * @param c Second rational
+ * @return b*c
+ */
+AVRational av_mul_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Divide one rational by another.
+ * @param b First rational
+ * @param c Second rational
+ * @return b/c
+ */
+AVRational av_div_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Add two rationals.
+ * @param b First rational
+ * @param c Second rational
+ * @return b+c
+ */
+AVRational av_add_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Subtract one rational from another.
+ * @param b First rational
+ * @param c Second rational
+ * @return b-c
+ */
+AVRational av_sub_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Invert a rational.
+ * @param q value
+ * @return 1 / q
+ */
+static av_always_inline AVRational av_inv_q(AVRational q)
+{
+    AVRational r = { q.den, q.num };
+    return r;
+}
+
+/**
+ * Convert a double precision floating point number to a rational.
+ *
+ * In case of infinity, the returned value is expressed as `{1, 0}` or
+ * `{-1, 0}` depending on the sign.
+ *
+ * @param d   `double` to convert
+ * @param max Maximum allowed numerator and denominator
+ * @return `d` in AVRational form
+ * @see av_q2d()
+ */
+AVRational av_d2q(double d, int max) av_const;
+
+/**
+ * Find which of the two rationals is closer to another rational.
+ *
+ * @param q     Rational to be compared against
+ * @param q1,q2 Rationals to be tested
+ * @return One of the following values:
+ *         - 1 if `q1` is nearer to `q` than `q2`
+ *         - -1 if `q2` is nearer to `q` than `q1`
+ *         - 0 if they have the same distance
+ */
+int av_nearer_q(AVRational q, AVRational q1, AVRational q2);
+
+/**
+ * Find the value in a list of rationals nearest a given reference rational.
+ *
+ * @param q      Reference rational
+ * @param q_list Array of rationals terminated by `{0, 0}`
+ * @return Index of the nearest value found in the array
+ */
+int av_find_nearest_q_idx(AVRational q, const AVRational* q_list);
+
+/**
+ * Convert an AVRational to a IEEE 32-bit `float` expressed in fixed-point
+ * format.
+ *
+ * @param q Rational to be converted
+ * @return Equivalent floating-point value, expressed as an unsigned 32-bit
+ *         integer.
+ * @note The returned value is platform-indepedant.
+ */
+uint32_t av_q2intfloat(AVRational q);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_RATIONAL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rc4.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rc4.h
new file mode 100644
index 0000000..029cd2a
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/rc4.h
@@ -0,0 +1,66 @@
+/*
+ * RC4 encryption/decryption/pseudo-random number generator
+ *
+ * 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 AVUTIL_RC4_H
+#define AVUTIL_RC4_H
+
+#include <stdint.h>
+
+/**
+ * @defgroup lavu_rc4 RC4
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+typedef struct AVRC4 {
+    uint8_t state[256];
+    int x, y;
+} AVRC4;
+
+/**
+ * Allocate an AVRC4 context.
+ */
+AVRC4 *av_rc4_alloc(void);
+
+/**
+ * @brief Initializes an AVRC4 context.
+ *
+ * @param key_bits must be a multiple of 8
+ * @param decrypt 0 for encryption, 1 for decryption, currently has no effect
+ * @return zero on success, negative value otherwise
+ */
+int av_rc4_init(struct AVRC4 *d, const uint8_t *key, int key_bits, int decrypt);
+
+/**
+ * @brief Encrypts / decrypts using the RC4 algorithm.
+ *
+ * @param count number of bytes
+ * @param dst destination array, can be equal to src
+ * @param src source array, can be equal to dst, may be NULL
+ * @param iv not (yet) used for RC4, should be NULL
+ * @param decrypt 0 for encryption, 1 for decryption, not (yet) used
+ */
+void av_rc4_crypt(struct AVRC4 *d, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_RC4_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/replaygain.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/replaygain.h
new file mode 100644
index 0000000..b49bf1a
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/replaygain.h
@@ -0,0 +1,50 @@
+/*
+ * 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 AVUTIL_REPLAYGAIN_H
+#define AVUTIL_REPLAYGAIN_H
+
+#include <stdint.h>
+
+/**
+ * ReplayGain information (see
+ * http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification).
+ * The size of this struct is a part of the public ABI.
+ */
+typedef struct AVReplayGain {
+    /**
+     * Track replay gain in microbels (divide by 100000 to get the value in dB).
+     * Should be set to INT32_MIN when unknown.
+     */
+    int32_t track_gain;
+    /**
+     * Peak track amplitude, with 100000 representing full scale (but values
+     * may overflow). 0 when unknown.
+     */
+    uint32_t track_peak;
+    /**
+     * Same as track_gain, but for the whole album.
+     */
+    int32_t album_gain;
+    /**
+     * Same as track_peak, but for the whole album,
+     */
+    uint32_t album_peak;
+} AVReplayGain;
+
+#endif /* AVUTIL_REPLAYGAIN_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ripemd.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ripemd.h
new file mode 100644
index 0000000..0db6858
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/ripemd.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (C) 2013 James Almer <jamrial@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_ripemd
+ * Public header for RIPEMD hash function implementation.
+ */
+
+#ifndef AVUTIL_RIPEMD_H
+#define AVUTIL_RIPEMD_H
+
+#include <stdint.h>
+
+#include "attributes.h"
+#include "version.h"
+
+/**
+ * @defgroup lavu_ripemd RIPEMD
+ * @ingroup lavu_hash
+ * RIPEMD hash function implementation.
+ *
+ * @{
+ */
+
+extern const int av_ripemd_size;
+
+struct AVRIPEMD;
+
+/**
+ * Allocate an AVRIPEMD context.
+ */
+struct AVRIPEMD *av_ripemd_alloc(void);
+
+/**
+ * Initialize RIPEMD hashing.
+ *
+ * @param context pointer to the function context (of size av_ripemd_size)
+ * @param bits    number of bits in digest (128, 160, 256 or 320 bits)
+ * @return        zero if initialization succeeded, -1 otherwise
+ */
+int av_ripemd_init(struct AVRIPEMD* context, int bits);
+
+/**
+ * Update hash value.
+ *
+ * @param context hash function context
+ * @param data    input data to update hash with
+ * @param len     input data length
+ */
+#if FF_API_CRYPTO_SIZE_T
+void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, unsigned int len);
+#else
+void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, size_t len);
+#endif
+
+/**
+ * Finish hashing and output digest value.
+ *
+ * @param context hash function context
+ * @param digest  buffer where output digest value is stored
+ */
+void av_ripemd_final(struct AVRIPEMD* context, uint8_t *digest);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_RIPEMD_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/samplefmt.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/samplefmt.h
new file mode 100644
index 0000000..8cd43ae
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/samplefmt.h
@@ -0,0 +1,272 @@
+/*
+ * 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 AVUTIL_SAMPLEFMT_H
+#define AVUTIL_SAMPLEFMT_H
+
+#include <stdint.h>
+
+#include "avutil.h"
+#include "attributes.h"
+
+/**
+ * @addtogroup lavu_audio
+ * @{
+ *
+ * @defgroup lavu_sampfmts Audio sample formats
+ *
+ * Audio sample format enumeration and related convenience functions.
+ * @{
+ */
+
+/**
+ * Audio sample formats
+ *
+ * - The data described by the sample format is always in native-endian order.
+ *   Sample values can be expressed by native C types, hence the lack of a signed
+ *   24-bit sample format even though it is a common raw audio data format.
+ *
+ * - The floating-point formats are based on full volume being in the range
+ *   [-1.0, 1.0]. Any values outside this range are beyond full volume level.
+ *
+ * - The data layout as used in av_samples_fill_arrays() and elsewhere in FFmpeg
+ *   (such as AVFrame in libavcodec) is as follows:
+ *
+ * @par
+ * For planar sample formats, each audio channel is in a separate data plane,
+ * and linesize is the buffer size, in bytes, for a single plane. All data
+ * planes must be the same size. For packed sample formats, only the first data
+ * plane is used, and samples for each channel are interleaved. In this case,
+ * linesize is the buffer size, in bytes, for the 1 plane.
+ *
+ */
+enum AVSampleFormat {
+    AV_SAMPLE_FMT_NONE = -1,
+    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
+    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
+    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
+    AV_SAMPLE_FMT_FLT,         ///< float
+    AV_SAMPLE_FMT_DBL,         ///< double
+
+    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
+    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
+    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
+    AV_SAMPLE_FMT_FLTP,        ///< float, planar
+    AV_SAMPLE_FMT_DBLP,        ///< double, planar
+    AV_SAMPLE_FMT_S64,         ///< signed 64 bits
+    AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar
+
+    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
+};
+
+/**
+ * Return the name of sample_fmt, or NULL if sample_fmt is not
+ * recognized.
+ */
+const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt);
+
+/**
+ * Return a sample format corresponding to name, or AV_SAMPLE_FMT_NONE
+ * on error.
+ */
+enum AVSampleFormat av_get_sample_fmt(const char *name);
+
+/**
+ * Return the planar<->packed alternative form of the given sample format, or
+ * AV_SAMPLE_FMT_NONE on error. If the passed sample_fmt is already in the
+ * requested planar/packed format, the format returned is the same as the
+ * input.
+ */
+enum AVSampleFormat av_get_alt_sample_fmt(enum AVSampleFormat sample_fmt, int planar);
+
+/**
+ * Get the packed alternative form of the given sample format.
+ *
+ * If the passed sample_fmt is already in packed format, the format returned is
+ * the same as the input.
+ *
+ * @return  the packed alternative form of the given sample format or
+            AV_SAMPLE_FMT_NONE on error.
+ */
+enum AVSampleFormat av_get_packed_sample_fmt(enum AVSampleFormat sample_fmt);
+
+/**
+ * Get the planar alternative form of the given sample format.
+ *
+ * If the passed sample_fmt is already in planar format, the format returned is
+ * the same as the input.
+ *
+ * @return  the planar alternative form of the given sample format or
+            AV_SAMPLE_FMT_NONE on error.
+ */
+enum AVSampleFormat av_get_planar_sample_fmt(enum AVSampleFormat sample_fmt);
+
+/**
+ * Generate a string corresponding to the sample format with
+ * sample_fmt, or a header if sample_fmt is negative.
+ *
+ * @param buf the buffer where to write the string
+ * @param buf_size the size of buf
+ * @param sample_fmt the number of the sample format to print the
+ * corresponding info string, or a negative value to print the
+ * corresponding header.
+ * @return the pointer to the filled buffer or NULL if sample_fmt is
+ * unknown or in case of other errors
+ */
+char *av_get_sample_fmt_string(char *buf, int buf_size, enum AVSampleFormat sample_fmt);
+
+/**
+ * Return number of bytes per sample.
+ *
+ * @param sample_fmt the sample format
+ * @return number of bytes per sample or zero if unknown for the given
+ * sample format
+ */
+int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt);
+
+/**
+ * Check if the sample format is planar.
+ *
+ * @param sample_fmt the sample format to inspect
+ * @return 1 if the sample format is planar, 0 if it is interleaved
+ */
+int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt);
+
+/**
+ * Get the required buffer size for the given audio parameters.
+ *
+ * @param[out] linesize calculated linesize, may be NULL
+ * @param nb_channels   the number of channels
+ * @param nb_samples    the number of samples in a single channel
+ * @param sample_fmt    the sample format
+ * @param align         buffer size alignment (0 = default, 1 = no alignment)
+ * @return              required buffer size, or negative error code on failure
+ */
+int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
+                               enum AVSampleFormat sample_fmt, int align);
+
+/**
+ * @}
+ *
+ * @defgroup lavu_sampmanip Samples manipulation
+ *
+ * Functions that manipulate audio samples
+ * @{
+ */
+
+/**
+ * Fill plane data pointers and linesize for samples with sample
+ * format sample_fmt.
+ *
+ * The audio_data array is filled with the pointers to the samples data planes:
+ * for planar, set the start point of each channel's data within the buffer,
+ * for packed, set the start point of the entire buffer only.
+ *
+ * The value pointed to by linesize is set to the aligned size of each
+ * channel's data buffer for planar layout, or to the aligned size of the
+ * buffer for all channels for packed layout.
+ *
+ * The buffer in buf must be big enough to contain all the samples
+ * (use av_samples_get_buffer_size() to compute its minimum size),
+ * otherwise the audio_data pointers will point to invalid data.
+ *
+ * @see enum AVSampleFormat
+ * The documentation for AVSampleFormat describes the data layout.
+ *
+ * @param[out] audio_data  array to be filled with the pointer for each channel
+ * @param[out] linesize    calculated linesize, may be NULL
+ * @param buf              the pointer to a buffer containing the samples
+ * @param nb_channels      the number of channels
+ * @param nb_samples       the number of samples in a single channel
+ * @param sample_fmt       the sample format
+ * @param align            buffer size alignment (0 = default, 1 = no alignment)
+ * @return                 >=0 on success or a negative error code on failure
+ * @todo return minimum size in bytes required for the buffer in case
+ * of success at the next bump
+ */
+int av_samples_fill_arrays(uint8_t **audio_data, int *linesize,
+                           const uint8_t *buf,
+                           int nb_channels, int nb_samples,
+                           enum AVSampleFormat sample_fmt, int align);
+
+/**
+ * Allocate a samples buffer for nb_samples samples, and fill data pointers and
+ * linesize accordingly.
+ * The allocated samples buffer can be freed by using av_freep(&audio_data[0])
+ * Allocated data will be initialized to silence.
+ *
+ * @see enum AVSampleFormat
+ * The documentation for AVSampleFormat describes the data layout.
+ *
+ * @param[out] audio_data  array to be filled with the pointer for each channel
+ * @param[out] linesize    aligned size for audio buffer(s), may be NULL
+ * @param nb_channels      number of audio channels
+ * @param nb_samples       number of samples per channel
+ * @param align            buffer size alignment (0 = default, 1 = no alignment)
+ * @return                 >=0 on success or a negative error code on failure
+ * @todo return the size of the allocated buffer in case of success at the next bump
+ * @see av_samples_fill_arrays()
+ * @see av_samples_alloc_array_and_samples()
+ */
+int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels,
+                     int nb_samples, enum AVSampleFormat sample_fmt, int align);
+
+/**
+ * Allocate a data pointers array, samples buffer for nb_samples
+ * samples, and fill data pointers and linesize accordingly.
+ *
+ * This is the same as av_samples_alloc(), but also allocates the data
+ * pointers array.
+ *
+ * @see av_samples_alloc()
+ */
+int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels,
+                                       int nb_samples, enum AVSampleFormat sample_fmt, int align);
+
+/**
+ * Copy samples from src to dst.
+ *
+ * @param dst destination array of pointers to data planes
+ * @param src source array of pointers to data planes
+ * @param dst_offset offset in samples at which the data will be written to dst
+ * @param src_offset offset in samples at which the data will be read from src
+ * @param nb_samples number of samples to be copied
+ * @param nb_channels number of audio channels
+ * @param sample_fmt audio sample format
+ */
+int av_samples_copy(uint8_t **dst, uint8_t * const *src, int dst_offset,
+                    int src_offset, int nb_samples, int nb_channels,
+                    enum AVSampleFormat sample_fmt);
+
+/**
+ * Fill an audio buffer with silence.
+ *
+ * @param audio_data  array of pointers to data planes
+ * @param offset      offset in samples at which to start filling
+ * @param nb_samples  number of samples to fill
+ * @param nb_channels number of audio channels
+ * @param sample_fmt  audio sample format
+ */
+int av_samples_set_silence(uint8_t **audio_data, int offset, int nb_samples,
+                           int nb_channels, enum AVSampleFormat sample_fmt);
+
+/**
+ * @}
+ * @}
+ */
+#endif /* AVUTIL_SAMPLEFMT_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha.h
new file mode 100644
index 0000000..c0180e5
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2007 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_sha
+ * Public header for SHA-1 & SHA-256 hash function implementations.
+ */
+
+#ifndef AVUTIL_SHA_H
+#define AVUTIL_SHA_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "attributes.h"
+#include "version.h"
+
+/**
+ * @defgroup lavu_sha SHA
+ * @ingroup lavu_hash
+ * SHA-1 and SHA-256 (Secure Hash Algorithm) hash function implementations.
+ *
+ * This module supports the following SHA hash functions:
+ *
+ * - SHA-1: 160 bits
+ * - SHA-224: 224 bits, as a variant of SHA-2
+ * - SHA-256: 256 bits, as a variant of SHA-2
+ *
+ * @see For SHA-384, SHA-512, and variants thereof, see @ref lavu_sha512.
+ *
+ * @{
+ */
+
+extern const int av_sha_size;
+
+struct AVSHA;
+
+/**
+ * Allocate an AVSHA context.
+ */
+struct AVSHA *av_sha_alloc(void);
+
+/**
+ * Initialize SHA-1 or SHA-2 hashing.
+ *
+ * @param context pointer to the function context (of size av_sha_size)
+ * @param bits    number of bits in digest (SHA-1 - 160 bits, SHA-2 224 or 256 bits)
+ * @return        zero if initialization succeeded, -1 otherwise
+ */
+int av_sha_init(struct AVSHA* context, int bits);
+
+/**
+ * Update hash value.
+ *
+ * @param ctx     hash function context
+ * @param data    input data to update hash with
+ * @param len     input data length
+ */
+#if FF_API_CRYPTO_SIZE_T
+void av_sha_update(struct AVSHA *ctx, const uint8_t *data, unsigned int len);
+#else
+void av_sha_update(struct AVSHA *ctx, const uint8_t *data, size_t len);
+#endif
+
+/**
+ * Finish hashing and output digest value.
+ *
+ * @param context hash function context
+ * @param digest  buffer where output digest value is stored
+ */
+void av_sha_final(struct AVSHA* context, uint8_t *digest);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_SHA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha512.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha512.h
new file mode 100644
index 0000000..bef714b
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/sha512.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2007 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (C) 2013 James Almer <jamrial@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu_sha512
+ * Public header for SHA-512 implementation.
+ */
+
+#ifndef AVUTIL_SHA512_H
+#define AVUTIL_SHA512_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "attributes.h"
+#include "version.h"
+
+/**
+ * @defgroup lavu_sha512 SHA-512
+ * @ingroup lavu_hash
+ * SHA-512 (Secure Hash Algorithm) hash function implementations.
+ *
+ * This module supports the following SHA-2 hash functions:
+ *
+ * - SHA-512/224: 224 bits
+ * - SHA-512/256: 256 bits
+ * - SHA-384: 384 bits
+ * - SHA-512: 512 bits
+ *
+ * @see For SHA-1, SHA-256, and variants thereof, see @ref lavu_sha.
+ *
+ * @{
+ */
+
+extern const int av_sha512_size;
+
+struct AVSHA512;
+
+/**
+ * Allocate an AVSHA512 context.
+ */
+struct AVSHA512 *av_sha512_alloc(void);
+
+/**
+ * Initialize SHA-2 512 hashing.
+ *
+ * @param context pointer to the function context (of size av_sha512_size)
+ * @param bits    number of bits in digest (224, 256, 384 or 512 bits)
+ * @return        zero if initialization succeeded, -1 otherwise
+ */
+int av_sha512_init(struct AVSHA512* context, int bits);
+
+/**
+ * Update hash value.
+ *
+ * @param context hash function context
+ * @param data    input data to update hash with
+ * @param len     input data length
+ */
+#if FF_API_CRYPTO_SIZE_T
+void av_sha512_update(struct AVSHA512* context, const uint8_t* data, unsigned int len);
+#else
+void av_sha512_update(struct AVSHA512* context, const uint8_t* data, size_t len);
+#endif
+
+/**
+ * Finish hashing and output digest value.
+ *
+ * @param context hash function context
+ * @param digest  buffer where output digest value is stored
+ */
+void av_sha512_final(struct AVSHA512* context, uint8_t *digest);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_SHA512_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/spherical.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/spherical.h
new file mode 100644
index 0000000..cef759c
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/spherical.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2016 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Spherical video
+ */
+
+#ifndef AVUTIL_SPHERICAL_H
+#define AVUTIL_SPHERICAL_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * @addtogroup lavu_video
+ * @{
+ *
+ * @defgroup lavu_video_spherical Spherical video mapping
+ * @{
+ */
+
+/**
+ * @addtogroup lavu_video_spherical
+ * A spherical video file contains surfaces that need to be mapped onto a
+ * sphere. Depending on how the frame was converted, a different distortion
+ * transformation or surface recomposition function needs to be applied before
+ * the video should be mapped and displayed.
+ */
+
+/**
+ * Projection of the video surface(s) on a sphere.
+ */
+enum AVSphericalProjection {
+    /**
+     * Video represents a sphere mapped on a flat surface using
+     * equirectangular projection.
+     */
+    AV_SPHERICAL_EQUIRECTANGULAR,
+
+    /**
+     * Video frame is split into 6 faces of a cube, and arranged on a
+     * 3x2 layout. Faces are oriented upwards for the front, left, right,
+     * and back faces. The up face is oriented so the top of the face is
+     * forwards and the down face is oriented so the top of the face is
+     * to the back.
+     */
+    AV_SPHERICAL_CUBEMAP,
+
+    /**
+     * Video represents a portion of a sphere mapped on a flat surface
+     * using equirectangular projection. The @ref bounding fields indicate
+     * the position of the current video in a larger surface.
+     */
+    AV_SPHERICAL_EQUIRECTANGULAR_TILE,
+};
+
+/**
+ * This structure describes how to handle spherical videos, outlining
+ * information about projection, initial layout, and any other view modifier.
+ *
+ * @note The struct must be allocated with av_spherical_alloc() and
+ *       its size is not a part of the public ABI.
+ */
+typedef struct AVSphericalMapping {
+    /**
+     * Projection type.
+     */
+    enum AVSphericalProjection projection;
+
+    /**
+     * @name Initial orientation
+     * @{
+     * There fields describe additional rotations applied to the sphere after
+     * the video frame is mapped onto it. The sphere is rotated around the
+     * viewer, who remains stationary. The order of transformation is always
+     * yaw, followed by pitch, and finally by roll.
+     *
+     * The coordinate system matches the one defined in OpenGL, where the
+     * forward vector (z) is coming out of screen, and it is equivalent to
+     * a rotation matrix of R = r_y(yaw) * r_x(pitch) * r_z(roll).
+     *
+     * A positive yaw rotates the portion of the sphere in front of the viewer
+     * toward their right. A positive pitch rotates the portion of the sphere
+     * in front of the viewer upwards. A positive roll tilts the portion of
+     * the sphere in front of the viewer to the viewer's right.
+     *
+     * These values are exported as 16.16 fixed point.
+     *
+     * See this equirectangular projection as example:
+     *
+     * @code{.unparsed}
+     *                   Yaw
+     *     -180           0           180
+     *   90 +-------------+-------------+  180
+     *      |             |             |                  up
+     * P    |             |             |                 y|    forward
+     * i    |             ^             |                  |   /z
+     * t  0 +-------------X-------------+    0 Roll        |  /
+     * c    |             |             |                  | /
+     * h    |             |             |                 0|/_____right
+     *      |             |             |                        x
+     *  -90 +-------------+-------------+ -180
+     *
+     * X - the default camera center
+     * ^ - the default up vector
+     * @endcode
+     */
+    int32_t yaw;   ///< Rotation around the up vector [-180, 180].
+    int32_t pitch; ///< Rotation around the right vector [-90, 90].
+    int32_t roll;  ///< Rotation around the forward vector [-180, 180].
+    /**
+     * @}
+     */
+
+    /**
+     * @name Bounding rectangle
+     * @anchor bounding
+     * @{
+     * These fields indicate the location of the current tile, and where
+     * it should be mapped relative to the original surface. They are
+     * exported as 0.32 fixed point, and can be converted to classic
+     * pixel values with av_spherical_bounds().
+     *
+     * @code{.unparsed}
+     *      +----------------+----------+
+     *      |                |bound_top |
+     *      |            +--------+     |
+     *      | bound_left |tile    |     |
+     *      +<---------->|        |<--->+bound_right
+     *      |            +--------+     |
+     *      |                |          |
+     *      |    bound_bottom|          |
+     *      +----------------+----------+
+     * @endcode
+     *
+     * If needed, the original video surface dimensions can be derived
+     * by adding the current stream or frame size to the related bounds,
+     * like in the following example:
+     *
+     * @code{c}
+     *     original_width  = tile->width  + bound_left + bound_right;
+     *     original_height = tile->height + bound_top  + bound_bottom;
+     * @endcode
+     *
+     * @note These values are valid only for the tiled equirectangular
+     *       projection type (@ref AV_SPHERICAL_EQUIRECTANGULAR_TILE),
+     *       and should be ignored in all other cases.
+     */
+    uint32_t bound_left;   ///< Distance from the left edge
+    uint32_t bound_top;    ///< Distance from the top edge
+    uint32_t bound_right;  ///< Distance from the right edge
+    uint32_t bound_bottom; ///< Distance from the bottom edge
+    /**
+     * @}
+     */
+
+    /**
+     * Number of pixels to pad from the edge of each cube face.
+     *
+     * @note This value is valid for only for the cubemap projection type
+     *       (@ref AV_SPHERICAL_CUBEMAP), and should be ignored in all other
+     *       cases.
+     */
+    uint32_t padding;
+} AVSphericalMapping;
+
+/**
+ * Allocate a AVSphericalVideo structure and initialize its fields to default
+ * values.
+ *
+ * @return the newly allocated struct or NULL on failure
+ */
+AVSphericalMapping *av_spherical_alloc(size_t *size);
+
+/**
+ * Convert the @ref bounding fields from an AVSphericalVideo
+ * from 0.32 fixed point to pixels.
+ *
+ * @param map    The AVSphericalVideo map to read bound values from.
+ * @param width  Width of the current frame or stream.
+ * @param height Height of the current frame or stream.
+ * @param left   Pixels from the left edge.
+ * @param top    Pixels from the top edge.
+ * @param right  Pixels from the right edge.
+ * @param bottom Pixels from the bottom edge.
+ */
+void av_spherical_tile_bounds(const AVSphericalMapping *map,
+                              size_t width, size_t height,
+                              size_t *left, size_t *top,
+                              size_t *right, size_t *bottom);
+
+/**
+ * Provide a human-readable name of a given AVSphericalProjection.
+ *
+ * @param projection The input AVSphericalProjection.
+ *
+ * @return The name of the AVSphericalProjection, or "unknown".
+ */
+const char *av_spherical_projection_name(enum AVSphericalProjection projection);
+
+/**
+ * Get the AVSphericalProjection form a human-readable name.
+ *
+ * @param name The input string.
+ *
+ * @return The AVSphericalProjection value, or -1 if not found.
+ */
+int av_spherical_from_name(const char *name);
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_SPHERICAL_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/stereo3d.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/stereo3d.h
new file mode 100644
index 0000000..d421aac
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/stereo3d.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2013 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Stereoscopic video
+ */
+
+#ifndef AVUTIL_STEREO3D_H
+#define AVUTIL_STEREO3D_H
+
+#include <stdint.h>
+
+#include "frame.h"
+
+/**
+ * @addtogroup lavu_video
+ * @{
+ *
+ * @defgroup lavu_video_stereo3d Stereo3D types and functions
+ * @{
+ */
+
+/**
+ * @addtogroup lavu_video_stereo3d
+ * A stereoscopic video file consists in multiple views embedded in a single
+ * frame, usually describing two views of a scene. This file describes all
+ * possible codec-independent view arrangements.
+ * */
+
+/**
+ * List of possible 3D Types
+ */
+enum AVStereo3DType {
+    /**
+     * Video is not stereoscopic (and metadata has to be there).
+     */
+    AV_STEREO3D_2D,
+
+    /**
+     * Views are next to each other.
+     *
+     * @code{.unparsed}
+     *    LLLLRRRR
+     *    LLLLRRRR
+     *    LLLLRRRR
+     *    ...
+     * @endcode
+     */
+    AV_STEREO3D_SIDEBYSIDE,
+
+    /**
+     * Views are on top of each other.
+     *
+     * @code{.unparsed}
+     *    LLLLLLLL
+     *    LLLLLLLL
+     *    RRRRRRRR
+     *    RRRRRRRR
+     * @endcode
+     */
+    AV_STEREO3D_TOPBOTTOM,
+
+    /**
+     * Views are alternated temporally.
+     *
+     * @code{.unparsed}
+     *     frame0   frame1   frame2   ...
+     *    LLLLLLLL RRRRRRRR LLLLLLLL
+     *    LLLLLLLL RRRRRRRR LLLLLLLL
+     *    LLLLLLLL RRRRRRRR LLLLLLLL
+     *    ...      ...      ...
+     * @endcode
+     */
+    AV_STEREO3D_FRAMESEQUENCE,
+
+    /**
+     * Views are packed in a checkerboard-like structure per pixel.
+     *
+     * @code{.unparsed}
+     *    LRLRLRLR
+     *    RLRLRLRL
+     *    LRLRLRLR
+     *    ...
+     * @endcode
+     */
+    AV_STEREO3D_CHECKERBOARD,
+
+    /**
+     * Views are next to each other, but when upscaling
+     * apply a checkerboard pattern.
+     *
+     * @code{.unparsed}
+     *     LLLLRRRR          L L L L    R R R R
+     *     LLLLRRRR    =>     L L L L  R R R R
+     *     LLLLRRRR          L L L L    R R R R
+     *     LLLLRRRR           L L L L  R R R R
+     * @endcode
+     */
+    AV_STEREO3D_SIDEBYSIDE_QUINCUNX,
+
+    /**
+     * Views are packed per line, as if interlaced.
+     *
+     * @code{.unparsed}
+     *    LLLLLLLL
+     *    RRRRRRRR
+     *    LLLLLLLL
+     *    ...
+     * @endcode
+     */
+    AV_STEREO3D_LINES,
+
+    /**
+     * Views are packed per column.
+     *
+     * @code{.unparsed}
+     *    LRLRLRLR
+     *    LRLRLRLR
+     *    LRLRLRLR
+     *    ...
+     * @endcode
+     */
+    AV_STEREO3D_COLUMNS,
+};
+
+/**
+ * List of possible view types.
+ */
+enum AVStereo3DView {
+    /**
+     * Frame contains two packed views.
+     */
+    AV_STEREO3D_VIEW_PACKED,
+
+    /**
+     * Frame contains only the left view.
+     */
+    AV_STEREO3D_VIEW_LEFT,
+
+    /**
+     * Frame contains only the right view.
+     */
+    AV_STEREO3D_VIEW_RIGHT,
+};
+
+/**
+ * Inverted views, Right/Bottom represents the left view.
+ */
+#define AV_STEREO3D_FLAG_INVERT     (1 << 0)
+
+/**
+ * Stereo 3D type: this structure describes how two videos are packed
+ * within a single video surface, with additional information as needed.
+ *
+ * @note The struct must be allocated with av_stereo3d_alloc() and
+ *       its size is not a part of the public ABI.
+ */
+typedef struct AVStereo3D {
+    /**
+     * How views are packed within the video.
+     */
+    enum AVStereo3DType type;
+
+    /**
+     * Additional information about the frame packing.
+     */
+    int flags;
+
+    /**
+     * Determines which views are packed.
+     */
+    enum AVStereo3DView view;
+} AVStereo3D;
+
+/**
+ * Allocate an AVStereo3D structure and set its fields to default values.
+ * The resulting struct can be freed using av_freep().
+ *
+ * @return An AVStereo3D filled with default values or NULL on failure.
+ */
+AVStereo3D *av_stereo3d_alloc(void);
+
+/**
+ * Allocate a complete AVFrameSideData and add it to the frame.
+ *
+ * @param frame The frame which side data is added to.
+ *
+ * @return The AVStereo3D structure to be filled by caller.
+ */
+AVStereo3D *av_stereo3d_create_side_data(AVFrame *frame);
+
+/**
+ * Provide a human-readable name of a given stereo3d type.
+ *
+ * @param type The input stereo3d type value.
+ *
+ * @return The name of the stereo3d value, or "unknown".
+ */
+const char *av_stereo3d_type_name(unsigned int type);
+
+/**
+ * Get the AVStereo3DType form a human-readable name.
+ *
+ * @param name The input string.
+ *
+ * @return The AVStereo3DType value, or -1 if not found.
+ */
+int av_stereo3d_from_name(const char *name);
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_STEREO3D_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tea.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tea.h
new file mode 100644
index 0000000..dd929bd
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tea.h
@@ -0,0 +1,71 @@
+/*
+ * A 32-bit implementation of the TEA algorithm
+ * Copyright (c) 2015 Vesselin Bontchev
+ *
+ * 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 AVUTIL_TEA_H
+#define AVUTIL_TEA_H
+
+#include <stdint.h>
+
+/**
+ * @file
+ * @brief Public header for libavutil TEA algorithm
+ * @defgroup lavu_tea TEA
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+extern const int av_tea_size;
+
+struct AVTEA;
+
+/**
+  * Allocate an AVTEA context
+  * To free the struct: av_free(ptr)
+  */
+struct AVTEA *av_tea_alloc(void);
+
+/**
+ * Initialize an AVTEA context.
+ *
+ * @param ctx an AVTEA context
+ * @param key a key of 16 bytes used for encryption/decryption
+ * @param rounds the number of rounds in TEA (64 is the "standard")
+ */
+void av_tea_init(struct AVTEA *ctx, const uint8_t key[16], int rounds);
+
+/**
+ * Encrypt or decrypt a buffer using a previously initialized context.
+ *
+ * @param ctx an AVTEA context
+ * @param dst destination array, can be equal to src
+ * @param src source array, can be equal to dst
+ * @param count number of 8 byte blocks
+ * @param iv initialization vector for CBC mode, if NULL then ECB will be used
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_tea_crypt(struct AVTEA *ctx, uint8_t *dst, const uint8_t *src,
+                  int count, uint8_t *iv, int decrypt);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_TEA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/threadmessage.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/threadmessage.h
new file mode 100644
index 0000000..42ce655
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/threadmessage.h
@@ -0,0 +1,115 @@
+/*
+ * 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 AVUTIL_THREADMESSAGE_H
+#define AVUTIL_THREADMESSAGE_H
+
+typedef struct AVThreadMessageQueue AVThreadMessageQueue;
+
+typedef enum AVThreadMessageFlags {
+
+    /**
+     * Perform non-blocking operation.
+     * If this flag is set, send and recv operations are non-blocking and
+     * return AVERROR(EAGAIN) immediately if they can not proceed.
+     */
+    AV_THREAD_MESSAGE_NONBLOCK = 1,
+
+} AVThreadMessageFlags;
+
+/**
+ * Allocate a new message queue.
+ *
+ * @param mq      pointer to the message queue
+ * @param nelem   maximum number of elements in the queue
+ * @param elsize  size of each element in the queue
+ * @return  >=0 for success; <0 for error, in particular AVERROR(ENOSYS) if
+ *          lavu was built without thread support
+ */
+int av_thread_message_queue_alloc(AVThreadMessageQueue **mq,
+                                  unsigned nelem,
+                                  unsigned elsize);
+
+/**
+ * Free a message queue.
+ *
+ * The message queue must no longer be in use by another thread.
+ */
+void av_thread_message_queue_free(AVThreadMessageQueue **mq);
+
+/**
+ * Send a message on the queue.
+ */
+int av_thread_message_queue_send(AVThreadMessageQueue *mq,
+                                 void *msg,
+                                 unsigned flags);
+
+/**
+ * Receive a message from the queue.
+ */
+int av_thread_message_queue_recv(AVThreadMessageQueue *mq,
+                                 void *msg,
+                                 unsigned flags);
+
+/**
+ * Set the sending error code.
+ *
+ * If the error code is set to non-zero, av_thread_message_queue_send() will
+ * return it immediately. Conventional values, such as AVERROR_EOF or
+ * AVERROR(EAGAIN), can be used to cause the sending thread to stop or
+ * suspend its operation.
+ */
+void av_thread_message_queue_set_err_send(AVThreadMessageQueue *mq,
+                                          int err);
+
+/**
+ * Set the receiving error code.
+ *
+ * If the error code is set to non-zero, av_thread_message_queue_recv() will
+ * return it immediately when there are no longer available messages.
+ * Conventional values, such as AVERROR_EOF or AVERROR(EAGAIN), can be used
+ * to cause the receiving thread to stop or suspend its operation.
+ */
+void av_thread_message_queue_set_err_recv(AVThreadMessageQueue *mq,
+                                          int err);
+
+/**
+ * Set the optional free message callback function which will be called if an
+ * operation is removing messages from the queue.
+ */
+void av_thread_message_queue_set_free_func(AVThreadMessageQueue *mq,
+                                           void (*free_func)(void *msg));
+
+/**
+ * Return the current number of messages in the queue.
+ *
+ * @return the current number of messages or AVERROR(ENOSYS) if lavu was built
+ *         without thread support
+ */
+int av_thread_message_queue_nb_elems(AVThreadMessageQueue *mq);
+
+/**
+ * Flush the message queue
+ *
+ * This function is mostly equivalent to reading and free-ing every message
+ * except that it will be done in a single operation (no lock/unlock between
+ * reads).
+ */
+void av_thread_message_flush(AVThreadMessageQueue *mq);
+
+#endif /* AVUTIL_THREADMESSAGE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/time.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/time.h
new file mode 100644
index 0000000..dc169b0
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/time.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2000-2003 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 AVUTIL_TIME_H
+#define AVUTIL_TIME_H
+
+#include <stdint.h>
+
+/**
+ * Get the current time in microseconds.
+ */
+int64_t av_gettime(void);
+
+/**
+ * Get the current time in microseconds since some unspecified starting point.
+ * On platforms that support it, the time comes from a monotonic clock
+ * This property makes this time source ideal for measuring relative time.
+ * The returned values may not be monotonic on platforms where a monotonic
+ * clock is not available.
+ */
+int64_t av_gettime_relative(void);
+
+/**
+ * Indicates with a boolean result if the av_gettime_relative() time source
+ * is monotonic.
+ */
+int av_gettime_relative_is_monotonic(void);
+
+/**
+ * Sleep for a period of time.  Although the duration is expressed in
+ * microseconds, the actual delay may be rounded to the precision of the
+ * system timer.
+ *
+ * @param  usec Number of microseconds to sleep.
+ * @return zero on success or (negative) error code.
+ */
+int av_usleep(unsigned usec);
+
+#endif /* AVUTIL_TIME_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timecode.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timecode.h
new file mode 100644
index 0000000..37c1361
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timecode.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2006 Smartjog S.A.S, Baptiste Coudurier <baptiste.coudurier@gmail.com>
+ * Copyright (c) 2011-2012 Smartjog S.A.S, Clément Bœsch <clement.boesch@smartjog.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Timecode helpers header
+ */
+
+#ifndef AVUTIL_TIMECODE_H
+#define AVUTIL_TIMECODE_H
+
+#include <stdint.h>
+#include "rational.h"
+
+#define AV_TIMECODE_STR_SIZE 23
+
+enum AVTimecodeFlag {
+    AV_TIMECODE_FLAG_DROPFRAME      = 1<<0, ///< timecode is drop frame
+    AV_TIMECODE_FLAG_24HOURSMAX     = 1<<1, ///< timecode wraps after 24 hours
+    AV_TIMECODE_FLAG_ALLOWNEGATIVE  = 1<<2, ///< negative time values are allowed
+};
+
+typedef struct {
+    int start;          ///< timecode frame start (first base frame number)
+    uint32_t flags;     ///< flags such as drop frame, +24 hours support, ...
+    AVRational rate;    ///< frame rate in rational form
+    unsigned fps;       ///< frame per second; must be consistent with the rate field
+} AVTimecode;
+
+/**
+ * Adjust frame number for NTSC drop frame time code.
+ *
+ * @param framenum frame number to adjust
+ * @param fps      frame per second, 30 or 60
+ * @return         adjusted frame number
+ * @warning        adjustment is only valid in NTSC 29.97 and 59.94
+ */
+int av_timecode_adjust_ntsc_framenum2(int framenum, int fps);
+
+/**
+ * Convert frame number to SMPTE 12M binary representation.
+ *
+ * @param tc       timecode data correctly initialized
+ * @param framenum frame number
+ * @return         the SMPTE binary representation
+ *
+ * @note Frame number adjustment is automatically done in case of drop timecode,
+ *       you do NOT have to call av_timecode_adjust_ntsc_framenum2().
+ * @note The frame number is relative to tc->start.
+ * @note Color frame (CF), binary group flags (BGF) and biphase mark polarity
+ *       correction (PC) bits are set to zero.
+ */
+uint32_t av_timecode_get_smpte_from_framenum(const AVTimecode *tc, int framenum);
+
+/**
+ * Load timecode string in buf.
+ *
+ * @param buf      destination buffer, must be at least AV_TIMECODE_STR_SIZE long
+ * @param tc       timecode data correctly initialized
+ * @param framenum frame number
+ * @return         the buf parameter
+ *
+ * @note Timecode representation can be a negative timecode and have more than
+ *       24 hours, but will only be honored if the flags are correctly set.
+ * @note The frame number is relative to tc->start.
+ */
+char *av_timecode_make_string(const AVTimecode *tc, char *buf, int framenum);
+
+/**
+ * Get the timecode string from the SMPTE timecode format.
+ *
+ * @param buf        destination buffer, must be at least AV_TIMECODE_STR_SIZE long
+ * @param tcsmpte    the 32-bit SMPTE timecode
+ * @param prevent_df prevent the use of a drop flag when it is known the DF bit
+ *                   is arbitrary
+ * @return           the buf parameter
+ */
+char *av_timecode_make_smpte_tc_string(char *buf, uint32_t tcsmpte, int prevent_df);
+
+/**
+ * Get the timecode string from the 25-bit timecode format (MPEG GOP format).
+ *
+ * @param buf     destination buffer, must be at least AV_TIMECODE_STR_SIZE long
+ * @param tc25bit the 25-bits timecode
+ * @return        the buf parameter
+ */
+char *av_timecode_make_mpeg_tc_string(char *buf, uint32_t tc25bit);
+
+/**
+ * Init a timecode struct with the passed parameters.
+ *
+ * @param log_ctx     a pointer to an arbitrary struct of which the first field
+ *                    is a pointer to an AVClass struct (used for av_log)
+ * @param tc          pointer to an allocated AVTimecode
+ * @param rate        frame rate in rational form
+ * @param flags       miscellaneous flags such as drop frame, +24 hours, ...
+ *                    (see AVTimecodeFlag)
+ * @param frame_start the first frame number
+ * @return            0 on success, AVERROR otherwise
+ */
+int av_timecode_init(AVTimecode *tc, AVRational rate, int flags, int frame_start, void *log_ctx);
+
+/**
+ * Parse timecode representation (hh:mm:ss[:;.]ff).
+ *
+ * @param log_ctx a pointer to an arbitrary struct of which the first field is a
+ *                pointer to an AVClass struct (used for av_log).
+ * @param tc      pointer to an allocated AVTimecode
+ * @param rate    frame rate in rational form
+ * @param str     timecode string which will determine the frame start
+ * @return        0 on success, AVERROR otherwise
+ */
+int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *str, void *log_ctx);
+
+/**
+ * Check if the timecode feature is available for the given frame rate
+ *
+ * @return 0 if supported, <0 otherwise
+ */
+int av_timecode_check_frame_rate(AVRational rate);
+
+#endif /* AVUTIL_TIMECODE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timestamp.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timestamp.h
new file mode 100644
index 0000000..e082f01
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/timestamp.h
@@ -0,0 +1,78 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * timestamp utils, mostly useful for debugging/logging purposes
+ */
+
+#ifndef AVUTIL_TIMESTAMP_H
+#define AVUTIL_TIMESTAMP_H
+
+#include "common.h"
+
+#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
+#error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
+#endif
+
+#define AV_TS_MAX_STRING_SIZE 32
+
+/**
+ * Fill the provided buffer with a string containing a timestamp
+ * representation.
+ *
+ * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
+ * @param ts the timestamp to represent
+ * @return the buffer in input
+ */
+static inline char *av_ts_make_string(char *buf, int64_t ts)
+{
+    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%" PRId64, ts);
+    return buf;
+}
+
+/**
+ * Convenience macro, the return value should be used only directly in
+ * function arguments but never stand-alone.
+ */
+#define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
+
+/**
+ * Fill the provided buffer with a string containing a timestamp time
+ * representation.
+ *
+ * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
+ * @param ts the timestamp to represent
+ * @param tb the timebase of the timestamp
+ * @return the buffer in input
+ */
+static inline char *av_ts_make_time_string(char *buf, int64_t ts, AVRational *tb)
+{
+    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
+    return buf;
+}
+
+/**
+ * Convenience macro, the return value should be used only directly in
+ * function arguments but never stand-alone.
+ */
+#define av_ts2timestr(ts, tb) av_ts_make_time_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb)
+
+#endif /* AVUTIL_TIMESTAMP_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tree.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tree.h
new file mode 100644
index 0000000..d5e0aeb
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/tree.h
@@ -0,0 +1,138 @@
+/*
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * A tree container.
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#ifndef AVUTIL_TREE_H
+#define AVUTIL_TREE_H
+
+#include "attributes.h"
+#include "version.h"
+
+/**
+ * @addtogroup lavu_tree AVTree
+ * @ingroup lavu_data
+ *
+ * Low-complexity tree container
+ *
+ * Insertion, removal, finding equal, largest which is smaller than and
+ * smallest which is larger than, all have O(log n) worst-case complexity.
+ * @{
+ */
+
+
+struct AVTreeNode;
+extern const int av_tree_node_size;
+
+/**
+ * Allocate an AVTreeNode.
+ */
+struct AVTreeNode *av_tree_node_alloc(void);
+
+/**
+ * Find an element.
+ * @param root a pointer to the root node of the tree
+ * @param next If next is not NULL, then next[0] will contain the previous
+ *             element and next[1] the next element. If either does not exist,
+ *             then the corresponding entry in next is unchanged.
+ * @param cmp compare function used to compare elements in the tree,
+ *            API identical to that of Standard C's qsort
+ *            It is guaranteed that the first and only the first argument to cmp()
+ *            will be the key parameter to av_tree_find(), thus it could if the
+ *            user wants, be a different type (like an opaque context).
+ * @return An element with cmp(key, elem) == 0 or NULL if no such element
+ *         exists in the tree.
+ */
+void *av_tree_find(const struct AVTreeNode *root, void *key,
+                   int (*cmp)(const void *key, const void *b), void *next[2]);
+
+/**
+ * Insert or remove an element.
+ *
+ * If *next is NULL, then the supplied element will be removed if it exists.
+ * If *next is non-NULL, then the supplied element will be inserted, unless
+ * it already exists in the tree.
+ *
+ * @param rootp A pointer to a pointer to the root node of the tree; note that
+ *              the root node can change during insertions, this is required
+ *              to keep the tree balanced.
+ * @param key  pointer to the element key to insert in the tree
+ * @param next Used to allocate and free AVTreeNodes. For insertion the user
+ *             must set it to an allocated and zeroed object of at least
+ *             av_tree_node_size bytes size. av_tree_insert() will set it to
+ *             NULL if it has been consumed.
+ *             For deleting elements *next is set to NULL by the user and
+ *             av_tree_insert() will set it to the AVTreeNode which was
+ *             used for the removed element.
+ *             This allows the use of flat arrays, which have
+ *             lower overhead compared to many malloced elements.
+ *             You might want to define a function like:
+ *             @code
+ *             void *tree_insert(struct AVTreeNode **rootp, void *key,
+ *                               int (*cmp)(void *key, const void *b),
+ *                               AVTreeNode **next)
+ *             {
+ *                 if (!*next)
+ *                     *next = av_mallocz(av_tree_node_size);
+ *                 return av_tree_insert(rootp, key, cmp, next);
+ *             }
+ *             void *tree_remove(struct AVTreeNode **rootp, void *key,
+ *                               int (*cmp)(void *key, const void *b, AVTreeNode **next))
+ *             {
+ *                 av_freep(next);
+ *                 return av_tree_insert(rootp, key, cmp, next);
+ *             }
+ *             @endcode
+ * @param cmp compare function used to compare elements in the tree, API identical
+ *            to that of Standard C's qsort
+ * @return If no insertion happened, the found element; if an insertion or
+ *         removal happened, then either key or NULL will be returned.
+ *         Which one it is depends on the tree state and the implementation. You
+ *         should make no assumptions that it's one or the other in the code.
+ */
+void *av_tree_insert(struct AVTreeNode **rootp, void *key,
+                     int (*cmp)(const void *key, const void *b),
+                     struct AVTreeNode **next);
+
+void av_tree_destroy(struct AVTreeNode *t);
+
+/**
+ * Apply enu(opaque, &elem) to all the elements in the tree in a given range.
+ *
+ * @param cmp a comparison function that returns < 0 for an element below the
+ *            range, > 0 for an element above the range and == 0 for an
+ *            element inside the range
+ *
+ * @note The cmp function should use the same ordering used to construct the
+ *       tree.
+ */
+void av_tree_enumerate(struct AVTreeNode *t, void *opaque,
+                       int (*cmp)(void *opaque, void *elem),
+                       int (*enu)(void *opaque, void *elem));
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_TREE_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/twofish.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/twofish.h
new file mode 100644
index 0000000..813cfec
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/twofish.h
@@ -0,0 +1,70 @@
+/*
+ * An implementation of the TwoFish algorithm
+ * Copyright (c) 2015 Supraja Meedinti
+ *
+ * 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 AVUTIL_TWOFISH_H
+#define AVUTIL_TWOFISH_H
+
+#include <stdint.h>
+
+
+/**
+  * @file
+  * @brief Public header for libavutil TWOFISH algorithm
+  * @defgroup lavu_twofish TWOFISH
+  * @ingroup lavu_crypto
+  * @{
+  */
+
+extern const int av_twofish_size;
+
+struct AVTWOFISH;
+
+/**
+  * Allocate an AVTWOFISH context
+  * To free the struct: av_free(ptr)
+  */
+struct AVTWOFISH *av_twofish_alloc(void);
+
+/**
+  * Initialize an AVTWOFISH context.
+  *
+  * @param ctx an AVTWOFISH context
+  * @param key a key of size ranging from 1 to 32 bytes used for encryption/decryption
+  * @param key_bits number of keybits: 128, 192, 256 If less than the required, padded with zeroes to nearest valid value; return value is 0 if key_bits is 128/192/256, -1 if less than 0, 1 otherwise
+ */
+int av_twofish_init(struct AVTWOFISH *ctx, const uint8_t *key, int key_bits);
+
+/**
+  * Encrypt or decrypt a buffer using a previously initialized context
+  *
+  * @param ctx an AVTWOFISH context
+  * @param dst destination array, can be equal to src
+  * @param src source array, can be equal to dst
+  * @param count number of 16 byte blocks
+  * @paran iv initialization vector for CBC mode, NULL for ECB mode
+  * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_twofish_crypt(struct AVTWOFISH *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t* iv, int decrypt);
+
+/**
+ * @}
+ */
+#endif /* AVUTIL_TWOFISH_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/version.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/version.h
new file mode 100644
index 0000000..8f6da6a
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/version.h
@@ -0,0 +1,139 @@
+/*
+ * copyright (c) 2003 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
+ */
+
+/**
+ * @file
+ * @ingroup lavu
+ * Libavutil version macros
+ */
+
+#ifndef AVUTIL_VERSION_H
+#define AVUTIL_VERSION_H
+
+#include "macros.h"
+
+/**
+ * @addtogroup version_utils
+ *
+ * Useful to check and match library version in order to maintain
+ * backward compatibility.
+ *
+ * The FFmpeg libraries follow a versioning sheme very similar to
+ * Semantic Versioning (http://semver.org/)
+ * The difference is that the component called PATCH is called MICRO in FFmpeg
+ * and its value is reset to 100 instead of 0 to keep it above or equal to 100.
+ * Also we do not increase MICRO for every bugfix or change in git master.
+ *
+ * Prior to FFmpeg 3.2 point releases did not change any lib version number to
+ * avoid aliassing different git master checkouts.
+ * Starting with FFmpeg 3.2, the released library versions will occupy
+ * a separate MAJOR.MINOR that is not used on the master development branch.
+ * That is if we branch a release of master 55.10.123 we will bump to 55.11.100
+ * for the release and master will continue at 55.12.100 after it. Each new
+ * point release will then bump the MICRO improving the usefulness of the lib
+ * versions.
+ *
+ * @{
+ */
+
+#define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
+#define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c
+#define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c)
+
+/**
+ * Extract version components from the full ::AV_VERSION_INT int as returned
+ * by functions like ::avformat_version() and ::avcodec_version()
+ */
+#define AV_VERSION_MAJOR(a) ((a) >> 16)
+#define AV_VERSION_MINOR(a) (((a) & 0x00FF00) >> 8)
+#define AV_VERSION_MICRO(a) ((a) & 0xFF)
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lavu_ver Version and Build diagnostics
+ *
+ * Macros and function useful to check at compiletime and at runtime
+ * which version of libavutil is in use.
+ *
+ * @{
+ */
+
+#define LIBAVUTIL_VERSION_MAJOR  56
+#define LIBAVUTIL_VERSION_MINOR  22
+#define LIBAVUTIL_VERSION_MICRO 100
+
+#define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
+                                               LIBAVUTIL_VERSION_MINOR, \
+                                               LIBAVUTIL_VERSION_MICRO)
+#define LIBAVUTIL_VERSION       AV_VERSION(LIBAVUTIL_VERSION_MAJOR,     \
+                                           LIBAVUTIL_VERSION_MINOR,     \
+                                           LIBAVUTIL_VERSION_MICRO)
+#define LIBAVUTIL_BUILD         LIBAVUTIL_VERSION_INT
+
+#define LIBAVUTIL_IDENT         "Lavu" AV_STRINGIFY(LIBAVUTIL_VERSION)
+
+/**
+ * @defgroup lavu_depr_guards Deprecation Guards
+ * FF_API_* defines may be placed below to indicate public API that will be
+ * dropped at a future version bump. The defines themselves are not part of
+ * the public API and may change, break or disappear at any time.
+ *
+ * @note, when bumping the major version it is recommended to manually
+ * disable each FF_API_* in its own commit instead of disabling them all
+ * at once through the bump. This improves the git bisect-ability of the change.
+ *
+ * @{
+ */
+
+#ifndef FF_API_VAAPI
+#define FF_API_VAAPI                    (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_FRAME_QP
+#define FF_API_FRAME_QP                 (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_PLUS1_MINUS1
+#define FF_API_PLUS1_MINUS1             (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_ERROR_FRAME
+#define FF_API_ERROR_FRAME              (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_PKT_PTS
+#define FF_API_PKT_PTS                  (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_CRYPTO_SIZE_T
+#define FF_API_CRYPTO_SIZE_T            (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_FRAME_GET_SET
+#define FF_API_FRAME_GET_SET            (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+#ifndef FF_API_PSEUDOPAL
+#define FF_API_PSEUDOPAL                (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
+
+
+/**
+ * @}
+ * @}
+ */
+
+#endif /* AVUTIL_VERSION_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/xtea.h b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/xtea.h
new file mode 100644
index 0000000..735427c
--- /dev/null
+++ b/src/third_party/ffmpeg_includes/ffmpeg.58.35.100/libavutil/xtea.h
@@ -0,0 +1,94 @@
+/*
+ * A 32-bit implementation of the XTEA algorithm
+ * Copyright (c) 2012 Samuel Pitoiset
+ *
+ * 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 AVUTIL_XTEA_H
+#define AVUTIL_XTEA_H
+
+#include <stdint.h>
+
+/**
+ * @file
+ * @brief Public header for libavutil XTEA algorithm
+ * @defgroup lavu_xtea XTEA
+ * @ingroup lavu_crypto
+ * @{
+ */
+
+typedef struct AVXTEA {
+    uint32_t key[16];
+} AVXTEA;
+
+/**
+ * Allocate an AVXTEA context.
+ */
+AVXTEA *av_xtea_alloc(void);
+
+/**
+ * Initialize an AVXTEA context.
+ *
+ * @param ctx an AVXTEA context
+ * @param key a key of 16 bytes used for encryption/decryption,
+ *            interpreted as big endian 32 bit numbers
+ */
+void av_xtea_init(struct AVXTEA *ctx, const uint8_t key[16]);
+
+/**
+ * Initialize an AVXTEA context.
+ *
+ * @param ctx an AVXTEA context
+ * @param key a key of 16 bytes used for encryption/decryption,
+ *            interpreted as little endian 32 bit numbers
+ */
+void av_xtea_le_init(struct AVXTEA *ctx, const uint8_t key[16]);
+
+/**
+ * Encrypt or decrypt a buffer using a previously initialized context,
+ * in big endian format.
+ *
+ * @param ctx an AVXTEA context
+ * @param dst destination array, can be equal to src
+ * @param src source array, can be equal to dst
+ * @param count number of 8 byte blocks
+ * @param iv initialization vector for CBC mode, if NULL then ECB will be used
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_xtea_crypt(struct AVXTEA *ctx, uint8_t *dst, const uint8_t *src,
+                   int count, uint8_t *iv, int decrypt);
+
+/**
+ * Encrypt or decrypt a buffer using a previously initialized context,
+ * in little endian format.
+ *
+ * @param ctx an AVXTEA context
+ * @param dst destination array, can be equal to src
+ * @param src source array, can be equal to dst
+ * @param count number of 8 byte blocks
+ * @param iv initialization vector for CBC mode, if NULL then ECB will be used
+ * @param decrypt 0 for encryption, 1 for decryption
+ */
+void av_xtea_le_crypt(struct AVXTEA *ctx, uint8_t *dst, const uint8_t *src,
+                      int count, uint8_t *iv, int decrypt);
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_XTEA_H */
diff --git a/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp b/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp
index ef2977c..51e447c 100644
--- a/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp
+++ b/src/third_party/ffmpeg_includes/ffmpeg_includes.gyp
@@ -35,5 +35,12 @@
         'include_dirs': [ 'ffmpeg.57.107.100' ],
       },
     },
+    {
+      'target_name': 'ffmpeg.58.35.100',
+      'type': 'none',
+      'direct_dependent_settings': {
+        'include_dirs': [ 'ffmpeg.58.35.100' ],
+      },
+    },
   ],
 }
diff --git a/src/third_party/llvm-project/libunwind/libunwind.gyp b/src/third_party/llvm-project/libunwind/libunwind.gyp
index 3a32bbd..0d2587c 100644
--- a/src/third_party/llvm-project/libunwind/libunwind.gyp
+++ b/src/third_party/llvm-project/libunwind/libunwind.gyp
@@ -71,7 +71,6 @@
         'src/libunwind_ext.h',
         'src/Registers.hpp',
         'src/RWMutex.hpp',
-        'src/Unwind_AppleExtras.cpp',
         'src/UnwindCursor.hpp',
         'src/Unwind-EHABI.cpp',
         'src/Unwind-EHABI.h',
@@ -91,6 +90,13 @@
           'LLVM_PATH="<(DEPTH)/third_party/llvm-project/llvm/"',
         ],
       },
+      'conditions': [
+        ['target_os == "tvos"', {
+          'sources': [
+            'src/Unwind_AppleExtras.cpp',
+          ],
+        }],
+      ],
     }
   ]
 }
diff --git a/src/third_party/mozjs-45/config/milestone.txt.orig b/src/third_party/mozjs-45/config/milestone.txt.orig
deleted file mode 100644
index da9c934..0000000
--- a/src/third_party/mozjs-45/config/milestone.txt.orig
+++ /dev/null
@@ -1 +0,0 @@
-44.0a2
diff --git a/src/third_party/mozjs-45/js/public/UbiNodeCensus.h.rej b/src/third_party/mozjs-45/js/public/UbiNodeCensus.h.rej
deleted file mode 100644
index 070a25d..0000000
--- a/src/third_party/mozjs-45/js/public/UbiNodeCensus.h.rej
+++ /dev/null
@@ -1,21 +0,0 @@
---- UbiNodeCensus.h
-+++ UbiNodeCensus.h
-@@ -4,16 +4,18 @@
-  * License, v. 2.0. If a copy of the MPL was not distributed with this
-  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- 
- #ifndef js_UbiNodeCensus_h
- #define js_UbiNodeCensus_h
- 
- #include "mozilla/Move.h"
- 
-+#include "jsapi.h"
-+
- #include "js/UbiNode.h"
- #include "js/UbiNodeTraverse.h"
- 
- // A census is a ubi::Node traversal that assigns each node to one or more
- // buckets, and returns a report with the size of each bucket.
- //
- // We summarize the results of a census with counts broken down according to
- // criteria selected by the API consumer code that is requesting the census. For
diff --git a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/analyzeRoots.js.orig b/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/analyzeRoots.js.orig
deleted file mode 100644
index 096c8c6..0000000
--- a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/analyzeRoots.js.orig
+++ /dev/null
@@ -1,802 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
-
-"use strict";
-
-loadRelativeToScript('utility.js');
-loadRelativeToScript('annotations.js');
-loadRelativeToScript('CFG.js');
-
-var sourceRoot = (os.getenv('SOURCE') || '') + '/'
-
-var functionName;
-var functionBodies;
-
-if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string')
-    throw "Usage: analyzeRoots.js [-f function_name] <gcFunctions.lst> <gcEdges.txt> <suppressedFunctions.lst> <gcTypes.txt> <typeInfo.txt> [start end [tmpfile]]";
-
-var theFunctionNameToFind;
-if (scriptArgs[0] == '--function') {
-    theFunctionNameToFind = scriptArgs[1];
-    scriptArgs = scriptArgs.slice(2);
-}
-
-var gcFunctionsFile = scriptArgs[0] || "gcFunctions.lst";
-var gcEdgesFile = scriptArgs[1] || "gcEdges.txt";
-var suppressedFunctionsFile = scriptArgs[2] || "suppressedFunctions.lst";
-var gcTypesFile = scriptArgs[3] || "gcTypes.txt";
-var typeInfoFile = scriptArgs[4] || "typeInfo.txt";
-var batch = (scriptArgs[5]|0) || 1;
-var numBatches = (scriptArgs[6]|0) || 1;
-var tmpfile = scriptArgs[7] || "tmp.txt";
-
-GCSuppressionTypes.push(...loadTypeInfo(typeInfoFile)["Suppress GC"]);
-
-var gcFunctions = {};
-var text = snarf("gcFunctions.lst").split("\n");
-assert(text.pop().length == 0);
-for (var line of text)
-    gcFunctions[mangled(line)] = true;
-
-var suppressedFunctions = {};
-var text = snarf(suppressedFunctionsFile).split("\n");
-assert(text.pop().length == 0);
-for (var line of text) {
-    suppressedFunctions[line] = true;
-}
-text = null;
-
-var gcEdges = {};
-text = snarf(gcEdgesFile).split('\n');
-assert(text.pop().length == 0);
-for (var line of text) {
-    var [ block, edge, func ] = line.split(" || ");
-    if (!(block in gcEdges))
-        gcEdges[block] = {}
-    gcEdges[block][edge] = func;
-}
-text = null;
-
-for (var line of readFileLines_gen(typeInfoFile)) {
-    line = line.replace(/\n/, "");
-    GCSuppressionTypes.push(line);
-}
-
-var match;
-var gcThings = {};
-var gcPointers = {};
-
-text = snarf(gcTypesFile).split("\n");
-for (var line of text) {
-    if (match = /^GCThing: (.*)/.exec(line))
-        gcThings[match[1]] = true;
-    if (match = /^GCPointer: (.*)/.exec(line))
-        gcPointers[match[1]] = true;
-}
-text = null;
-
-function isGCType(type)
-{
-    if (type.Kind == "CSU")
-        return type.Name in gcThings;
-    else if (type.Kind == "Array")
-        return isGCType(type.Type);
-    return false;
-}
-
-function isUnrootedType(type)
-{
-    if (type.Kind == "Pointer")
-        return isGCType(type.Type);
-    else if (type.Kind == "Array")
-        return isUnrootedType(type.Type);
-    else if (type.Kind == "CSU")
-        return type.Name in gcPointers;
-    else
-        return false;
-}
-
-function expressionUsesVariable(exp, variable)
-{
-    if (exp.Kind == "Var" && sameVariable(exp.Variable, variable))
-        return true;
-    if (!("Exp" in exp))
-        return false;
-    for (var childExp of exp.Exp) {
-        if (expressionUsesVariable(childExp, variable))
-            return true;
-    }
-    return false;
-}
-
-function expressionUsesVariableContents(exp, variable)
-{
-    if (!("Exp" in exp))
-        return false;
-    for (var childExp of exp.Exp) {
-        if (childExp.Kind == 'Drf') {
-            if (expressionUsesVariable(childExp, variable))
-                return true;
-        } else if (expressionUsesVariableContents(childExp, variable)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-// Detect simple |return nullptr;| statements.
-function isReturningImmobileValue(edge, variable)
-{
-    if (variable.Kind == "Return") {
-        if (edge.Exp[0].Kind == "Var" && sameVariable(edge.Exp[0].Variable, variable)) {
-            if (edge.Exp[1].Kind == "Int" && edge.Exp[1].String == "0") {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-// If the edge uses the given variable, return the earliest point at which the
-// use is definite. Usually, that means the source of the edge (anything that
-// reaches that source point will end up using the variable, but there may be
-// other ways to reach the destination of the edge.)
-//
-// Return values are implicitly used at the very last point in the function.
-// This makes a difference: if an RAII class GCs in its destructor, we need to
-// start looking at the final point in the function, not one point back from
-// that, since that would skip over the GCing call.
-//
-function edgeUsesVariable(edge, variable, body)
-{
-    if (ignoreEdgeUse(edge, variable, body))
-        return 0;
-
-    if (variable.Kind == "Return" && body.Index[1] == edge.Index[1] && body.BlockId.Kind == "Function")
-        return edge.Index[1]; // Last point in function body uses the return value.
-
-    var src = edge.Index[0];
-
-    switch (edge.Kind) {
-
-    case "Assign":
-        if (isReturningImmobileValue(edge, variable))
-            return 0;
-        if (expressionUsesVariable(edge.Exp[0], variable))
-            return src;
-        return expressionUsesVariable(edge.Exp[1], variable) ? src : 0;
-
-    case "Assume":
-        return expressionUsesVariableContents(edge.Exp[0], variable) ? src : 0;
-
-    case "Call":
-        if (expressionUsesVariable(edge.Exp[0], variable))
-            return src;
-        if (1 in edge.Exp && expressionUsesVariable(edge.Exp[1], variable))
-            return src;
-        if ("PEdgeCallInstance" in edge) {
-            if (expressionUsesVariable(edge.PEdgeCallInstance.Exp, variable))
-                return src;
-        }
-        if ("PEdgeCallArguments" in edge) {
-            for (var exp of edge.PEdgeCallArguments.Exp) {
-                if (expressionUsesVariable(exp, variable))
-                    return src;
-            }
-        }
-        return 0;
-
-    case "Loop":
-        return 0;
-
-    default:
-        assert(false);
-    }
-}
-
-function expressionIsVariableAddress(exp, variable)
-{
-    while (exp.Kind == "Fld")
-        exp = exp.Exp[0];
-    return exp.Kind == "Var" && sameVariable(exp.Variable, variable);
-}
-
-function edgeTakesVariableAddress(edge, variable, body)
-{
-    if (ignoreEdgeUse(edge, variable, body))
-        return false;
-    if (ignoreEdgeAddressTaken(edge))
-        return false;
-    switch (edge.Kind) {
-    case "Assign":
-        return expressionIsVariableAddress(edge.Exp[1], variable);
-    case "Call":
-        if ("PEdgeCallArguments" in edge) {
-            for (var exp of edge.PEdgeCallArguments.Exp) {
-                if (expressionIsVariableAddress(exp, variable))
-                    return true;
-            }
-        }
-        return false;
-    default:
-        return false;
-    }
-}
-
-function edgeKillsVariable(edge, variable)
-{
-    // Direct assignments kill their lhs: var = value
-    if (edge.Kind == "Assign") {
-        var lhs = edge.Exp[0];
-        if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable))
-            return !isReturningImmobileValue(edge, variable);
-    }
-
-    if (edge.Kind != "Call")
-        return false;
-
-    // Assignments of call results kill their lhs.
-    if (1 in edge.Exp) {
-        var lhs = edge.Exp[1];
-        if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable))
-            return true;
-    }
-
-    // Constructor calls kill their 'this' value.
-    if ("PEdgeCallInstance" in edge) {
-        do {
-            var instance = edge.PEdgeCallInstance.Exp;
-
-            // Kludge around incorrect dereference on some constructor calls.
-            if (instance.Kind == "Drf")
-                instance = instance.Exp[0];
-
-            if (instance.Kind != "Var" || !sameVariable(instance.Variable, variable))
-                break;
-
-            var callee = edge.Exp[0];
-            if (callee.Kind != "Var")
-                break;
-
-            assert(callee.Variable.Kind == "Func");
-            var calleeName = readable(callee.Variable.Name[0]);
-
-            // Constructor calls include the text 'Name::Name(' or 'Name<...>::Name('.
-            var openParen = calleeName.indexOf('(');
-            if (openParen < 0)
-                break;
-            calleeName = calleeName.substring(0, openParen);
-
-            var lastColon = calleeName.lastIndexOf('::');
-            if (lastColon < 0)
-                break;
-            var constructorName = calleeName.substr(lastColon + 2);
-            calleeName = calleeName.substr(0, lastColon);
-
-            var lastTemplateOpen = calleeName.lastIndexOf('<');
-            if (lastTemplateOpen >= 0)
-                calleeName = calleeName.substr(0, lastTemplateOpen);
-
-            if (calleeName.endsWith(constructorName))
-                return true;
-        } while (false);
-    }
-
-    return false;
-}
-
-function edgeCanGC(edge)
-{
-    if (edge.Kind != "Call")
-        return false;
-
-    var callee = edge.Exp[0];
-
-    while (callee.Kind == "Drf")
-        callee = callee.Exp[0];
-
-    if (callee.Kind == "Var") {
-        var variable = callee.Variable;
-
-        if (variable.Kind == "Func") {
-            var callee = mangled(variable.Name[0]);
-            if ((callee in gcFunctions) || ((callee + internalMarker) in gcFunctions))
-                return "'" + variable.Name[0] + "'";
-            return null;
-        }
-
-        var varName = variable.Name[0];
-        return indirectCallCannotGC(functionName, varName) ? null : "*" + varName;
-    }
-
-    if (callee.Kind == "Fld") {
-        var field = callee.Field;
-        var csuName = field.FieldCSU.Type.Name;
-        var fullFieldName = csuName + "." + field.Name[0];
-        if (fieldCallCannotGC(csuName, fullFieldName))
-            return null;
-        return (fullFieldName in suppressedFunctions) ? null : fullFieldName;
-    }
-}
-
-// Search recursively through predecessors from a variable use, returning
-// whether a GC call is reachable (in the reverse direction; this means that
-// the variable use is reachable from the GC call, and therefore the variable
-// is live after the GC call), along with some additional information. What
-// info we want depends on whether the variable turns out to be live across any
-// GC call. We are looking for both hazards (unrooted variables live across GC
-// calls) and unnecessary roots (rooted variables that have no GC calls in
-// their live ranges.)
-//
-// If not:
-//
-//  - 'minimumUse': the earliest point in each body that uses the variable, for
-//    reporting on unnecessary roots.
-//
-// If so:
-//
-//  - 'why': a path from the GC call to a use of the variable after the GC
-//    call, chained through a 'why' field in the returned edge descriptor
-//
-//  - 'gcInfo': a direct pointer to the GC call edge
-//
-function findGCBeforeVariableUse(start_body, start_point, suppressed, variable)
-{
-    // Scan through all edges preceding an unrooted variable use, using an
-    // explicit worklist, looking for a GC call. A worklist contains an
-    // incoming edge together with a description of where it or one of its
-    // successors GC'd (if any).
-
-    var bodies_visited = new Map();
-
-    let worklist = [{body: start_body, ppoint: start_point, preGCLive: false, gcInfo: null, why: null}];
-    while (worklist.length) {
-        // Grab an entry off of the worklist, representing a point within the
-        // CFG identified by <body,ppoint>. If this point has a descendant
-        // later in the CFG that can GC, gcInfo will be set to the information
-        // about that GC call.
-
-        var entry = worklist.pop();
-        var { body, ppoint, gcInfo, preGCLive } = entry;
-
-        // Handle the case where there are multiple ways to reach this point
-        // (traversing backwards).
-        var visited = bodies_visited.get(body);
-        if (!visited)
-            bodies_visited.set(body, visited = new Map());
-        if (visited.has(ppoint)) {
-            var seenEntry = visited.get(ppoint);
-
-            // This point already knows how to GC through some other path, so
-            // we have nothing new to learn. (The other path will consider the
-            // predecessors.)
-            if (seenEntry.gcInfo)
-                continue;
-
-            // If this worklist's entry doesn't know of any way to GC, then
-            // there's no point in continuing the traversal through it. Perhaps
-            // another edge will be found that *can* GC; otherwise, the first
-            // route to the point will traverse through predecessors.
-            //
-            // Note that this means we may visit a point more than once, if the
-            // first time we visit we don't have a known reachable GC call and
-            // the second time we do.
-            if (!gcInfo)
-                continue;
-        }
-        visited.set(ppoint, {body: body, gcInfo: gcInfo});
-
-        // Check for hitting the entry point of the current body (which may be
-        // the outer function or a loop within it.)
-        if (ppoint == body.Index[0]) {
-            if (body.BlockId.Kind == "Loop") {
-                // Propagate to outer body parents that enter the loop body.
-                if ("BlockPPoint" in body) {
-                    for (var parent of body.BlockPPoint) {
-                        var found = false;
-                        for (var xbody of functionBodies) {
-                            if (sameBlockId(xbody.BlockId, parent.BlockId)) {
-                                assert(!found);
-                                found = true;
-                                worklist.push({body: xbody, ppoint: parent.Index,
-                                               gcInfo: gcInfo, why: entry});
-                            }
-                        }
-                        assert(found);
-                    }
-                }
-            } else if (variable.Kind == "Arg" && gcInfo) {
-                // The scope of arguments starts at the beginning of the
-                // function
-                return entry;
-            } else if (entry.preGCLive) {
-                // We didn't find a "good" explanation beginning of the live
-                // range, but we do know the variable was live across the GC.
-                // This can happen if the live range started when a variable is
-                // used as a retparam.
-                return entry;
-            }
-        }
-
-        var predecessors = getPredecessors(body);
-        if (!(ppoint in predecessors))
-            continue;
-
-        for (var edge of predecessors[ppoint]) {
-            var source = edge.Index[0];
-
-            var edge_kills = edgeKillsVariable(edge, variable);
-            var edge_uses = edgeUsesVariable(edge, variable, body);
-
-            if (edge_kills || edge_uses) {
-                if (!body.minimumUse || source < body.minimumUse)
-                    body.minimumUse = source;
-            }
-
-            if (edge_kills) {
-                // This is a beginning of the variable's live range. If we can
-                // reach a GC call from here, then we're done -- we have a path
-                // from the beginning of the live range, through the GC call,
-                // to a use after the GC call that proves its live range
-                // extends at least that far.
-                if (gcInfo)
-                    return {body: body, ppoint: source, gcInfo: gcInfo, why: entry };
-
-                // Otherwise, keep searching through the graph, but truncate
-                // this particular branch of the search at this edge.
-                continue;
-            }
-
-            var src_gcInfo = gcInfo;
-            var src_preGCLive = preGCLive;
-            if (!gcInfo && !(source in body.suppressed) && !suppressed) {
-                var gcName = edgeCanGC(edge, body);
-                if (gcName)
-                    src_gcInfo = {name:gcName, body:body, ppoint:source};
-            }
-
-            if (edge_uses) {
-                // The live range starts at least this far back, so we're done
-                // for the same reason as with edge_kills. The only difference
-                // is that a GC on this edge indicates a hazard, whereas if
-                // we're killing a live range in the GC call then it's not live
-                // *across* the call.
-                //
-                // However, we may want to generate a longer usage chain for
-                // the variable than is minimally necessary. For example,
-                // consider:
-                //
-                //   Value v = f();
-                //   if (v.isUndefined())
-                //     return false;
-                //   gc();
-                //   return v;
-                //
-                // The call to .isUndefined() is considered to be a use and
-                // therefore indicates that v must be live at that point. But
-                // it's more helpful to the user to continue the 'why' path to
-                // include the ancestor where the value was generated. So we
-                // will only return here if edge.Kind is Assign; otherwise,
-                // we'll pass a "preGCLive" value up through the worklist to
-                // remember that the variable *is* alive before the GC and so
-                // this function should be returning a true value.
-
-                if (src_gcInfo) {
-                    src_preGCLive = true;
-                    if (edge.Kind == 'Assign')
-                        return {body:body, ppoint:source, gcInfo:src_gcInfo, why:entry};
-                }
-            }
-
-            if (edge.Kind == "Loop") {
-                // Additionally propagate the search into a loop body, starting
-                // with the exit point.
-                var found = false;
-                for (var xbody of functionBodies) {
-                    if (sameBlockId(xbody.BlockId, edge.BlockId)) {
-                        assert(!found);
-                        found = true;
-                        worklist.push({body:xbody, ppoint:xbody.Index[1],
-                                       preGCLive: src_preGCLive, gcInfo:src_gcInfo,
-                                       why:entry});
-                    }
-                }
-                assert(found);
-                break;
-            }
-
-            // Propagate the search to the predecessors of this edge.
-            worklist.push({body:body, ppoint:source,
-                           preGCLive: src_preGCLive, gcInfo:src_gcInfo,
-                           why:entry});
-        }
-    }
-
-    return null;
-}
-
-function variableLiveAcrossGC(suppressed, variable)
-{
-    // A variable is live across a GC if (1) it is used by an edge (as in, it
-    // was at least initialized), and (2) it is used after a GC in a successor
-    // edge.
-
-    for (var body of functionBodies)
-        body.minimumUse = 0;
-
-    for (var body of functionBodies) {
-        if (!("PEdge" in body))
-            continue;
-        for (var edge of body.PEdge) {
-            var usePoint = edgeUsesVariable(edge, variable, body);
-            // Example for !edgeKillsVariable:
-            //
-            //   JSObject* obj = NewObject();
-            //   cangc();
-            //   obj = NewObject();    <-- uses 'obj', but kills previous value
-            //
-            if (usePoint && !edgeKillsVariable(edge, variable)) {
-                // Found a use, possibly after a GC.
-                var call = findGCBeforeVariableUse(body, usePoint, suppressed, variable);
-                if (!call)
-                    continue;
-
-                call.afterGCUse = usePoint;
-                return call;
-            }
-        }
-    }
-    return null;
-}
-
-// An unrooted variable has its address stored in another variable via
-// assignment, or passed into a function that can GC. If the address is
-// assigned into some other variable, we can't track it to see if it is held
-// live across a GC. If it is passed into a function that can GC, then it's
-// sort of like a Handle to an unrooted location, and the callee could GC
-// before overwriting it or rooting it.
-function unsafeVariableAddressTaken(suppressed, variable)
-{
-    for (var body of functionBodies) {
-        if (!("PEdge" in body))
-            continue;
-        for (var edge of body.PEdge) {
-            if (edgeTakesVariableAddress(edge, variable, body)) {
-                if (edge.Kind == "Assign" || (!suppressed && edgeCanGC(edge)))
-                    return {body:body, ppoint:edge.Index[0]};
-            }
-        }
-    }
-    return null;
-}
-
-// Read out the brief (non-JSON, semi-human-readable) CFG description for the
-// given function and store it.
-function loadPrintedLines(functionName)
-{
-    assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
-    var lines = snarf(tmpfile).split('\n');
-
-    for (var body of functionBodies)
-        body.lines = [];
-
-    // Distribute lines of output to the block they originate from.
-    var currentBody = null;
-    for (var line of lines) {
-        if (/^block:/.test(line)) {
-            if (match = /:(loop#[\d#]+)/.exec(line)) {
-                var loop = match[1];
-                var found = false;
-                for (var body of functionBodies) {
-                    if (body.BlockId.Kind == "Loop" && body.BlockId.Loop == loop) {
-                        assert(!found);
-                        found = true;
-                        currentBody = body;
-                    }
-                }
-                assert(found);
-            } else {
-                for (var body of functionBodies) {
-                    if (body.BlockId.Kind == "Function")
-                        currentBody = body;
-                }
-            }
-        }
-        if (currentBody)
-            currentBody.lines.push(line);
-    }
-}
-
-function findLocation(body, ppoint, opts={brief: false})
-{
-    var location = body.PPoint[ppoint - 1].Location;
-    var file = location.CacheString;
-
-    if (file.indexOf(sourceRoot) == 0)
-        file = file.substring(sourceRoot.length);
-
-    if (opts.brief) {
-        var m = /.*\/(.*)/.exec(file);
-        if (m)
-            file = m[1];
-    }
-
-    return file + ":" + location.Line;
-}
-
-function locationLine(text)
-{
-    if (match = /:(\d+)$/.exec(text))
-        return match[1];
-    return 0;
-}
-
-function printEntryTrace(functionName, entry)
-{
-    var gcPoint = entry.gcInfo ? entry.gcInfo.ppoint : 0;
-
-    if (!functionBodies[0].lines)
-        loadPrintedLines(functionName);
-
-    while (entry) {
-        var ppoint = entry.ppoint;
-        var lineText = findLocation(entry.body, ppoint, {"brief": true});
-
-        var edgeText = "";
-        if (entry.why && entry.why.body == entry.body) {
-            // If the next point in the trace is in the same block, look for an edge between them.
-            var next = entry.why.ppoint;
-
-            if (!entry.body.edgeTable) {
-                var table = {};
-                entry.body.edgeTable = table;
-                for (var line of entry.body.lines) {
-                    if (match = /\((\d+,\d+),/.exec(line))
-                        table[match[1]] = line; // May be multiple?
-                }
-            }
-
-            edgeText = entry.body.edgeTable[ppoint + "," + next];
-            assert(edgeText);
-            if (ppoint == gcPoint)
-                edgeText += " [[GC call]]";
-        } else {
-            // Look for any outgoing edge from the chosen point.
-            for (var line of entry.body.lines) {
-                if (match = /\((\d+),/.exec(line)) {
-                    if (match[1] == ppoint) {
-                        edgeText = line;
-                        break;
-                    }
-                }
-            }
-            if (ppoint == entry.body.Index[1] && entry.body.BlockId.Kind == "Function")
-                edgeText += " [[end of function]]";
-        }
-
-        print("    " + lineText + (edgeText.length ? ": " + edgeText : ""));
-        entry = entry.why;
-    }
-}
-
-function isRootedType(type)
-{
-    return type.Kind == "CSU" && isRootedTypeName(type.Name);
-}
-
-function typeDesc(type)
-{
-    if (type.Kind == "CSU") {
-        return type.Name;
-    } else if ('Type' in type) {
-        var inner = typeDesc(type.Type);
-        if (type.Kind == 'Pointer')
-            return inner + '*';
-        else if (type.Kind == 'Array')
-            return inner + '[]';
-        else
-            return inner + '?';
-    } else {
-        return '???';
-    }
-}
-
-function processBodies(functionName)
-{
-    if (!("DefineVariable" in functionBodies[0]))
-        return;
-    var suppressed = (mangled(functionName) in suppressedFunctions);
-    for (var variable of functionBodies[0].DefineVariable) {
-        var name;
-        if (variable.Variable.Kind == "This")
-            name = "this";
-        else if (variable.Variable.Kind == "Return")
-            name = "<returnvalue>";
-        else
-            name = variable.Variable.Name[0];
-
-        if (isRootedType(variable.Type)) {
-            if (!variableLiveAcrossGC(suppressed, variable.Variable)) {
-                // The earliest use of the variable should be its constructor.
-                var lineText;
-                for (var body of functionBodies) {
-                    if (body.minimumUse) {
-                        var text = findLocation(body, body.minimumUse);
-                        if (!lineText || locationLine(lineText) > locationLine(text))
-                            lineText = text;
-                    }
-                }
-                print("\nFunction '" + functionName + "'" +
-                      " has unnecessary root '" + name + "' at " + lineText);
-            }
-        } else if (isUnrootedType(variable.Type)) {
-            var result = variableLiveAcrossGC(suppressed, variable.Variable);
-            if (result) {
-                var lineText = findLocation(result.gcInfo.body, result.gcInfo.ppoint);
-                print("\nFunction '" + functionName + "'" +
-                      " has unrooted '" + name + "'" +
-                      " of type '" + typeDesc(variable.Type) + "'" +
-                      " live across GC call " + result.gcInfo.name +
-                      " at " + lineText);
-                printEntryTrace(functionName, result);
-            }
-            result = unsafeVariableAddressTaken(suppressed, variable.Variable);
-            if (result) {
-                var lineText = findLocation(result.body, result.ppoint);
-                print("\nFunction '" + functionName + "'" +
-                      " takes unsafe address of unrooted '" + name + "'" +
-                      " at " + lineText);
-                printEntryTrace(functionName, {body:result.body, ppoint:result.ppoint});
-            }
-        }
-    }
-}
-
-if (batch == 1)
-    print("Time: " + new Date);
-
-var xdb = xdbLibrary();
-xdb.open("src_body.xdb");
-
-var minStream = xdb.min_data_stream()|0;
-var maxStream = xdb.max_data_stream()|0;
-
-var N = (maxStream - minStream) + 1;
-var start = Math.floor((batch - 1) / numBatches * N) + minStream;
-var start_next = Math.floor(batch / numBatches * N) + minStream;
-var end = start_next - 1;
-
-function process(name, json) {
-    functionName = name;
-    functionBodies = JSON.parse(json);
-
-    for (var body of functionBodies)
-        body.suppressed = [];
-    for (var body of functionBodies) {
-        for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor))
-            pbody.suppressed[id] = true;
-    }
-    processBodies(functionName);
-}
-
-if (theFunctionNameToFind) {
-    var data = xdb.read_entry(theFunctionNameToFind);
-    var json = data.readString();
-    process(theFunctionNameToFind, json);
-    xdb.free_string(data);
-    quit(0);
-}
-
-for (var nameIndex = start; nameIndex <= end; nameIndex++) {
-    var name = xdb.read_key(nameIndex);
-    var functionName = name.readString();
-    var data = xdb.read_entry(name);
-    xdb.free_string(name);
-    var json = data.readString();
-    try {
-        process(functionName, json);
-    } catch (e) {
-        printErr("Exception caught while handling " + functionName);
-        throw(e);
-    }
-    xdb.free_string(data);
-}
diff --git a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/annotations.js.orig b/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/annotations.js.orig
deleted file mode 100644
index 671f797..0000000
--- a/src/third_party/mozjs-45/js/src/devtools/rootAnalysis/annotations.js.orig
+++ /dev/null
@@ -1,379 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
-
-"use strict";
-
-// RAII types within which we should assume GC is suppressed, eg
-// AutoSuppressGC.
-var GCSuppressionTypes = [];
-
-// Ignore calls made through these function pointers
-var ignoreIndirectCalls = {
-    "mallocSizeOf" : true,
-    "aMallocSizeOf" : true,
-    "_malloc_message" : true,
-    "je_malloc_message" : true,
-    "chunk_dalloc" : true,
-    "chunk_alloc" : true,
-    "__conv" : true,
-    "__convf" : true,
-    "prerrortable.c:callback_newtable" : true,
-    "mozalloc_oom.cpp:void (* gAbortHandler)(size_t)" : true,
-};
-
-function indirectCallCannotGC(fullCaller, fullVariable)
-{
-    var caller = readable(fullCaller);
-
-    // This is usually a simple variable name, but sometimes a full name gets
-    // passed through. And sometimes that name is truncated. Examples:
-    //   _ZL13gAbortHandler|mozalloc_oom.cpp:void (* gAbortHandler)(size_t)
-    //   _ZL14pMutexUnlockFn|umutex.cpp:void (* pMutexUnlockFn)(const void*
-    var name = readable(fullVariable);
-
-    if (name in ignoreIndirectCalls)
-        return true;
-
-    if (name == "mapper" && caller == "ptio.c:pt_MapError")
-        return true;
-
-    if (name == "params" && caller == "PR_ExplodeTime")
-        return true;
-
-    if (name == "op" && /GetWeakmapKeyDelegate/.test(caller))
-        return true;
-
-    // hook called during script finalization which cannot GC.
-    if (/CallDestroyScriptHook/.test(caller))
-        return true;
-
-    // template method called during marking and hence cannot GC
-    if (name == "op" && caller.indexOf("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)") != -1)
-    {
-        return true;
-    }
-
-    return false;
-}
-
-// Ignore calls through functions pointers with these types
-var ignoreClasses = {
-    "JSStringFinalizer" : true,
-    "SprintfState" : true,
-    "SprintfStateStr" : true,
-    "JSLocaleCallbacks" : true,
-    "JSC::ExecutableAllocator" : true,
-    "PRIOMethods": true,
-    "XPCOMFunctions" : true, // I'm a little unsure of this one
-    "_MD_IOVector" : true,
-    "malloc_table_t": true, // replace_malloc
-    "malloc_hook_table_t": true, // replace_malloc
-};
-
-// Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
-// a function pointer field named FIELD.
-var ignoreCallees = {
-    "js::Class.trace" : true,
-    "js::Class.finalize" : true,
-    "JSRuntime.destroyPrincipals" : true,
-    "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC
-    "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC.
-    "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
-    "PLDHashTableOps.hashKey" : true,
-    "z_stream_s.zfree" : true,
-    "z_stream_s.zalloc" : true,
-    "GrGLInterface.fCallback" : true,
-    "std::strstreambuf._M_alloc_fun" : true,
-    "std::strstreambuf._M_free_fun" : true,
-    "struct js::gc::Callback<void (*)(JSRuntime*, void*)>.op" : true,
-};
-
-function fieldCallCannotGC(csu, fullfield)
-{
-    if (csu in ignoreClasses)
-        return true;
-    if (fullfield in ignoreCallees)
-        return true;
-    return false;
-}
-
-function ignoreEdgeUse(edge, variable, body)
-{
-    // Horrible special case for ignoring a false positive in xptcstubs: there
-    // is a local variable 'paramBuffer' holding an array of nsXPTCMiniVariant
-    // on the stack, which appears to be live across a GC call because its
-    // constructor is called when the array is initialized, even though the
-    // constructor is a no-op. So we'll do a very narrow exclusion for the use
-    // that incorrectly started the live range, which was basically "__temp_1 =
-    // paramBuffer".
-    //
-    // By scoping it so narrowly, we can detect most hazards that would be
-    // caused by modifications in the PrepareAndDispatch code. It just barely
-    // avoids having a hazard already.
-    if (('Name' in variable) && (variable.Name[0] == 'paramBuffer')) {
-        if (body.BlockId.Kind == 'Function' && body.BlockId.Variable.Name[0] == 'PrepareAndDispatch')
-            if (edge.Kind == 'Assign' && edge.Type.Kind == 'Pointer')
-                if (edge.Exp[0].Kind == 'Var' && edge.Exp[1].Kind == 'Var')
-                    if (edge.Exp[1].Variable.Kind == 'Local' && edge.Exp[1].Variable.Name[0] == 'paramBuffer')
-                        return true;
-    }
-
-    // Functions which should not be treated as using variable.
-    if (edge.Kind == "Call") {
-        var callee = edge.Exp[0];
-        if (callee.Kind == "Var") {
-            var name = callee.Variable.Name[0];
-            if (/~DebugOnly/.test(name))
-                return true;
-            if (/~ScopedThreadSafeStringInspector/.test(name))
-                return true;
-        }
-    }
-
-    return false;
-}
-
-function ignoreEdgeAddressTaken(edge)
-{
-    // Functions which may take indirect pointers to unrooted GC things,
-    // but will copy them into rooted locations before calling anything
-    // that can GC. These parameters should usually be replaced with
-    // handles or mutable handles.
-    if (edge.Kind == "Call") {
-        var callee = edge.Exp[0];
-        if (callee.Kind == "Var") {
-            var name = callee.Variable.Name[0];
-            if (/js::Invoke\(/.test(name))
-                return true;
-        }
-    }
-
-    return false;
-}
-
-// Return whether csu.method is one that we claim can never GC.
-function isSuppressedVirtualMethod(csu, method)
-{
-    return csu == "nsISupports" && (method == "AddRef" || method == "Release");
-}
-
-// Ignore calls of these functions (so ignore any stack containing these)
-var ignoreFunctions = {
-    "ptio.c:pt_MapError" : true,
-    "je_malloc_printf" : true,
-    "PR_ExplodeTime" : true,
-    "PR_ErrorInstallTable" : true,
-    "PR_SetThreadPrivate" : true,
-    "JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoSuppressGCAnalysis instead
-    "uint8 NS_IsMainThread()" : true,
-
-    // Has an indirect call under it by the name "__f", which seemed too
-    // generic to ignore by itself.
-    "void* std::_Locale_impl::~_Locale_impl(int32)" : true,
-
-    // Bug 1056410 - devirtualization prevents the standard nsISupports::Release heuristic from working
-    "uint32 nsXPConnect::Release()" : true,
-
-    // FIXME!
-    "NS_LogInit": true,
-    "NS_LogTerm": true,
-    "NS_LogAddRef": true,
-    "NS_LogRelease": true,
-    "NS_LogCtor": true,
-    "NS_LogDtor": true,
-    "NS_LogCOMPtrAddRef": true,
-    "NS_LogCOMPtrRelease": true,
-
-    // FIXME!
-    "NS_DebugBreak": true,
-
-    // These are a little overzealous -- these destructors *can* GC if they end
-    // up wrapping a pending exception. See bug 898815 for the heavyweight fix.
-    "void js::AutoCompartment::~AutoCompartment(int32)" : true,
-    "void JSAutoCompartment::~JSAutoCompartment(int32)" : true,
-
-    // Bug 948646 - the only thing AutoJSContext's constructor calls
-    // is an Init() routine whose entire body is covered with an
-    // AutoSuppressGCAnalysis. AutoSafeJSContext is the same thing, just with
-    // a different value for the 'aSafe' parameter.
-    "void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true,
-    "void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true,
-
-    // And these are workarounds to avoid even more analysis work,
-    // which would sadly still be needed even with bug 898815.
-    "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true,
-
-    // The nsScriptNameSpaceManager functions can't actually GC.  They
-    // just use a PLDHashTable which has function pointers, which makes the
-    // analysis think maybe they can.
-    "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupNavigatorName(nsAString_internal*)": true,
-    "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupName(nsAString_internal*, uint16**)": true,
-
-    // Similar to heap snapshot mock classes, and GTests below. This posts a
-    // synchronous runnable when a GTest fails, and we are pretty sure that the
-    // particular runnable it posts can't even GC, but the analysis isn't
-    // currently smart enough to determine that. In either case, this is (a)
-    // only in GTests, and (b) only when the Gtest has already failed. We have
-    // static and dynamic checks for no GC in the non-test code, and in the test
-    // code we fall back to only the dynamic checks.
-    "void test::RingbufferDumper::OnTestPartResult(testing::TestPartResult*)" : true,
-
-    "float64 JS_GetCurrentEmbedderTime()" : true,
-
-    "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
-    "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
-};
-
-function isProtobuf(name)
-{
-    return name.match(/\bgoogle::protobuf\b/) ||
-           name.match(/\bmozilla::devtools::protobuf\b/);
-}
-
-function isHeapSnapshotMockClass(name)
-{
-    return name.match(/\bMockWriter\b/) ||
-           name.match(/\bMockDeserializedNode\b/);
-}
-
-function isGTest(name)
-{
-    return name.match(/\btesting::/);
-}
-
-function ignoreGCFunction(mangled)
-{
-    assert(mangled in readableNames);
-    var fun = readableNames[mangled][0];
-
-    if (fun in ignoreFunctions)
-        return true;
-
-    // The protobuf library, and [de]serialization code generated by the
-    // protobuf compiler, uses a _ton_ of function pointers but they are all
-    // internal. Easiest to just ignore that mess here.
-    if (isProtobuf(fun))
-        return true;
-
-    // Ignore anything that goes through heap snapshot GTests or mocked classes
-    // used in heap snapshot GTests. GTest and GMock expose a lot of virtual
-    // methods and function pointers that could potentially GC after an
-    // assertion has already failed (depending on user-provided code), but don't
-    // exhibit that behavior currently. For non-test code, we have dynamic and
-    // static checks that ensure we don't GC. However, for test code we opt out
-    // of static checks here, because of the above stated GMock/GTest issues,
-    // and rely on only the dynamic checks provided by AutoAssertCannotGC.
-    if (isHeapSnapshotMockClass(fun) || isGTest(fun))
-        return true;
-
-    // Templatized function
-    if (fun.indexOf("void nsCOMPtr<T>::Assert_NoQueryNeeded()") >= 0)
-        return true;
-
-    // These call through an 'op' function pointer.
-    if (fun.indexOf("js::WeakMap<Key, Value, HashPolicy>::getDelegate(") >= 0)
-        return true;
-
-    // XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC.
-    if (/refillFreeList/.test(fun) && /\(js::AllowGC\)0u/.test(fun))
-        return true;
-    return false;
-}
-
-function stripUCSAndNamespace(name)
-{
-    name = name.replace(/(struct|class|union|const) /g, "");
-    name = name.replace(/(js::ctypes::|js::|JS::|mozilla::dom::|mozilla::)/g, "");
-    return name;
-}
-
-function isRootedGCTypeName(name)
-{
-    return (name == "JSAddonId");
-}
-
-function isRootedGCPointerTypeName(name)
-{
-    name = stripUCSAndNamespace(name);
-
-    if (name.startsWith('MaybeRooted<'))
-        return /\(js::AllowGC\)1u>::RootType/.test(name);
-
-    if (name == "ErrorResult" ||
-        name == "JSErrorResult" ||
-        name == "WrappableJSErrorResult" ||
-        name == "frontend::TokenStream" ||
-        name == "frontend::TokenStream::Position" ||
-        name == "ModuleValidator")
-    {
-        return true;
-    }
-
-    return name.startsWith('Rooted') || name.startsWith('PersistentRooted');
-}
-
-function isRootedTypeName(name)
-{
-    return isRootedGCTypeName(name) || isRootedGCPointerTypeName(name);
-}
-
-function isUnsafeStorage(typeName)
-{
-    typeName = stripUCSAndNamespace(typeName);
-    return typeName.startsWith('UniquePtr<');
-}
-
-function isSuppressConstructor(funcName)
-{
-    let [ qualifiedFunction, unqualifiedFunction ] = funcName;
-    let [ mangled, unmangled ] = splitFunction(qualifiedFunction);
-    if (!mangled.match(/C\dE/))
-        return false; // Constructors have C1E (or C4E etc.) in their mangled names
-    let m = unmangled.match(/((\w+)(<.*>)?)::\2\(/); // Foo<T>::Foo
-    if (!m)
-        return false;
-    let classType = m[1]; // Foo<T>
-    for (let type of GCSuppressionTypes) {
-        // type is something like class js::Foo<T>
-        if (type.endsWith(classType)) {
-            // Screen out js::OtherFoo
-            if (type == classType || type.endsWith("::" + classType) || type.endsWith(" " + classType))
-                return true;
-        }
-    }
-    return false;
-}
-
-// nsISupports subclasses' methods may be scriptable (or overridden
-// via binary XPCOM), and so may GC. But some fields just aren't going
-// to get overridden with something that can GC.
-function isOverridableField(initialCSU, csu, field)
-{
-    if (csu != 'nsISupports')
-        return false;
-    if (field == 'GetCurrentJSContext')
-        return false;
-    if (field == 'IsOnCurrentThread')
-        return false;
-    if (field == 'GetNativeContext')
-        return false;
-    if (field == "GetGlobalJSObject")
-        return false;
-    if (field == "GetIsMainThread")
-        return false;
-    if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject')
-        return false;
-    if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext')
-        return false;
-    if (initialCSU == 'nsIScriptContext') {
-        if (field == 'GetWindowProxy' || field == 'GetWindowProxyPreserveColor')
-            return false;
-    }
-    return true;
-}
-
-function listNonGCPointers() {
-    return [
-        // Safe only because jsids are currently only made from pinned strings.
-        'NPIdentifier',
-    ];
-}
diff --git a/src/third_party/mozjs-45/js/src/frontend/BytecodeCompiler.cpp.rej b/src/third_party/mozjs-45/js/src/frontend/BytecodeCompiler.cpp.rej
deleted file mode 100644
index b4299cb..0000000
--- a/src/third_party/mozjs-45/js/src/frontend/BytecodeCompiler.cpp.rej
+++ /dev/null
@@ -1,109 +0,0 @@
---- BytecodeCompiler.cpp
-+++ BytecodeCompiler.cpp
-@@ -22,16 +22,36 @@
- 
- #include "frontend/Parser-inl.h"
- #include "vm/ScopeObject-inl.h"
- 
- using namespace js;
- using namespace js::frontend;
- using mozilla::Maybe;
- 
-+class MOZ_STACK_CLASS AutoCompilationTraceLogger
-+{
-+  public:
-+    AutoCompilationTraceLogger(ExclusiveContext* cx, const TraceLoggerTextId id);
-+
-+  private:
-+    TraceLoggerThread* logger;
-+    TraceLoggerEvent event;
-+    AutoTraceLog scriptLogger;
-+    AutoTraceLog typeLogger;
-+};
-+
-+AutoCompilationTraceLogger::AutoCompilationTraceLogger(ExclusiveContext* cx, const TraceLoggerTextId id)
-+  : logger(cx->isJSContext() ? TraceLoggerForMainThread(cx->asJSContext()->runtime())
-+                             : TraceLoggerForCurrentThread()),
-+    event(logger, TraceLogger_AnnotateScripts),
-+    scriptLogger(logger, event),
-+    typeLogger(logger, id)
-+{}
-+
- static bool
- CheckLength(ExclusiveContext* cx, SourceBufferHolder& srcBuf)
- {
-     // Note this limit is simply so we can store sourceStart and sourceEnd in
-     // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
-     // is using size_t internally already.
-     if (srcBuf.length() > UINT32_MAX) {
-         if (cx->isJSContext())
-@@ -218,24 +238,17 @@ frontend::CompileScript(ExclusiveContext
-                         JSString* source_ /* = nullptr */,
-                         unsigned staticLevel /* = 0 */,
-                         SourceCompressionTask* extraSct /* = nullptr */)
- {
-     MOZ_ASSERT(srcBuf.get());
- 
-     RootedString source(cx, source_);
- 
--    js::TraceLoggerThread* logger = nullptr;
--    if (cx->isJSContext())
--        logger = TraceLoggerForMainThread(cx->asJSContext()->runtime());
--    else
--        logger = TraceLoggerForCurrentThread();
--    js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options);
--    js::AutoTraceLog scriptLogger(logger, event);
--    js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileScript);
-+    AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileScript);
- 
-     /*
-      * The scripted callerFrame can only be given for compile-and-go scripts
-      * and non-zero static level requires callerFrame.
-      */
-     MOZ_ASSERT_IF(evalCaller, options.isRunOnce);
-     MOZ_ASSERT_IF(evalCaller, options.forEval);
-     MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption);
-@@ -467,20 +480,17 @@ frontend::CompileLazyFunction(JSContext*
- 
-     CompileOptions options(cx, lazy->version());
-     options.setMutedErrors(lazy->mutedErrors())
-            .setFileAndLine(lazy->filename(), lazy->lineno())
-            .setColumn(lazy->column())
-            .setNoScriptRval(false)
-            .setSelfHostingMode(false);
- 
--    js::TraceLoggerThread* logger = js::TraceLoggerForMainThread(cx->runtime());
--    js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options);
--    js::AutoTraceLog scriptLogger(logger, event);
--    js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileLazy);
-+    AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileLazy);
- 
-     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
-                                     /* foldConstants = */ true, nullptr, lazy);
-     if (!parser.checkOptions())
-         return false;
- 
-     uint32_t staticLevel = lazy->staticLevel(cx);
- 
-@@ -534,20 +544,17 @@ frontend::CompileLazyFunction(JSContext*
- // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
- static bool
- CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyCompileOptions& options,
-                     const AutoNameVector& formals, SourceBufferHolder& srcBuf,
-                     HandleObject enclosingStaticScope, GeneratorKind generatorKind)
- {
-     MOZ_ASSERT(!options.isRunOnce);
- 
--    js::TraceLoggerThread* logger = js::TraceLoggerForMainThread(cx->runtime());
--    js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options);
--    js::AutoTraceLog scriptLogger(logger, event);
--    js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileFunction);
-+    AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileFunction);
- 
-     // FIXME: make Function pass in two strings and parse them as arguments and
-     // ProgramElements respectively.
- 
-     if (!CheckLength(cx, srcBuf))
-         return false;
- 
-     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
diff --git a/src/third_party/mozjs-45/js/src/gc/Marking.cpp.rej b/src/third_party/mozjs-45/js/src/gc/Marking.cpp.rej
deleted file mode 100644
index 07083c5..0000000
--- a/src/third_party/mozjs-45/js/src/gc/Marking.cpp.rej
+++ /dev/null
@@ -1,83 +0,0 @@
---- Marking.cpp
-+++ Marking.cpp
-@@ -587,68 +587,70 @@ DispatchToTracer(JSTracer* trc, T* thing
-     DoCallback(trc->asCallbackTracer(), thingp, name);
- }
- 
- 
- /*** GC Marking Interface *************************************************************************/
- 
- namespace js {
- 
-+typedef bool DoNothingMarkingType;
-+
- template <typename T>
- struct LinearlyMarkedEphemeronKeyType {
--    typedef bool Type; // used to instantiate a do-nothing markPotentialEphemeronKeyHelper
-+    typedef DoNothingMarkingType Type;
- };
- 
- // For now, we only handle JSObject* keys, but the linear time algorithm can be
--// easily extended by adding in more types here, then having them call
--// markPotentialEphemeronKey in their GCMarker::traverse instantiation.
-+// easily extended by adding in more types here, then instantiating a
-+// GCMarker::traverse method that calls markPotentialEphemeronKey.
- template <>
- struct LinearlyMarkedEphemeronKeyType<JSObject*> {
-     typedef JSObject* Type;
- };
- 
- // Iterate over the vector. It should not be appended to during iteration
- // because the key is already marked, and even in cases where we have a
- // multipart key, we should only be inserting entries for the unmarked
- // portions.
- void
--GCMarker::markEphemeronValues(gc::Cell* oldKey, WeakEntryVector& values)
-+GCMarker::markEphemeronValues(gc::Cell* markedCell, WeakEntryVector& values)
- {
-     size_t initialLen = values.length();
-     for (size_t i = 0; i < initialLen; i++)
--        values[i]->maybeMarkEntry(this, oldKey);
-+        values[i].weakmap->maybeMarkEntry(this, markedCell, values[i].key);
-     MOZ_ASSERT(values.length() == initialLen);
- }
- 
- template <typename T>
- void
--GCMarker::markPotentialEphemeronKeyHelper(T oldThing)
-+GCMarker::markPotentialEphemeronKeyHelper(T markedThing)
- {
-     if (!isWeakMarkingTracer())
-         return;
- 
--    auto weakValues = weakKeys.get(JS::GCCellPtr(oldThing));
-+    auto weakValues = weakKeys.get(JS::GCCellPtr(markedThing));
-     if (!weakValues)
-         return;
- 
--    markEphemeronValues(oldThing, weakValues->value);
-+    markEphemeronValues(markedThing, weakValues->value);
-     weakValues->value.clear(); // If key address is reused, it should do nothing
- }
- 
- template <>
- void
- GCMarker::markPotentialEphemeronKeyHelper(bool)
- {
- }
- 
- template <typename T>
- void
--GCMarker::markPotentialEphemeronKey(T* oldThing)
-+GCMarker::markPotentialEphemeronKey(T* thing)
- {
--    markPotentialEphemeronKeyHelper<typename LinearlyMarkedEphemeronKeyType<T*>::Type>(oldThing);
-+    markPotentialEphemeronKeyHelper<typename LinearlyMarkedEphemeronKeyType<T*>::Type>(thing);
- }
- 
- } // namespace js
- 
- template <typename T>
- static inline bool
- MustSkipMarking(T thing)
- {
diff --git a/src/third_party/mozjs-45/js/src/gc/Marking.h.rej b/src/third_party/mozjs-45/js/src/gc/Marking.h.rej
deleted file mode 100644
index 97cb72c..0000000
--- a/src/third_party/mozjs-45/js/src/gc/Marking.h.rej
+++ /dev/null
@@ -1,89 +0,0 @@
---- Marking.h
-+++ Marking.h
-@@ -11,16 +11,17 @@
- #include "mozilla/HashFunctions.h"
- 
- #include "jsfriendapi.h"
- 
- #include "ds/OrderedHashTable.h"
- #include "gc/Heap.h"
- #include "gc/Tracer.h"
- #include "js/GCAPI.h"
-+#include "js/HeapAPI.h"
- #include "js/SliceBudget.h"
- #include "js/TracingAPI.h"
- 
- class JSLinearString;
- class JSRope;
- namespace js {
- class BaseShape;
- class GCMarker;
-@@ -141,17 +142,27 @@ namespace gc {
- struct WeakKeyTableHashPolicy {
-     typedef JS::GCCellPtr Lookup;
-     static HashNumber hash(const Lookup& v) { return mozilla::HashGeneric(v.asCell()); }
-     static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; }
-     static bool isEmpty(const JS::GCCellPtr& v) { return !v; }
-     static void makeEmpty(JS::GCCellPtr* vp) { *vp = nullptr; }
- };
- 
--typedef Vector<WeakMapBase*, 2, js::SystemAllocPolicy> WeakEntryVector;
-+struct WeakMarkable {
-+    WeakMapBase* weakmap;
-+    JS::GCCellPtr key;
-+
-+    WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr&& keyArg)
-+      : weakmap(weakmapArg), key(keyArg) {}
-+    WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr keyArg)
-+      : weakmap(weakmapArg), key(keyArg) {}
-+};
-+
-+typedef Vector<WeakMarkable, 2, js::SystemAllocPolicy> WeakEntryVector;
- 
- typedef OrderedHashMap<JS::GCCellPtr,
-                        WeakEntryVector,
-                        WeakKeyTableHashPolicy,
-                        js::SystemAllocPolicy> WeakKeyTable;
- 
- } /* namespace gc */
- 
-@@ -203,16 +214,20 @@ class GCMarker : public JSTracer
-     }
- 
-     void endWeakMarkingPhase() {
-         MOZ_ASSERT_IF(weakMapAction() == ExpandWeakMaps, tag_ == TracerKindTag::WeakMarking);
-         tag_ = TracerKindTag::Marking;
-         weakKeys.clear();
-     }
- 
-+    void abortLinearWeakMarking() {
-+        endWeakMarkingPhase();
-+    }
-+
-     void delayMarkingArena(gc::ArenaHeader* aheader);
-     void delayMarkingChildren(const void* thing);
-     void markDelayedChildren(gc::ArenaHeader* aheader);
-     bool markDelayedChildren(SliceBudget& budget);
-     bool hasDelayedChildren() const {
-         return !!unmarkedArenaStackTop;
-     }
- 
-@@ -228,17 +243,17 @@ class GCMarker : public JSTracer
- 
- #ifdef DEBUG
-     bool shouldCheckCompartments() { return strictCompartmentChecking; }
- #endif
- 
-     /* This is public exclusively for ScanRope. */
-     MarkStack stack;
-     
--    void markEphemeronValues(gc::Cell* oldKey, gc::WeakEntryVector& entry);
-+    void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry);
- 
-     /*
-      * Mapping from not yet marked keys to a vector of all values that the key
-      * maps to in any live weak map.
-      */
-     gc::WeakKeyTable weakKeys;
- 
-   private:
diff --git a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-01.js.rej b/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-01.js.rej
deleted file mode 100644
index 55e21a6..0000000
--- a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-01.js.rej
+++ /dev/null
@@ -1,40 +0,0 @@
---- weak-marking-01.js
-+++ weak-marking-01.js
-@@ -85,15 +85,35 @@ function deadKeys() {
- 
-   gc();
-   assertEq(finalizeCount(), initialCount + 2);
-   assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0);
- }
- 
- deadKeys();
- 
-+// The weakKeys table has to grow if it encounters enough new unmarked weakmap
-+// keys. Trigger this to happen during weakmap marking.
-+//
-+// There's some trickiness involved in getting it to test the right thing,
-+// because if a key is marked before the weakmap, then it won't get entered
-+// into the weakKeys table. This chains through multiple weakmap layers to
-+// ensure that the objects can't get marked before the weakmaps.
- function weakKeysRealloc() {
-   var wm1 = new WeakMap;
-+  var wm2 = new WeakMap;
-+  var wm3 = new WeakMap;
-   var obj1 = {'name': 'obj1'};
--  var wm2 = new WeakMap;
-+  var obj2 = {'name': 'obj2'};
-   wm1.set(obj1, wm2);
--  
-+  wm2.set(obj2, wm3);
-+  for (var i = 0; i < 10000; i++) {
-+    wm3.set(Object.create(null), wm2);
-+  }
-+  wm3.set(Object.create(null), makeFinalizeObserver());
-+  wm2 = undefined;
-+  wm3 = undefined;
-+  obj2 = undefined;
-+
-+  var initialCount = finalizeCount();
-+  gc();
-+  assertEq(finalizeCount(), initialCount + 1);
- }
diff --git a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-02.js.rej b/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-02.js.rej
deleted file mode 100644
index cd65b0e..0000000
--- a/src/third_party/mozjs-45/js/src/jit-test/tests/gc/weak-marking-02.js.rej
+++ /dev/null
@@ -1,66 +0,0 @@
---- weak-marking-02.js
-+++ weak-marking-02.js
-@@ -0,0 +1,63 @@
-+// These tests will be using object literals as keys, and we want some of them
-+// to be dead after being inserted into a WeakMap. That means we must wrap
-+// everything in functions because it seems like the toplevel script hangs onto
-+// its object literals.
-+
-+// Cross-compartment WeakMap keys work by storing a cross-compartment wrapper
-+// in the WeakMap, and the actual "delegate" object in the target compartment
-+// is the thing whose liveness is checked.
-+
-+var g2 = newGlobal();
-+g2.eval('function genObj(name) { return {"name": name} }');
-+
-+function basicSweeping() {
-+  var wm1 = new WeakMap();
-+  wm1.set({'name': 'obj1'}, {'name': 'val1'});
-+  var hold = g2.genObj('obj2');
-+  wm1.set(hold, {'name': 'val2'});
-+  wm1.set({'name': 'obj3'}, {'name': 'val3'});
-+  var obj4 = g2.genObj('obj4');
-+  wm1.set(obj4, {'name': 'val3'});
-+  obj4 = undefined;
-+
-+  gc();
-+  assertEq(wm1.get(hold).name, 'val2');
-+  assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
-+}
-+
-+basicSweeping();
-+
-+// Same, but behind an additional WM layer, to avoid ordering problems (not
-+// that I've checked that basicSweeping even has any problems.)
-+function basicSweeping2() {
-+  var wm1 = new WeakMap();
-+  wm1.set({'name': 'obj1'}, {'name': 'val1'});
-+  var hold = g2.genObj('obj2');
-+  wm1.set(hold, {'name': 'val2'});
-+  wm1.set({'name': 'obj3'}, {'name': 'val3'});
-+  var obj4 = g2.genObj('obj4');
-+  wm1.set(obj4, {'name': 'val3'});
-+  obj4 = undefined;
-+
-+  var base1 = {'name': 'base1'};
-+  var base2 = {'name': 'base2'};
-+  var wm_base1 = new WeakMap();
-+  var wm_base2 = new WeakMap();
-+  wm_base1.set(base1, wm_base2);
-+  wm_base2.set(base2, wm1);
-+  wm1 = wm_base2 = undefined;
-+  
-+  gc();
-+
-+  assertEq(nondeterministicGetWeakMapKeys(wm_base1).length, 1);
-+  wm_base2 = wm_base1.get(base1);
-+  assertEq(nondeterministicGetWeakMapKeys(wm_base2).length, 1);
-+  assertEq(nondeterministicGetWeakMapKeys(wm_base1)[0], base1);
-+  assertEq(nondeterministicGetWeakMapKeys(wm_base2)[0], base2);
-+  wm_base2 = wm_base1.get(base1);
-+  wm1 = wm_base2.get(base2);
-+  assertEq(wm1.get(hold).name, 'val2');
-+  assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
-+}
-+
-+basicSweeping2();
diff --git a/src/third_party/mozjs-45/js/src/jsweakmap.h.rej b/src/third_party/mozjs-45/js/src/jsweakmap.h.rej
deleted file mode 100644
index bcd1675..0000000
--- a/src/third_party/mozjs-45/js/src/jsweakmap.h.rej
+++ /dev/null
@@ -1,124 +0,0 @@
---- jsweakmap.h
-+++ jsweakmap.h
-@@ -78,17 +78,17 @@ class WeakMapBase {
- 
-     // Restore information about which weak maps are marked for many compartments.
-     static void restoreCompartmentMarkedWeakMaps(WeakMapSet& markedWeakMaps);
- 
-     // Remove a weakmap from its compartment's weakmaps list.
-     static void removeWeakMapFromList(WeakMapBase* weakmap);
- 
-     // Object keys must participate in ephemeron marking by overriding this method.
--    virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* l) = 0;
-+    virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
- 
-   protected:
-     // Instance member functions called by the above. Instantiations of WeakMap override
-     // these with definitions appropriate for their Key and Value types.
-     virtual void nonMarkingTraceKeys(JSTracer* tracer) = 0;
-     virtual void nonMarkingTraceValues(JSTracer* tracer) = 0;
-     virtual bool markIteratively(JSTracer* tracer) = 0;
-     virtual void markEphemeronEntries(JSTracer* trc) = 0;
-@@ -160,65 +160,76 @@ class WeakMap : public HashMap<Key, Valu
-         return p;
-     }
- 
-     // The WeakMap and some part of the key are marked. Mark the value if the
-     // key is "fully marked" according to the exact semantics of this WeakMap.
-     // (For a standard WeakMap, the key is either marked or not, so just mark
-     // the value here. But a subclass might have multipart keys with more
-     // complicated semantics.)
--    virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* l)
-+    virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr origKey)
-     {
-         MOZ_ASSERT(marked);
- 
-+        gc::Cell* l = origKey.asCell();
-         Ptr p = Base::lookup(reinterpret_cast<const Lookup&>(l));
-         Key key(p->key());
--        if (gc::IsMarked(&key))
-+        if (gc::IsMarked(&key)) {
-             TraceEdge(trc, &p->value(), "ephemeron value");
-+        } else if (keyNeedsMark(key)) {
-+            TraceEdge(trc, &p->value(), "WeakMap ephemeron value");
-+            TraceEdge(trc, &key, "proxy-preserved WeakMap ephemeron key");
-+            MOZ_ASSERT(key == p->key()); // No moving
-+        }
-         key.unsafeSet(nullptr);
-     }
- 
-   protected:
-+    static void addWeakEntry(JSTracer* trc, JS::GCCellPtr key, gc::WeakMarkable markable)
-+    {
-+        GCMarker& marker = *static_cast<GCMarker*>(trc);
-+
-+        auto p = marker.weakKeys.get(key);
-+        if (p) {
-+            gc::WeakEntryVector& weakEntries = p->value;
-+            if (!weakEntries.append(Move(markable)))
-+                marker.abortLinearWeakMarking();
-+        } else {
-+            gc::WeakEntryVector weakEntries;
-+            JS_ALWAYS_TRUE(weakEntries.append(Move(markable)));
-+            if (!marker.weakKeys.put(JS::GCCellPtr(key), Move(weakEntries)))
-+                marker.abortLinearWeakMarking();
-+        }
-+    }
-+
-     virtual void markEphemeronEntries(JSTracer* trc) {
--        gc::WeakKeyTable& weakKeys = static_cast<GCMarker*>(trc)->weakKeys;
--
-         MOZ_ASSERT(marked);
-         for (Enum e(*this); !e.empty(); e.popFront()) {
-             Key key(e.front().key());
- 
-             // If the entry is live, ensure its key and value are marked.
-             if (gc::IsMarked(&key)) {
-                 (void) markValue(trc, &e.front().value());
--                if (e.front().key() != key)
--                    entryMoved(e, key);
-+                MOZ_ASSERT(key == e.front().key()); // No moving
-             } else if (keyNeedsMark(key)) {
-                 TraceEdge(trc, &e.front().value(), "WeakMap entry value");
-                 TraceEdge(trc, &key, "proxy-preserved WeakMap entry key");
--                if (e.front().key() != key)
--                    entryMoved(e, key);
-+                MOZ_ASSERT(key == e.front().key()); // No moving
-             } else {
-                 // Entry is not known to be live yet. Record it in the list of
--                // weak keys. Or rather, record a pointer to this weakmap, so
--                // we can look the key up again when we need to (to allow
--                // incremental weak marking.)
--                auto p = weakKeys.get(JS::GCCellPtr(key));
--                if (p) {
--                    gc::WeakEntryVector& weakEntries = p->value;
--                    if (!weakEntries.append(this)) {
--                        static_cast<GCMarker*>(trc)->endWeakMarkingPhase();
--                        break;
--                    }
--                } else {
--                    gc::WeakEntryVector weakEntries;
--                    JS_ALWAYS_TRUE(weakEntries.append(this));
--                    if (!weakKeys.put(JS::GCCellPtr(key), Move(weakEntries))) {
--                        static_cast<GCMarker*>(trc)->endWeakMarkingPhase();
--                        break;
--                    }
--                }
-+                // weak keys. Or rather, record this weakmap and the lookup key
-+                // so we can repeat the lookup when we need to (to allow
-+                // incremental weak marking, we can't just store a pointer to
-+                // the entry.) Also record the delegate, if any, because
-+                // marking the delegate must also mark the entry.
-+                JS::GCCellPtr weakKey(key);
-+                gc::WeakMarkable markable(this, weakKey);
-+                addWeakEntry(trc, weakKey, markable);
-+                if (JSObject* delegate = getDelegate(key))
-+                    addWeakEntry(trc, JS::GCCellPtr(delegate), markable);
-             }
-             key.unsafeSet(nullptr);
-         }
-     }
- 
-   private:
-     void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); }
-     void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
diff --git a/src/third_party/mozjs-45/js/src/shell/OSObject.cppTNHQfd b/src/third_party/mozjs-45/js/src/shell/OSObject.cppTNHQfd
deleted file mode 100644
index 4744d38..0000000
--- a/src/third_party/mozjs-45/js/src/shell/OSObject.cppTNHQfd
+++ /dev/null
@@ -1,443 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// OSObject.h - os object for exposing posix system calls in the JS shell
-
-#include "shell/OSObject.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#ifdef XP_WIN
-#include <direct.h>
-#include <process.h>
-#include <string.h>
-#else
-#include <sys/wait.h>
-#include <unistd.h>
-#endif
-
-#include "jsapi.h"
-// For JSFunctionSpecWithHelp
-#include "jsfriendapi.h"
-#include "jsobj.h"
-#ifdef XP_WIN
-# include "jswin.h"
-#endif
-#include "jswrapper.h"
-
-#include "js/Conversions.h"
-#include "shell/jsshell.h"
-#include "vm/TypedArrayObject.h"
-
-#include "jsobjinlines.h"
-
-#ifdef XP_WIN
-# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
-# define getcwd _getcwd
-#else
-# include <libgen.h>
-#endif
-
-using js::shell::RCFile;
-
-static RCFile** gErrFilePtr = nullptr;
-static RCFile** gOutFilePtr = nullptr;
-
-using namespace JS;
-
-namespace js {
-namespace shell {
-
-/*
- * Resolve a (possibly) relative filename to an absolute path. If
- * |scriptRelative| is true, then the result will be relative to the directory
- * containing the currently-running script, or the current working directory if
- * the currently-running script is "-e" (namely, you're using it from the
- * command line.) Otherwise, it will be relative to the current working
- * directory.
- */
-JSString*
-ResolvePath(JSContext* cx, HandleString filenameStr, PathResolutionMode resolveMode)
-{
-    JSAutoByteString filename(cx, filenameStr);
-    if (!filename)
-        return nullptr;
-
-    const char* pathname = filename.ptr();
-    if (pathname[0] == '/')
-        return filenameStr;
-#ifdef XP_WIN
-    // Various forms of absolute paths per http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
-    // "\..."
-    if (pathname[0] == '\\')
-        return filenameStr;
-    // "C:\..."
-    if (strlen(pathname) > 3 && isalpha(pathname[0]) && pathname[1] == ':' && pathname[2] == '\\')
-        return filenameStr;
-    // "\\..."
-    if (strlen(pathname) > 2 && pathname[1] == '\\' && pathname[2] == '\\')
-        return filenameStr;
-#endif
-
-    /* Get the currently executing script's name. */
-    JS::AutoFilename scriptFilename;
-    if (!DescribeScriptedCaller(cx, &scriptFilename))
-        return nullptr;
-
-    if (!scriptFilename.get())
-        return nullptr;
-
-    if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0)
-        resolveMode = RootRelative;
-
-    static char buffer[PATH_MAX+1];
-    if (resolveMode == ScriptRelative) {
-#ifdef XP_WIN
-        // The docs say it can return EINVAL, but the compiler says it's void
-        _splitpath(scriptFilename.get(), nullptr, buffer, nullptr, nullptr);
-#else
-        strncpy(buffer, scriptFilename.get(), PATH_MAX+1);
-        if (buffer[PATH_MAX] != '\0')
-            return nullptr;
-
-        // dirname(buffer) might return buffer, or it might return a
-        // statically-allocated string
-        memmove(buffer, dirname(buffer), strlen(buffer) + 1);
-#endif
-    } else {
-        const char* cwd = getcwd(buffer, PATH_MAX);
-        if (!cwd)
-            return nullptr;
-    }
-
-    size_t len = strlen(buffer);
-    buffer[len] = '/';
-    strncpy(buffer + len + 1, pathname, sizeof(buffer) - (len+1));
-    if (buffer[PATH_MAX] != '\0')
-        return nullptr;
-
-    return JS_NewStringCopyZ(cx, buffer);
-}
-
-static JSObject*
-FileAsTypedArray(JSContext* cx, const char* pathname)
-{
-    FILE* file = fopen(pathname, "rb");
-    if (!file) {
-        JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
-        return nullptr;
-    }
-    AutoCloseInputFile autoClose(file);
-
-    RootedObject obj(cx);
-    if (fseek(file, 0, SEEK_END) != 0) {
-        JS_ReportError(cx, "can't seek end of %s", pathname);
-    } else {
-        size_t len = ftell(file);
-        if (fseek(file, 0, SEEK_SET) != 0) {
-            JS_ReportError(cx, "can't seek start of %s", pathname);
-        } else {
-            obj = JS_NewUint8Array(cx, len);
-            if (!obj)
-                return nullptr;
-            char* buf = (char*) obj->as<js::TypedArrayObject>().viewData();
-            size_t cc = fread(buf, 1, len, file);
-            if (cc != len) {
-                JS_ReportError(cx, "can't read %s: %s", pathname,
-                               (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
-                obj = nullptr;
-            }
-        }
-    }
-    return obj;
-}
-
-static bool
-ReadFile(JSContext* cx, unsigned argc, jsval* vp, bool scriptRelative)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1 || args.length() > 2) {
-        JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr,
-                             args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
-                             "snarf");
-        return false;
-    }
-
-    if (!args[0].isString() || (args.length() == 2 && !args[1].isString())) {
-        JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "snarf");
-        return false;
-    }
-
-    RootedString givenPath(cx, args[0].toString());
-    RootedString str(cx, js::shell::ResolvePath(cx, givenPath, scriptRelative ? ScriptRelative : RootRelative));
-    if (!str)
-        return false;
-
-    JSAutoByteString filename(cx, str);
-    if (!filename)
-        return false;
-
-    if (args.length() > 1) {
-        JSString* opt = JS::ToString(cx, args[1]);
-        if (!opt)
-            return false;
-        bool match;
-        if (!JS_StringEqualsAscii(cx, opt, "binary", &match))
-            return false;
-        if (match) {
-            JSObject* obj;
-            if (!(obj = FileAsTypedArray(cx, filename.ptr())))
-                return false;
-            args.rval().setObject(*obj);
-            return true;
-        }
-    }
-
-    if (!(str = FileAsString(cx, filename.ptr())))
-        return false;
-    args.rval().setString(str);
-    return true;
-}
-
-static bool
-osfile_readFile(JSContext* cx, unsigned argc, jsval* vp)
-{
-    return ReadFile(cx, argc, vp, false);
-}
-
-static bool
-osfile_readRelativeToScript(JSContext* cx, unsigned argc, jsval* vp)
-{
-    return ReadFile(cx, argc, vp, true);
-}
-
-static bool
-Redirect(JSContext* cx, FILE* fp, HandleString relFilename)
-{
-    RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
-    if (!filename)
-        return false;
-    JSAutoByteString filenameABS(cx, filename);
-    if (!filenameABS)
-        return false;
-    if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) {
-        JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
-        return false;
-    }
-    return true;
-}
-
-static bool
-osfile_redirect(JSContext* cx, unsigned argc, jsval* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1 || args.length() > 2) {
-        JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
-        return false;
-    }
-
-    if (args[0].isString()) {
-        RootedString stdoutPath(cx, args[0].toString());
-        if (!stdoutPath)
-            return false;
-        if (!Redirect(cx, stdout, stdoutPath))
-            return false;
-    }
-
-    if (args.length() > 1 && args[1].isString()) {
-        RootedString stderrPath(cx, args[1].toString());
-        if (!stderrPath)
-            return false;
-        if (!Redirect(cx, stderr, stderrPath))
-            return false;
-    }
-
-    args.rval().setUndefined();
-    return true;
-}
-<<<<<<<
-
-|||||||
-=======
-
-/* static */ RCFile*
-RCFile::create(JSContext* cx, const char* filename, const char* mode)
-{
-    FILE* fp = fopen(filename, mode);
-    if (!fp)
-        return nullptr;
-
-    RCFile* file = cx->new_<RCFile>(fp);
-    if (!file) {
-        fclose(fp);
-        return nullptr;
-    }
-
-    return file;
-}
-
-void
-RCFile::close()
-{
-    if (fp)
-        fclose(fp);
-    fp = nullptr;
-}
-
-bool
-RCFile::release()
-{
-    if (--numRefs)
-        return false;
-    this->close();
-    return true;
-}
-
-class FileObject : public JSObject {
-    enum : uint32_t {
-        FILE_SLOT = 0,
-        NUM_SLOTS
-    };
-
-  public:
-    static const js::Class class_;
-
-    static FileObject* create(JSContext* cx, RCFile* file) {
-        JSObject* obj = js::NewObjectWithClassProto(cx, &class_, JS::NullPtr());
-        if (!obj)
-            return nullptr;
-
-        FileObject* fileObj = &obj->as<FileObject>();
-        fileObj->setRCFile(file);
-        file->acquire();
-        return fileObj;
-    }
-
-    static void finalize(FreeOp* fop, JSObject* obj) {
-        FileObject* fileObj = &obj->as<FileObject>();
-        RCFile* file = fileObj->rcFile();
-        if (file->release()) {
-            fileObj->setRCFile(nullptr);
-            fop->delete_(file);
-        }
-    }
-
-    bool isOpen() {
-        RCFile* file = rcFile();
-        return file && file->isOpen();
-    }
-
-    void close() {
-        if (!isOpen())
-            return;
-        rcFile()->close();
-    }
-
-    RCFile* rcFile() {
-        return reinterpret_cast<RCFile*>(js::GetReservedSlot(this, FILE_SLOT).toPrivate());
-    }
-
-  private:
-
-    void setRCFile(RCFile* file) {
-        js::SetReservedSlot(this, FILE_SLOT, PrivateValue(file));
-    }
-};
-
-const js::Class FileObject::class_ = {
-    "File",
-    JSCLASS_HAS_RESERVED_SLOTS(FileObject::NUM_SLOTS),
-    nullptr,               /* addProperty */
-    nullptr,               /* delProperty */
-    nullptr,               /* getProperty */
-    nullptr,               /* setProperty */
-    nullptr,               /* enumerate */
-    nullptr,               /* resolve */
-    nullptr,               /* mayResolve */
-    nullptr,               /* convert */
-    FileObject::finalize,  /* finalize */
-    nullptr,               /* call */
-    nullptr,               /* hasInstance */
-    nullptr,               /* construct */
-    nullptr                /* trace */
-};
-
-static FileObject*
-redirect(JSContext* cx, HandleString relFilename, RCFile** globalFile)
-{
-    RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
-    if (!filename)
-        return nullptr;
-    JSAutoByteString filenameABS(cx, filename);
-    if (!filenameABS)
-        return nullptr;
-    RCFile* file = RCFile::create(cx, filenameABS.ptr(), "wb");
-    if (!file) {
-        JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
-        return nullptr;
-    }
-
-    // The global gOutFile acquires ownership of the new file, releases
-    // ownership of its old file, and we return a FileObject owning the old
-    // file.
-    file->acquire(); // Global owner of new file
-
-    FileObject* fileObj = FileObject::create(cx, *globalFile); // Newly created owner of old file
-    if (!fileObj) {
-        file->release();
-        return nullptr;
-    }
-
-    (*globalFile)->release(); // Release (global) ownership of old file.
-    *globalFile = file;
-
-    return fileObj;
-}
-
-static bool
-Redirect(JSContext* cx, const CallArgs& args, RCFile** outFile)
-{
-    if (args.length() > 1) {
-        JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
-        return false;
-    }
-
-    RCFile* oldFile = *outFile;
-    RootedObject oldFileObj(cx, FileObject::create(cx, oldFile));
-    if (!oldFileObj)
-        return false;
-
-    if (args.get(0).isUndefined()) {
-        args.rval().setObject(*oldFileObj);
-        return true;
-    }
-
-    if (args[0].isObject()) {
-        RootedObject fileObj(cx, js::CheckedUnwrap(&args[0].toObject()));
-        if (!fileObj)
-            return false;
-        if (fileObj->is<FileObject>()) {
-            // Passed in a FileObject. Create a FileObject for the previous
-            // global file, and set the global file to the passed-in one.
-            *outFile = fileObj->as<FileObject>().rcFile();
-            (*outFile)->acquire();
-            oldFile->release();
-
-            args.rval().setObject(*oldFileObj);
-            return true;
-        }
-    }
-
-    RootedString filename(cx, JS::ToString(cx, args[0]));
-    if (!filename)
-        return false;
-
-    if (!redirect(cx, filename, outFile))
-        return false;
-
-    args.rv
\ No newline at end of file
diff --git a/src/third_party/mozjs-45/js/src/shell/jsshell.h.rej b/src/third_party/mozjs-45/js/src/shell/jsshell.h.rej
deleted file mode 100644
index 217a0f6..0000000
--- a/src/third_party/mozjs-45/js/src/shell/jsshell.h.rej
+++ /dev/null
@@ -1,43 +0,0 @@
---- jsshell.h
-+++ jsshell.h
-@@ -45,30 +49,30 @@ class AutoCloseFile
-         bool success = true;
-         if (f_ && f_ != stdin && f_ != stdout && f_ != stderr)
-             success = !fclose(f_);
-         f_ = nullptr;
-         return success;
-     }
- };
- 
--// Reference counted file.
--struct RCFile {
-+struct File final : public RefCounted<File> {
-     FILE* fp;
--    uint32_t numRefs;
- 
--    RCFile() : fp(nullptr), numRefs(0) {}
--    RCFile(FILE* fp) : fp(fp), numRefs(0) {}
-+    File() : fp(nullptr) {}
-+    File(FILE* fp) : fp(fp) {}
-+    ~File() {
-+        this->close();
-+    }
- 
--    void acquire() { numRefs++; }
--
--    // Starts out with a ref count of zero.
--    static RCFile* create(JSContext* cx, const char* filename, const char* mode);
-+    static already_AddRefed<File>
-+    create(JSContext* cx, const char* filename, const char* mode);
- 
-     void close();
-     bool isOpen() const { return fp; }
--    bool release();
- };
- 
-+using RCFile = RefPtr<File>;
-+
- } /* namespace shell */
- } /* namespace js */
- 
- #endif
diff --git a/src/third_party/mozjs-45/js/src/vm/UbiNodeCensus.cpp.rej b/src/third_party/mozjs-45/js/src/vm/UbiNodeCensus.cpp.rej
deleted file mode 100644
index a54806a..0000000
--- a/src/third_party/mozjs-45/js/src/vm/UbiNodeCensus.cpp.rej
+++ /dev/null
@@ -1,23 +0,0 @@
---- UbiNodeCensus.cpp
-+++ UbiNodeCensus.cpp
-@@ -1,16 +1,20 @@
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-  * vim: set ts=8 sts=4 et sw=4 tw=99:
-  * This Source Code Form is subject to the terms of the Mozilla Public
-  * License, v. 2.0. If a copy of the MPL was not distributed with this
-  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- 
- #include "js/UbiNodeCensus.h"
- 
-+#include "jscntxt.h"
-+#include "jscompartment.h"
-+#include "jsobjinlines.h"
-+
- using namespace js;
- 
- namespace JS {
- namespace ubi {
- 
- void
- CountDeleter::operator()(CountBase* ptr)
- {
diff --git a/src/third_party/musl/musl.gyp b/src/third_party/musl/musl.gyp
index 895e07f..2a85789 100644
--- a/src/third_party/musl/musl.gyp
+++ b/src/third_party/musl/musl.gyp
@@ -55,7 +55,7 @@
         # no-unknown-pragmas  -w shift-op-parentheses
         '-nobuiltininc',
         '-isystem<(DEPTH)/third_party/musl/include',
-        '-isystem<(DEPTH)/third_party/musl/arch/x86_64',
+        '-isystem<(DEPTH)/third_party/musl/arch/<(musl_arch)',
         '-isystem<(DEPTH)/third_party/musl/arch/generic',
         '-w',
       ],
diff --git a/src/third_party/woff2/woff2.gyp b/src/third_party/woff2/woff2.gyp
index 68434ac..deca5f9 100644
--- a/src/third_party/woff2/woff2.gyp
+++ b/src/third_party/woff2/woff2.gyp
@@ -3,6 +3,9 @@
 # found in the LICENSE file.
 
 {
+  'variables': {
+    'optimize_target_for_speed': 1,
+  },
   'targets': [
     {
       'target_name': 'woff2_dec',
diff --git a/src/updater_components/bsdiff.h b/src/updater_components/bsdiff.h
new file mode 100644
index 0000000..c1d26c7
--- /dev/null
+++ b/src/updater_components/bsdiff.h
@@ -0,0 +1,107 @@
+// Copyright 2003, 2004 Colin Percival
+// All rights reserved
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted providing that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// For the terms under which this work may be distributed, please see
+// the adjoining file "LICENSE".
+//
+// Changelog:
+// 2005-04-26 - Define the header as a C structure, add a CRC32 checksum to
+//              the header, and make all the types 32-bit.
+//                --Benjamin Smedberg <benjamin@smedbergs.us>
+// 2009-03-31 - Change to use Streams.  Move CRC code to crc.{h,cc}
+//              Changed status to an enum, removed unused status codes.
+//                --Stephen Adams <sra@chromium.org>
+// 2013-04-10 - Added wrapper to apply a patch directly to files.
+//                --Joshua Pawlicki <waffles@chromium.org>
+
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COURGETTE_THIRD_PARTY_BSDIFF_BSDIFF_H_
+#define COURGETTE_THIRD_PARTY_BSDIFF_BSDIFF_H_
+
+#include <stdint.h>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+
+namespace courgette {
+class SourceStream;
+class SinkStream;
+}  // namespace courgette
+
+namespace bsdiff {
+
+enum BSDiffStatus {
+  OK = 0,
+  MEM_ERROR = 1,
+  CRC_ERROR = 2,
+  READ_ERROR = 3,
+  UNEXPECTED_ERROR = 4,
+  WRITE_ERROR = 5
+};
+
+// Creates a binary patch.
+//
+BSDiffStatus CreateBinaryPatch(courgette::SourceStream* old_stream,
+                               courgette::SourceStream* new_stream,
+                               courgette::SinkStream* patch_stream);
+
+// Applies the given patch file to a given source file. This method validates
+// the CRC of the original file stored in the patch file, before applying the
+// patch to it.
+//
+BSDiffStatus ApplyBinaryPatch(courgette::SourceStream* old_stream,
+                              courgette::SourceStream* patch_stream,
+                              courgette::SinkStream* new_stream);
+
+// As above, but simply takes base::Files.
+BSDiffStatus ApplyBinaryPatch(base::File old_stream,
+                              base::File patch_stream,
+                              base::File new_stream);
+
+// As above, but simply takes the file paths.
+BSDiffStatus ApplyBinaryPatch(const base::FilePath& old_stream,
+                              const base::FilePath& patch_stream,
+                              const base::FilePath& new_stream);
+
+// The following declarations are common to the patch-creation and
+// patch-application code.
+
+// The patch stream starts with a MBSPatchHeader.
+typedef struct MBSPatchHeader_ {
+  char tag[8];      // Contains MBS_PATCH_HEADER_TAG.
+  uint32_t slen;    // Length of the file to be patched.
+  uint32_t scrc32;  // CRC32 of the file to be patched.
+  uint32_t dlen;    // Length of the result file.
+} MBSPatchHeader;
+
+// This is the value for the tag field.  Must match length exactly, not counting
+// null at end of string.
+#define MBS_PATCH_HEADER_TAG "GBSDIF42"
+
+}  // namespace bsdiff
+
+#endif  // COURGETTE_THIRD_PARTY_BSDIFF_BSDIFF_H_
diff --git a/src/updater_components/client_update_protocol/BUILD.gn b/src/updater_components/client_update_protocol/BUILD.gn
new file mode 100644
index 0000000..e22ee24
--- /dev/null
+++ b/src/updater_components/client_update_protocol/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("client_update_protocol") {
+  sources = [
+    "ecdsa.cc",
+    "ecdsa.h",
+  ]
+
+  deps = [
+    "//base",
+    "//crypto",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "ecdsa_unittest.cc",
+  ]
+
+  deps = [
+    ":client_update_protocol",
+    "//base",
+    "//crypto",
+    "//testing/gtest",
+  ]
+}
diff --git a/src/updater_components/client_update_protocol/ecdsa.cc b/src/updater_components/client_update_protocol/ecdsa.cc
new file mode 100644
index 0000000..53b7e90
--- /dev/null
+++ b/src/updater_components/client_update_protocol/ecdsa.cc
@@ -0,0 +1,188 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/client_update_protocol/ecdsa.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "crypto/random.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_verifier.h"
+
+namespace client_update_protocol {
+
+namespace {
+
+std::vector<uint8_t> SHA256HashStr(const base::StringPiece& str) {
+  std::vector<uint8_t> result(crypto::kSHA256Length);
+  crypto::SHA256HashString(str, &result.front(), result.size());
+  return result;
+}
+
+std::vector<uint8_t> SHA256HashVec(const std::vector<uint8_t>& vec) {
+  if (vec.empty())
+    return SHA256HashStr(base::StringPiece());
+
+  return SHA256HashStr(base::StringPiece(
+      reinterpret_cast<const char*>(&vec.front()), vec.size()));
+}
+
+bool ParseETagHeader(const base::StringPiece& etag_header_value_in,
+                     std::vector<uint8_t>* ecdsa_signature_out,
+                     std::vector<uint8_t>* request_hash_out) {
+  DCHECK(ecdsa_signature_out);
+  DCHECK(request_hash_out);
+
+  // The ETag value is a UTF-8 string, formatted as "S:H", where:
+  // * S is the ECDSA signature in DER-encoded ASN.1 form, converted to hex.
+  // * H is the SHA-256 hash of the observed request body, standard hex format.
+  // A Weak ETag is formatted as W/"S:H". This function treats it the same as a
+  // strong ETag.
+  base::StringPiece etag_header_value(etag_header_value_in);
+
+  // Remove the weak prefix, then remove the begin and the end quotes.
+  const char kWeakETagPrefix[] = "W/";
+  if (etag_header_value.starts_with(kWeakETagPrefix))
+    etag_header_value.remove_prefix(base::size(kWeakETagPrefix) - 1);
+  if (etag_header_value.size() >= 2 && etag_header_value.starts_with("\"") &&
+      etag_header_value.ends_with("\"")) {
+    etag_header_value.remove_prefix(1);
+    etag_header_value.remove_suffix(1);
+  }
+
+  const base::StringPiece::size_type delim_pos = etag_header_value.find(':');
+  if (delim_pos == base::StringPiece::npos || delim_pos == 0 ||
+      delim_pos == etag_header_value.size() - 1)
+    return false;
+
+  const base::StringPiece sig_hex = etag_header_value.substr(0, delim_pos);
+  const base::StringPiece hash_hex = etag_header_value.substr(delim_pos + 1);
+
+  // Decode the ECDSA signature. Don't bother validating the contents of it;
+  // the SignatureValidator class will handle the actual DER decoding and
+  // ASN.1 parsing. Check for an expected size range only -- valid ECDSA
+  // signatures are between 8 and 72 bytes.
+  if (!base::HexStringToBytes(sig_hex, ecdsa_signature_out))
+    return false;
+  if (ecdsa_signature_out->size() < 8 || ecdsa_signature_out->size() > 72)
+    return false;
+
+  // Decode the SHA-256 hash; it should be exactly 32 bytes, no more or less.
+  if (!base::HexStringToBytes(hash_hex, request_hash_out))
+    return false;
+  if (request_hash_out->size() != crypto::kSHA256Length)
+    return false;
+
+  return true;
+}
+
+}  // namespace
+
+Ecdsa::Ecdsa(int key_version, const base::StringPiece& public_key)
+    : pub_key_version_(key_version),
+      public_key_(public_key.begin(), public_key.end()) {}
+
+Ecdsa::~Ecdsa() {}
+
+std::unique_ptr<Ecdsa> Ecdsa::Create(int key_version,
+                                     const base::StringPiece& public_key) {
+  DCHECK_GT(key_version, 0);
+  DCHECK(!public_key.empty());
+
+  return base::WrapUnique(new Ecdsa(key_version, public_key));
+}
+
+void Ecdsa::OverrideNonceForTesting(int key_version, uint32_t nonce) {
+  DCHECK(!request_query_cup2key_.empty());
+  request_query_cup2key_ = base::StringPrintf("%d:%u", pub_key_version_, nonce);
+}
+
+void Ecdsa::SignRequest(const base::StringPiece& request_body,
+                        std::string* query_params) {
+  DCHECK(query_params);
+
+  // Generate a random nonce to use for freshness, build the cup2key query
+  // string, and compute the SHA-256 hash of the request body. Set these
+  // two pieces of data aside to use during ValidateResponse().
+  uint32_t nonce = 0;
+  crypto::RandBytes(&nonce, sizeof(nonce));
+  request_query_cup2key_ = base::StringPrintf("%d:%u", pub_key_version_, nonce);
+  request_hash_ = SHA256HashStr(request_body);
+
+  // Return the query string for the user to send with the request.
+  std::string request_hash_hex =
+      base::HexEncode(&request_hash_.front(), request_hash_.size());
+  request_hash_hex = base::ToLowerASCII(request_hash_hex);
+
+  *query_params = base::StringPrintf("cup2key=%s&cup2hreq=%s",
+                                     request_query_cup2key_.c_str(),
+                                     request_hash_hex.c_str());
+}
+
+bool Ecdsa::ValidateResponse(const base::StringPiece& response_body,
+                             const base::StringPiece& server_etag) {
+  DCHECK(!request_hash_.empty());
+  DCHECK(!request_query_cup2key_.empty());
+
+  if (response_body.empty() || server_etag.empty())
+    return false;
+
+  // Break the ETag into its two components (the ECDSA signature, and the
+  // hash of the request that the server observed) and decode to byte buffers.
+  std::vector<uint8_t> signature;
+  std::vector<uint8_t> observed_request_hash;
+  if (!ParseETagHeader(server_etag, &signature, &observed_request_hash))
+    return false;
+
+  // Check that the server's observed request hash is equal to the original
+  // request hash. (This is a quick rejection test; the signature test is
+  // authoritative, but slower.)
+  DCHECK_EQ(request_hash_.size(), crypto::kSHA256Length);
+  if (observed_request_hash.size() != crypto::kSHA256Length)
+    return false;
+  if (!std::equal(observed_request_hash.begin(), observed_request_hash.end(),
+                  request_hash_.begin()))
+    return false;
+
+  // Next, build the buffer that the server will have signed on its end:
+  //   hash( hash(request) | hash(response) | cup2key_query_string )
+  // When building the client's version of the buffer, it's important to use
+  // the original request hash that it attempted to send, and not the observed
+  // request hash that the server sent back to us.
+  const std::vector<uint8_t> response_hash = SHA256HashStr(response_body);
+
+  std::vector<uint8_t> signed_message;
+  signed_message.insert(signed_message.end(), request_hash_.begin(),
+                        request_hash_.end());
+  signed_message.insert(signed_message.end(), response_hash.begin(),
+                        response_hash.end());
+  signed_message.insert(signed_message.end(), request_query_cup2key_.begin(),
+                        request_query_cup2key_.end());
+
+  const std::vector<uint8_t> signed_message_hash =
+      SHA256HashVec(signed_message);
+
+  // Initialize the signature verifier.
+  crypto::SignatureVerifier verifier;
+  if (!verifier.VerifyInit(crypto::SignatureVerifier::ECDSA_SHA256, signature,
+                           public_key_)) {
+    DVLOG(1) << "Couldn't init SignatureVerifier.";
+    return false;
+  }
+
+  // If the verification fails, that implies one of two outcomes:
+  // * The signature was modified
+  // * The buffer that the server signed does not match the buffer that the
+  //   client assembled -- implying that either request body or response body
+  //   was modified, or a different nonce value was used.
+  verifier.VerifyUpdate(signed_message_hash);
+  return verifier.VerifyFinal();
+}
+
+}  // namespace client_update_protocol
diff --git a/src/updater_components/client_update_protocol/ecdsa.h b/src/updater_components/client_update_protocol/ecdsa.h
new file mode 100644
index 0000000..24e4171
--- /dev/null
+++ b/src/updater_components/client_update_protocol/ecdsa.h
@@ -0,0 +1,88 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CLIENT_UPDATE_PROTOCOL_ECDSA_H_
+#define COMPONENTS_CLIENT_UPDATE_PROTOCOL_ECDSA_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+
+namespace client_update_protocol {
+
+// Client Update Protocol v2, or CUP-ECDSA, is used by Google Update (Omaha)
+// servers to ensure freshness and authenticity of server responses over HTTP,
+// without the overhead of HTTPS -- namely, no PKI, no guarantee of privacy, and
+// no request replay protection.
+//
+// CUP-ECDSA relies on a single signing operation using ECDSA with SHA-256,
+// instead of the original CUP which used HMAC-SHA1 with a random signing key
+// encrypted using RSA.
+//
+// Each |Ecdsa| object represents a single network ping in flight -- a call to
+// SignRequest() generates internal state that will be used by
+// ValidateResponse().
+class Ecdsa {
+ public:
+  ~Ecdsa();
+
+  // Initializes this instance of CUP-ECDSA with a versioned public key.
+  // |key_version| must be non-negative. |public_key| is expected to be a
+  // DER-encoded ASN.1 SubjectPublicKeyInfo containing an ECDSA public key.
+  // Returns a NULL pointer on failure.
+  static std::unique_ptr<Ecdsa> Create(int key_version,
+                                       const base::StringPiece& public_key);
+
+  // Generates freshness/authentication data for an outgoing ping.
+  // |request_body| contains the body of the ping in UTF-8.  On return,
+  // |query_params| contains a set of query parameters (in UTF-8) to be appended
+  // to the URL.
+  //
+  // This method will store internal state in this instance used by calls to
+  // ValidateResponse(); if you need to have multiple pings in flight,
+  // initialize a separate CUP-ECDSA instance for each one.
+  void SignRequest(const base::StringPiece& request_body,
+                   std::string* query_params);
+
+  // Validates a response given to a ping previously signed with
+  // SignRequest(). |response_body| contains the body of the response in
+  // UTF-8. |signature| contains the ECDSA signature and observed request
+  // hash. Returns true if the response is valid and the observed request hash
+  // matches the sent hash.  This method uses internal state that is set by a
+  // prior SignRequest() call.
+  bool ValidateResponse(const base::StringPiece& response_body,
+                        const base::StringPiece& signature);
+
+  // Sets the key and nonce that were used to generate a signature that is baked
+  // into a unit test.
+  void OverrideNonceForTesting(int key_version, uint32_t nonce);
+
+ private:
+  Ecdsa(int key_version, const base::StringPiece& public_key);
+
+  // The server keeps multiple signing keys; a version must be sent so that
+  // the correct signing key is used to sign the assembled message.
+  const int pub_key_version_;
+
+  // The ECDSA public key to use for verifying response signatures.
+  const std::vector<uint8_t> public_key_;
+
+  // The SHA-256 hash of the XML request.  This is modified on each call to
+  // SignRequest(), and checked by ValidateResponse().
+  std::vector<uint8_t> request_hash_;
+
+  // The query string containing key version and nonce in UTF-8 form.  This is
+  // modified on each call to SignRequest(), and checked by ValidateResponse().
+  std::string request_query_cup2key_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Ecdsa);
+};
+
+}  // namespace client_update_protocol
+
+#endif  // COMPONENTS_CLIENT_UPDATE_PROTOCOL_ECDSA_H_
diff --git a/src/updater_components/client_update_protocol/ecdsa_unittest.cc b/src/updater_components/client_update_protocol/ecdsa_unittest.cc
new file mode 100644
index 0000000..71c7441
--- /dev/null
+++ b/src/updater_components/client_update_protocol/ecdsa_unittest.cc
@@ -0,0 +1,294 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/client_update_protocol/ecdsa.h"
+
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "crypto/random.h"
+#include "crypto/secure_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace client_update_protocol {
+
+namespace {
+
+std::string GetPublicKeyForTesting() {
+  // How to generate this key:
+  //   openssl ecparam -genkey -name prime256v1 -out ecpriv.pem
+  //   openssl ec -in ecpriv.pem -pubout -out ecpub.pem
+
+  static const char kCupEcdsaTestKey_Base64[] =
+      "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJNOjKyN6UHyUGkGow+xCmQthQXUo"
+      "9sd7RIXSpVIM768UlbGb/5JrnISjSYejCc/pxQooI6mJTzWL3pZb5TA1DA==";
+
+  std::string result;
+  if (!base::Base64Decode(std::string(kCupEcdsaTestKey_Base64), &result))
+    return std::string();
+
+  return result;
+}
+
+}  // end namespace
+
+class CupEcdsaTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    cup_ = Ecdsa::Create(8, GetPublicKeyForTesting());
+    ASSERT_TRUE(cup_.get());
+  }
+
+  Ecdsa& CUP() { return *cup_; }
+
+ private:
+  std::unique_ptr<Ecdsa> cup_;
+};
+
+TEST_F(CupEcdsaTest, SignRequest) {
+  static const char kRequest[] = "TestSequenceForCupEcdsaUnitTest";
+  static const char kRequestHash[] =
+      "&cup2hreq="
+      "cde1f7dc1311ed96813057ca321c2f5a17ea2c9c776ee0eb31965f7985a3074a";
+  static const char kKeyId[] = "cup2key=8:";
+
+  std::string query;
+  CUP().SignRequest(kRequest, &query);
+  std::string query2;
+  CUP().SignRequest(kRequest, &query2);
+
+  EXPECT_FALSE(query.empty());
+  EXPECT_FALSE(query2.empty());
+  EXPECT_EQ(0UL, query.find(kKeyId));
+  EXPECT_EQ(0UL, query2.find(kKeyId));
+  EXPECT_NE(std::string::npos, query.find(kRequestHash));
+  EXPECT_NE(std::string::npos, query2.find(kRequestHash));
+
+  // In theory, this is a flaky test, as there's nothing preventing the RNG
+  // from returning the same nonce twice in a row. In practice, this should
+  // be fine.
+  EXPECT_NE(query, query2);
+}
+
+TEST_F(CupEcdsaTest, ValidateResponse_TestETagParsing) {
+  // Invalid ETags must be gracefully rejected without a crash.
+  std::string query_discard;
+  CUP().SignRequest("Request_A", &query_discard);
+  CUP().OverrideNonceForTesting(8, 12345);
+
+  // Expect a pass for a well-formed etag.
+  EXPECT_TRUE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+
+  // Reject empty etags.
+  EXPECT_FALSE(CUP().ValidateResponse("Response_A", ""));
+
+  // Reject etags with zero-length hashes or signatures, even if the other
+  // component is wellformed.
+  EXPECT_FALSE(CUP().ValidateResponse("Response_A", ":"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+
+  // Reject etags with non-hex content in either component.
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458__ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":2727bc2b3c33feb6800a830f4055901d__7d65a84184c5fbeb3f816db0a243f5"));
+
+  // Reject etags where either/both component has a length that's not a
+  // multiple of 2 (i.e. not a valid hex encoding).
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f"));
+
+  // Reject etags where the hash is even, but not 256 bits.
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5ff"));
+
+  // Reject etags where the signature field is too small to be valid. (Note that
+  // the case isn't even a signature -- it's a validly encoded ASN.1 NULL.)
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "0500"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+
+  // Reject etags where the signature field is too big to be a valid signature.
+  // (This is a validly formed structure, but both ints are over 256 bits.)
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3048"
+      "202207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+      "202207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5ff"));
+
+  // Reject etags where the signature is valid DER-encoded ASN.1, but is not
+  // an ECDSA signature. (This is actually stressing crypto's SignatureValidator
+  // library, and not CUP's use of it, but it's worth testing here.)  Cases:
+  // * Something that's not a sequence
+  // * Sequences that contain things other than ints (i.e. octet strings)
+  // * Sequences that contain a negative int.
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "0406020100020100"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "06200123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+      "06200123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3046"
+      "02047fffffff"
+      "0220ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+
+  // Reject etags where the signature is not a valid DER encoding. (Again, this
+  // is stressing SignatureValidator.)  Test cases are:
+  // * No length field
+  // * Zero length field
+  // * One of the ints has truncated content
+  // * One of the ints has content longer than its length field
+  // * A positive int is improperly zero-padded
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "30"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3000"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "02207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"
+      "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3044"
+      "022000007f24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
+      "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+}
+
+TEST_F(CupEcdsaTest, ValidateResponse_TestSigning) {
+  std::string query_discard;
+  CUP().SignRequest("Request_A", &query_discard);
+  CUP().OverrideNonceForTesting(8, 12345);
+
+  // How to generate an ECDSA signature:
+  //   echo -n Request_A | sha256sum | cut -d " " -f 1 > h
+  //   echo -n Response_A | sha256sum | cut -d " " -f 1 >> h
+  //   cat h | xxd -r -p > hbin
+  //   echo -n 8:12345 >> hbin
+  //   sha256sum hbin | cut -d " " -f 1 | xxd -r -p > hbin2
+  //   openssl dgst -hex -sha256 -sign ecpriv.pem hbin2 | cut -d " " -f 2 > sig
+  //   echo -n :Request_A | sha256sum | cut -d " " -f 1 >> sig
+  //   cat sig
+  // It's useful to throw this in a bash script and parameterize it if you're
+  // updating this unit test.
+
+  // Valid case:
+  //  * Send "Request_A" with key 8 / nonce 12345 to server.
+  //  * Receive "Response_A", signature, and observed request hash from server.
+  //  * Signature signs HASH(Request_A) | HASH(Response_A) | 8:12345.
+  //  * Observed hash matches HASH(Request_A).
+  EXPECT_TRUE(CUP().ValidateResponse(
+      "Response_A",
+      "3045022077a2d004f1643a92af5d356877c3434c46519ce32882d6e30ef6d154ee9775e3"
+      "022100aca63c77d34152bdc0918ae0629e82b59314e5459f607cdc5ac95f1a4b7c31a2"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+
+  // Failure case: "Request_A" made it to the server intact, but the response
+  // body is modified to "Response_B" on return.  The signature is now invalid.
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_B",
+      "3045022077a2d004f1643a92af5d356877c3434c46519ce32882d6e30ef6d154ee9775e3"
+      "022100aca63c77d34152bdc0918ae0629e82b59314e5459f607cdc5ac95f1a4b7c31a2"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+
+  // Failure case: Request body was modified to "Request_B" before it reached
+  // the server.  Test a fast reject based on the observed_hash parameter.
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_B",
+      "304402206289a7765f0371c7c48796779747f1166707d5937a99af518845f44af95876"
+      "8c0220139fe935fde3e6b416ee742f91c6a480113762d78d889a2661de37576866d21c"
+      ":80e3ef1b373efe5f2a8383a0cf9c89fb2e0cbb8e85db4813655ff5dc05009e7e"));
+
+  // Failure case: Request body was modified to "Request_B" before it reached
+  // the server.  Test a slow reject based on a signature mismatch.
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_B",
+      "304402206289a7765f0371c7c48796779747f1166707d5937a99af518845f44af95876"
+      "8c0220139fe935fde3e6b416ee742f91c6a480113762d78d889a2661de37576866d21c"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+
+  // Failure case: Request/response are intact, but the signature is invalid
+  // because it was signed against a different nonce (67890).
+  EXPECT_FALSE(CUP().ValidateResponse(
+      "Response_A",
+      "3046022100d3bbb1fb4451c8e04a07fe95404cc39121ed0e0bc084f87de19d52eee50a97"
+      "bf022100dd7d41d467be2af98d9116b0c7ba09740d54578c02a02f74da5f089834be3403"
+      ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
+}
+
+}  // namespace client_update_protocol
diff --git a/src/updater_components/courgette.h b/src/updater_components/courgette.h
new file mode 100644
index 0000000..5337566
--- /dev/null
+++ b/src/updater_components/courgette.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2011 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 COURGETTE_COURGETTE_H_
+#define COURGETTE_COURGETTE_H_
+
+#include <stddef.h>   // Required to define size_t on GCC
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+
+namespace courgette {
+
+// Status codes for Courgette APIs.
+//
+// Client code should only rely on the distintion between C_OK and the other
+// status codes.
+//
+enum Status {
+  C_OK = 1,                       // Successful operation.
+
+  C_GENERAL_ERROR = 2,            // Error other than listed below.
+
+  C_READ_OPEN_ERROR = 3,          // Could not open input file for reading.
+  C_READ_ERROR = 4,               // Could not read from opened input file.
+
+  C_WRITE_OPEN_ERROR = 3,         // Could not open output file for writing.
+  C_WRITE_ERROR = 4,              // Could not write to opened output file.
+
+  C_BAD_ENSEMBLE_MAGIC = 5,       // Ensemble patch has bad magic.
+  C_BAD_ENSEMBLE_VERSION = 6,     // Ensemble patch has wrong version.
+  C_BAD_ENSEMBLE_HEADER = 7,      // Ensemble patch has corrupt header.
+  C_BAD_ENSEMBLE_CRC = 8,         // Ensemble patch has corrupt data.
+
+  C_BAD_TRANSFORM = 12,           // Transform mis-specified.
+  C_BAD_BASE = 13,                // Base for transform malformed.
+
+  C_BINARY_DIFF_CRC_ERROR = 14,   // Internal diff input doesn't have expected
+                                  // CRC.
+
+  // Internal errors.
+  C_STREAM_ERROR = 20,            // Unexpected error from streams.h.
+  C_STREAM_NOT_CONSUMED = 21,     // Stream has extra data, is expected to be
+                                  // used up.
+  C_SERIALIZATION_FAILED = 22,    //
+  C_DESERIALIZATION_FAILED = 23,  //
+  C_INPUT_NOT_RECOGNIZED = 24,    // Unrecognized input (not an executable).
+  C_DISASSEMBLY_FAILED = 25,      //
+  C_ASSEMBLY_FAILED = 26,         //
+  C_ADJUSTMENT_FAILED = 27,       //
+};
+
+// What type of executable is something
+// This is part of the patch format. Never reuse an id number.
+enum ExecutableType {
+  EXE_UNKNOWN = 0,
+  EXE_WIN_32_X86 = 1,
+  EXE_ELF_32_X86 = 2,
+  EXE_ELF_32_ARM = 3,
+  EXE_WIN_32_X64 = 4,
+};
+
+class SinkStream;
+class SinkStreamSet;
+class SourceStream;
+
+class AssemblyProgram;
+class EncodedProgram;
+
+// Applies the patch to the bytes in |old| and writes the transformed ensemble
+// to |output|.
+// Returns C_OK unless something went wrong.
+Status ApplyEnsemblePatch(SourceStream* old, SourceStream* patch,
+                          SinkStream* output);
+
+// Applies the patch in |patch_file| to the bytes in |old_file| and writes the
+// transformed ensemble to |new_file|.
+// Returns C_OK unless something went wrong.
+// This function first validates that the patch file has a proper header, so the
+// function can be used to 'try' a patch.
+Status ApplyEnsemblePatch(base::File old_file,
+                          base::File patch_file,
+                          base::File new_file);
+
+// Applies the patch in |patch_file_name| to the bytes in |old_file_name| and
+// writes the transformed ensemble to |new_file_name|.
+// Returns C_OK unless something went wrong.
+// This function first validates that the patch file has a proper header, so the
+// function can be used to 'try' a patch.
+Status ApplyEnsemblePatch(const base::FilePath::CharType* old_file_name,
+                          const base::FilePath::CharType* patch_file_name,
+                          const base::FilePath::CharType* new_file_name);
+
+// Generates a patch that will transform the bytes in |old| into the bytes in
+// |target|.
+// Returns C_OK unless something when wrong (unexpected).
+Status GenerateEnsemblePatch(SourceStream* old, SourceStream* target,
+                             SinkStream* patch);
+
+// Serializes |encoded| into the stream set.
+// Returns C_OK if succeeded, otherwise returns an error status.
+Status WriteEncodedProgram(EncodedProgram* encoded, SinkStreamSet* sink);
+
+// Assembles |encoded|, emitting the bytes into |buffer|.
+// Returns C_OK if succeeded, otherwise returns an error status and leaves
+// |buffer| in an undefined state.
+Status Assemble(EncodedProgram* encoded, SinkStream* buffer);
+
+// Adjusts |program| to look more like |model|.
+//
+Status Adjust(const AssemblyProgram& model, AssemblyProgram *program);
+
+}  // namespace courgette
+
+#endif  // COURGETTE_COURGETTE_H_
diff --git a/src/updater_components/crx_file/BUILD.gn b/src/updater_components/crx_file/BUILD.gn
new file mode 100644
index 0000000..4e6eacb
--- /dev/null
+++ b/src/updater_components/crx_file/BUILD.gn
@@ -0,0 +1,82 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+static_library("crx_file") {
+  sources = [
+    "crx_file.h",
+    "crx_verifier.cc",
+    "crx_verifier.h",
+    "id_util.cc",
+    "id_util.h",
+  ]
+
+  deps = [
+    "//base",
+    "//crypto",
+  ]
+
+  public_deps = [
+    ":crx3_proto",
+  ]
+}
+
+static_library("crx_creator") {
+  sources = [
+    "crx_creator.cc",
+    "crx_creator.h",
+  ]
+
+  deps = [
+    ":crx_file",
+    "//base",
+    "//crypto",
+  ]
+
+  public_deps = [
+    ":crx3_proto",
+  ]
+}
+
+bundle_data("unit_tests_bundle_data") {
+  visibility = [ ":unit_tests" ]
+  testonly = true
+  sources = [
+    "//components/test/data/crx_file/sample.zip",
+    "//components/test/data/crx_file/unsigned.crx3",
+    "//components/test/data/crx_file/valid.crx2",
+    "//components/test/data/crx_file/valid_no_publisher.crx3",
+    "//components/test/data/crx_file/valid_publisher.crx3",
+    "//components/test/data/crx_file/valid_test_publisher.crx3",
+  ]
+  outputs = [
+    "{{bundle_resources_dir}}/" +
+        "{{source_root_relative_dir}}/{{source_file_part}}",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "crx_creator_unittest.cc",
+    "crx_verifier_unittest.cc",
+    "id_util_unittest.cc",
+  ]
+
+  deps = [
+    ":crx_creator",
+    ":crx_file",
+    ":unit_tests_bundle_data",
+    "//base",
+    "//crypto",
+    "//testing/gtest",
+  ]
+}
+
+proto_library("crx3_proto") {
+  sources = [
+    "crx3.proto",
+  ]
+}
diff --git a/src/updater_components/crx_file/crx3.proto b/src/updater_components/crx_file/crx3.proto
new file mode 100644
index 0000000..2ea91f1
--- /dev/null
+++ b/src/updater_components/crx_file/crx3.proto
@@ -0,0 +1,55 @@
+// 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
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package crx_file;
+
+// A CRX₃ file is a binary file of the following format:
+// [4 octets]: "Cr24", a magic number.
+// [4 octets]: The version of the *.crx file format used (currently 3).
+// [4 octets]: N, little-endian, the length of the header section.
+// [N octets]: The header (the binary encoding of a CrxFileHeader).
+// [M octets]: The ZIP archive.
+// Clients should reject CRX₃ files that contain an N that is too large for the
+// client to safely handle in memory.
+
+message CrxFileHeader {
+  // PSS signature with RSA public key. The public key is formatted as a
+  // X.509 SubjectPublicKeyInfo block, as in CRXâ‚‚. In the common case of a
+  // developer key proof, the first 128 bits of the SHA-256 hash of the
+  // public key must equal the crx_id.
+  repeated AsymmetricKeyProof sha256_with_rsa = 2;
+
+  // ECDSA signature, using the NIST P-256 curve. Public key appears in
+  // named-curve format.
+  // The pinned algorithm will be this, at least on 2017-01-01.
+  repeated AsymmetricKeyProof sha256_with_ecdsa = 3;
+
+  // The binary form of a SignedData message. We do not use a nested
+  // SignedData message, as handlers of this message must verify the proofs
+  // on exactly these bytes, so it is convenient to parse in two steps.
+  //
+  // All proofs in this CrxFile message are on the value
+  // "CRX3 SignedData\x00" + signed_header_size + signed_header_data +
+  // archive, where "\x00" indicates an octet with value 0, "CRX3 SignedData"
+  // is encoded using UTF-8, signed_header_size is the size in octets of the
+  // contents of this field and is encoded using 4 octets in little-endian
+  // order, signed_header_data is exactly the content of this field, and
+  // archive is the remaining contents of the file following the header.
+  optional bytes signed_header_data = 10000;
+}
+
+message AsymmetricKeyProof {
+  optional bytes public_key = 1;
+  optional bytes signature = 2;
+}
+
+message SignedData {
+  // This is simple binary, not UTF-8 encoded mpdecimal; i.e. it is exactly
+  // 16 bytes long.
+  optional bytes crx_id = 1;
+}
diff --git a/src/updater_components/crx_file/crx_creator.cc b/src/updater_components/crx_file/crx_creator.cc
new file mode 100644
index 0000000..8ec6bb3
--- /dev/null
+++ b/src/updater_components/crx_file/crx_creator.cc
@@ -0,0 +1,129 @@
+// 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 "components/crx_file/crx_creator.h"
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/stl_util.h"
+#include "components/crx_file/crx3.pb.h"
+#include "components/crx_file/crx_file.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_creator.h"
+
+namespace crx_file {
+
+namespace {
+
+std::string GetCrxId(const std::string& key) {
+  uint8_t hash[16] = {};  // CRX IDs are 16 bytes long.
+  crypto::SHA256HashString(key, hash, sizeof(hash));
+  static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
+  return std::string(reinterpret_cast<char*>(hash), sizeof(hash));
+}
+
+// Read to the end of the file, updating the signer.
+CreatorResult ReadAndSignArchive(base::File* file,
+                                 crypto::SignatureCreator* signer,
+                                 std::vector<uint8_t>* signature) {
+  uint8_t buffer[1 << 12] = {};
+  int read = 0;
+  static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
+  while ((read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer),
+                                        base::size(buffer))) > 0) {
+    if (!signer->Update(buffer, read))
+      return CreatorResult::ERROR_SIGNING_FAILURE;
+  }
+  if (read < 0)
+    return CreatorResult::ERROR_SIGNING_FAILURE;
+  return signer->Final(signature) ? CreatorResult::OK
+                                  : CreatorResult::ERROR_SIGNING_FAILURE;
+}
+
+bool WriteBuffer(base::File* file, const char buffer[], int len) {
+  return file->WriteAtCurrentPos(buffer, len) == len;
+}
+
+bool WriteArchive(base::File* out, base::File* in) {
+  char buffer[1 << 12] = {};
+  int read = 0;
+  in->Seek(base::File::Whence::FROM_BEGIN, 0);
+  while ((read = in->ReadAtCurrentPos(buffer, base::size(buffer))) > 0) {
+    if (out->WriteAtCurrentPos(buffer, read) != read)
+      return false;
+  }
+  return read == 0;
+}
+
+}  // namespace
+
+CreatorResult Create(const base::FilePath& output_path,
+                     const base::FilePath& zip_path,
+                     crypto::RSAPrivateKey* signing_key) {
+  // Get the public key.
+  std::vector<uint8_t> public_key;
+  signing_key->ExportPublicKey(&public_key);
+  const std::string public_key_str(public_key.begin(), public_key.end());
+
+  // Assemble SignedData section.
+  SignedData signed_header_data;
+  signed_header_data.set_crx_id(GetCrxId(public_key_str));
+  const std::string signed_header_data_str =
+      signed_header_data.SerializeAsString();
+  const int signed_header_size = signed_header_data_str.size();
+  const uint8_t signed_header_size_octets[] = {
+      signed_header_size, signed_header_size >> 8, signed_header_size >> 16,
+      signed_header_size >> 24};
+
+  // Create a signer, init with purpose, SignedData length, run SignedData
+  // through, run ZIP through.
+  auto signer = crypto::SignatureCreator::Create(
+      signing_key, crypto::SignatureCreator::HashAlgorithm::SHA256);
+  signer->Update(kSignatureContext, base::size(kSignatureContext));
+  signer->Update(signed_header_size_octets,
+                 base::size(signed_header_size_octets));
+  signer->Update(
+      reinterpret_cast<const uint8_t*>(signed_header_data_str.data()),
+      signed_header_data_str.size());
+  base::File file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!file.IsValid())
+    return CreatorResult::ERROR_FILE_NOT_READABLE;
+  std::vector<uint8_t> signature;
+  const CreatorResult signing_result =
+      ReadAndSignArchive(&file, signer.get(), &signature);
+  if (signing_result != CreatorResult::OK)
+    return signing_result;
+
+  // Create CRXFileHeader.
+  CrxFileHeader header;
+  AsymmetricKeyProof* proof = header.add_sha256_with_rsa();
+  proof->set_public_key(public_key_str);
+  proof->set_signature(std::string(signature.begin(), signature.end()));
+  header.set_signed_header_data(signed_header_data_str);
+  const std::string header_str = header.SerializeAsString();
+  const int header_size = header_str.size();
+  const uint8_t header_size_octets[] = {header_size, header_size >> 8,
+                                        header_size >> 16, header_size >> 24};
+
+  // Write CRX.
+  const uint8_t format_version_octets[] = {3, 0, 0, 0};
+  base::File crx(output_path,
+                 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  if (!crx.IsValid())
+    return CreatorResult::ERROR_FILE_NOT_WRITABLE;
+  static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
+  if (!WriteBuffer(&crx, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize) ||
+      !WriteBuffer(&crx, reinterpret_cast<const char*>(format_version_octets),
+                   base::size(format_version_octets)) ||
+      !WriteBuffer(&crx, reinterpret_cast<const char*>(header_size_octets),
+                   base::size(header_size_octets)) ||
+      !WriteBuffer(&crx, header_str.c_str(), header_str.length()) ||
+      !WriteArchive(&crx, &file)) {
+    return CreatorResult::ERROR_FILE_WRITE_FAILURE;
+  }
+  return CreatorResult::OK;
+}
+
+}  // namespace crx_file
diff --git a/src/updater_components/crx_file/crx_creator.h b/src/updater_components/crx_file/crx_creator.h
new file mode 100644
index 0000000..a8c2094
--- /dev/null
+++ b/src/updater_components/crx_file/crx_creator.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef COMPONENTS_CRX_FILE_CRX_CREATOR_H_
+#define COMPONENTS_CRX_FILE_CRX_CREATOR_H_
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace crypto {
+class RSAPrivateKey;
+}  // namespace crypto
+
+namespace crx_file {
+
+enum class CreatorResult {
+  OK,  // The CRX file was successfully created.
+  ERROR_SIGNING_FAILURE,
+  ERROR_FILE_NOT_READABLE,
+  ERROR_FILE_NOT_WRITABLE,
+  ERROR_FILE_WRITE_FAILURE,
+};
+
+// Create a CRX3 file at |output_path|, using the contents of the ZIP archive
+// located at |zip_path| and signing with (and deriving the CRX ID from)
+// |signing_key|.
+CreatorResult Create(const base::FilePath& output_path,
+                     const base::FilePath& zip_path,
+                     crypto::RSAPrivateKey* signing_key);
+
+}  // namespace crx_file
+
+#endif  // COMPONENTS_CRX_FILE_CRX_CREATOR_H_
diff --git a/src/updater_components/crx_file/crx_creator_unittest.cc b/src/updater_components/crx_file/crx_creator_unittest.cc
new file mode 100644
index 0000000..086a5a5
--- /dev/null
+++ b/src/updater_components/crx_file/crx_creator_unittest.cc
@@ -0,0 +1,61 @@
+// 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 "components/crx_file/crx_creator.h"
+#include "base/base64.h"
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "components/crx_file/crx_verifier.h"
+#include "crypto/rsa_private_key.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+base::FilePath TestFile(const std::string& file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("crx_file")
+      .AppendASCII(file);
+}
+
+}  // namespace
+
+namespace crx_file {
+
+using CrxCreatorTest = testing::Test;
+
+TEST_F(CrxCreatorTest, Create) {
+  // Set up a signing key.
+  auto signing_key = crypto::RSAPrivateKey::Create(4096);
+  std::vector<uint8_t> public_key;
+  signing_key->ExportPublicKey(&public_key);
+  std::string expected_public_key;
+  base::Base64Encode(std::string(public_key.begin(), public_key.end()),
+                     &expected_public_key);
+
+  // Create a CRX File.
+  base::FilePath tempFile;
+  EXPECT_TRUE(base::CreateTemporaryFile(&tempFile));
+  EXPECT_EQ(CreatorResult::OK,
+            Create(tempFile, TestFile("sample.zip"), signing_key.get()));
+
+  // Test that the created file can be verified.
+  const std::vector<std::vector<uint8_t>> keys;
+  const std::vector<uint8_t> hash;
+  std::string public_key_in_crx;
+  EXPECT_EQ(VerifierResult::OK_FULL,
+            Verify(tempFile, VerifierFormat::CRX3, keys, hash,
+                   &public_key_in_crx, nullptr));
+  EXPECT_EQ(expected_public_key, public_key_in_crx);
+
+  // Delete the file.
+  EXPECT_TRUE(base::DeleteFile(tempFile, false));
+}
+
+}  // namespace crx_file
diff --git a/src/updater_components/crx_file/crx_file.h b/src/updater_components/crx_file/crx_file.h
new file mode 100644
index 0000000..4e258e1
--- /dev/null
+++ b/src/updater_components/crx_file/crx_file.h
@@ -0,0 +1,18 @@
+// 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.
+
+#ifndef COMPONENTS_CRX_FILE_CRX_FILE_H_
+#define COMPONENTS_CRX_FILE_CRX_FILE_H_
+
+namespace crx_file {
+
+// The magic string embedded in the header.
+constexpr char kCrxFileHeaderMagic[] = "Cr24";
+constexpr char kCrxDiffFileHeaderMagic[] = "CrOD";
+constexpr int kCrxFileHeaderMagicSize = 4;
+constexpr unsigned char kSignatureContext[] = u8"CRX3 SignedData";
+
+}  // namespace crx_file
+
+#endif  // COMPONENTS_CRX_FILE_CRX_FILE_H_
diff --git a/src/updater_components/crx_file/crx_verifier.cc b/src/updater_components/crx_file/crx_verifier.cc
new file mode 100644
index 0000000..0d20251
--- /dev/null
+++ b/src/updater_components/crx_file/crx_verifier.cc
@@ -0,0 +1,331 @@
+// 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 "components/crx_file/crx_verifier.h"
+
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/crx_file/crx3.pb.h"
+#include "components/crx_file/crx_file.h"
+#include "components/crx_file/id_util.h"
+#include "crypto/secure_hash.h"
+#include "crypto/secure_util.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_verifier.h"
+
+namespace crx_file {
+
+namespace {
+
+// The maximum size the Crx2 parser will tolerate for a public key.
+constexpr uint32_t kMaxPublicKeySize = 1 << 16;
+
+// The maximum size the Crx2 parser will tolerate for a signature.
+constexpr uint32_t kMaxSignatureSize = 1 << 16;
+
+// The maximum size the Crx3 parser will tolerate for a header.
+constexpr uint32_t kMaxHeaderSize = 1 << 18;
+
+// The SHA256 hash of the DER SPKI "ecdsa_2017_public" Crx3 key.
+constexpr uint8_t kPublisherKeyHash[] = {
+    0x61, 0xf7, 0xf2, 0xa6, 0xbf, 0xcf, 0x74, 0xcd, 0x0b, 0xc1, 0xfe,
+    0x24, 0x97, 0xcc, 0x9b, 0x04, 0x25, 0x4c, 0x65, 0x8f, 0x79, 0xf2,
+    0x14, 0x53, 0x92, 0x86, 0x7e, 0xa8, 0x36, 0x63, 0x67, 0xcf};
+
+// The SHA256 hash of the DER SPKI "ecdsa_2017_public" Crx3 test key.
+constexpr uint8_t kPublisherTestKeyHash[] = {
+    0x6c, 0x46, 0x41, 0x3b, 0x00, 0xd0, 0xfa, 0x0e, 0x72, 0xc8, 0xd2,
+    0x5f, 0x64, 0xf3, 0xa6, 0x17, 0x03, 0x0d, 0xde, 0x21, 0x61, 0xbe,
+    0xb7, 0x95, 0x91, 0x95, 0x83, 0x68, 0x12, 0xe9, 0x78, 0x1e};
+
+using VerifierCollection =
+    std::vector<std::unique_ptr<crypto::SignatureVerifier>>;
+using RepeatedProof = google::protobuf::RepeatedPtrField<AsymmetricKeyProof>;
+
+int ReadAndHashBuffer(uint8_t* buffer,
+                      int length,
+                      base::File* file,
+                      crypto::SecureHash* hash) {
+  static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
+  int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length);
+  if (read > 0)
+    hash->Update(buffer, read);
+  return read;
+}
+
+// Returns UINT32_MAX in the case of an unexpected EOF or read error, else
+// returns the read uint32.
+uint32_t ReadAndHashLittleEndianUInt32(base::File* file,
+                                       crypto::SecureHash* hash) {
+  uint8_t buffer[4] = {};
+  if (ReadAndHashBuffer(buffer, 4, file, hash) != 4)
+    return UINT32_MAX;
+  return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
+}
+
+// Read to the end of the file, updating the hash and all verifiers.
+bool ReadHashAndVerifyArchive(base::File* file,
+                              crypto::SecureHash* hash,
+                              const VerifierCollection& verifiers) {
+  uint8_t buffer[1 << 12] = {};
+  size_t len = 0;
+  while ((len = ReadAndHashBuffer(buffer, base::size(buffer), file, hash)) >
+         0) {
+    for (auto& verifier : verifiers)
+      verifier->VerifyUpdate(base::make_span(buffer, len));
+  }
+  for (auto& verifier : verifiers) {
+    if (!verifier->VerifyFinal())
+      return false;
+  }
+  return len == 0;
+}
+
+// The remaining contents of a Crx3 file are [header-size][header][archive].
+// [header] is an encoded protocol buffer and contains both a signed and
+// unsigned section. The unsigned section contains a set of key/signature pairs,
+// and the signed section is the encoding of another protocol buffer. All
+// signatures cover [prefix][signed-header-size][signed-header][archive].
+VerifierResult VerifyCrx3(
+    base::File* file,
+    crypto::SecureHash* hash,
+    const std::vector<std::vector<uint8_t>>& required_key_hashes,
+    std::string* public_key,
+    std::string* crx_id,
+    bool require_publisher_key,
+    bool accept_publisher_test_key) {
+  // Parse [header-size] and [header].
+  const uint32_t header_size = ReadAndHashLittleEndianUInt32(file, hash);
+  if (header_size > kMaxHeaderSize)
+    return VerifierResult::ERROR_HEADER_INVALID;
+  std::vector<uint8_t> header_bytes(header_size);
+  // Assuming kMaxHeaderSize can fit in an int, the following cast is safe.
+  if (ReadAndHashBuffer(header_bytes.data(), header_size, file, hash) !=
+      static_cast<int>(header_size))
+    return VerifierResult::ERROR_HEADER_INVALID;
+  CrxFileHeader header;
+  if (!header.ParseFromArray(header_bytes.data(), header_size))
+    return VerifierResult::ERROR_HEADER_INVALID;
+
+  // Parse [signed-header].
+  const std::string& signed_header_data_str = header.signed_header_data();
+  SignedData signed_header_data;
+  if (!signed_header_data.ParseFromString(signed_header_data_str))
+    return VerifierResult::ERROR_HEADER_INVALID;
+  const std::string& crx_id_encoded = signed_header_data.crx_id();
+  const std::string declared_crx_id = id_util::GenerateIdFromHex(
+      base::HexEncode(crx_id_encoded.data(), crx_id_encoded.size()));
+
+  // Create a little-endian representation of [signed-header-size].
+  const int signed_header_size = signed_header_data_str.size();
+  const uint8_t header_size_octets[] = {
+      signed_header_size, signed_header_size >> 8, signed_header_size >> 16,
+      signed_header_size >> 24};
+
+  // Create a set of all required key hashes.
+  std::set<std::vector<uint8_t>> required_key_set(required_key_hashes.begin(),
+                                                  required_key_hashes.end());
+
+  using ProofFetcher = const RepeatedProof& (CrxFileHeader::*)() const;
+  ProofFetcher rsa = &CrxFileHeader::sha256_with_rsa;
+  ProofFetcher ecdsa = &CrxFileHeader::sha256_with_ecdsa;
+
+  std::string public_key_bytes;
+  VerifierCollection verifiers;
+  verifiers.reserve(header.sha256_with_rsa_size() +
+                    header.sha256_with_ecdsa_size());
+  const std::vector<
+      std::pair<ProofFetcher, crypto::SignatureVerifier::SignatureAlgorithm>>
+      proof_types = {
+          std::make_pair(rsa, crypto::SignatureVerifier::RSA_PKCS1_SHA256),
+          std::make_pair(ecdsa, crypto::SignatureVerifier::ECDSA_SHA256)};
+
+  std::vector<uint8_t> publisher_key(std::begin(kPublisherKeyHash),
+                                     std::end(kPublisherKeyHash));
+  base::Optional<std::vector<uint8_t>> publisher_test_key;
+  if (accept_publisher_test_key) {
+    publisher_test_key.emplace(std::begin(kPublisherTestKeyHash),
+                               std::end(kPublisherTestKeyHash));
+  }
+  bool found_publisher_key = false;
+
+  // Initialize all verifiers and update them with
+  // [prefix][signed-header-size][signed-header].
+  // Clear any elements of required_key_set that are encountered, and watch for
+  // the developer key.
+  for (const auto& proof_type : proof_types) {
+    for (const auto& proof : (header.*proof_type.first)()) {
+      const std::string& key = proof.public_key();
+      const std::string& sig = proof.signature();
+      if (id_util::GenerateId(key) == declared_crx_id)
+        public_key_bytes = key;
+      std::vector<uint8_t> key_hash(crypto::kSHA256Length);
+      crypto::SHA256HashString(key, key_hash.data(), key_hash.size());
+      required_key_set.erase(key_hash);
+      DCHECK_EQ(accept_publisher_test_key, publisher_test_key.has_value());
+      found_publisher_key =
+          found_publisher_key || key_hash == publisher_key ||
+          (accept_publisher_test_key && key_hash == *publisher_test_key);
+      auto v = std::make_unique<crypto::SignatureVerifier>();
+      static_assert(sizeof(unsigned char) == sizeof(uint8_t),
+                    "Unsupported char size.");
+      if (!v->VerifyInit(proof_type.second,
+                         base::as_bytes(base::make_span(sig)),
+                         base::as_bytes(base::make_span(key))))
+        return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
+      v->VerifyUpdate(kSignatureContext);
+      v->VerifyUpdate(header_size_octets);
+      v->VerifyUpdate(base::as_bytes(base::make_span(signed_header_data_str)));
+      verifiers.push_back(std::move(v));
+    }
+  }
+  if (public_key_bytes.empty() || !required_key_set.empty())
+    return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
+
+  if (require_publisher_key && !found_publisher_key)
+    return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
+
+  // Update and finalize the verifiers with [archive].
+  if (!ReadHashAndVerifyArchive(file, hash, verifiers))
+    return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED;
+
+  base::Base64Encode(public_key_bytes, public_key);
+  *crx_id = declared_crx_id;
+  return VerifierResult::OK_FULL;
+}
+
+VerifierResult VerifyCrx2(
+    base::File* file,
+    crypto::SecureHash* hash,
+    const std::vector<std::vector<uint8_t>>& required_key_hashes,
+    std::string* public_key,
+    std::string* crx_id) {
+  const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash);
+  if (key_size > kMaxPublicKeySize)
+    return VerifierResult::ERROR_HEADER_INVALID;
+  const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash);
+  if (sig_size > kMaxSignatureSize)
+    return VerifierResult::ERROR_HEADER_INVALID;
+  std::vector<uint8_t> key(key_size);
+  if (ReadAndHashBuffer(key.data(), key_size, file, hash) !=
+      static_cast<int>(key_size))
+    return VerifierResult::ERROR_HEADER_INVALID;
+  for (const auto& expected_hash : required_key_hashes) {
+    // In practice we expect zero or one key_hashes_ for Crx2 files.
+    std::vector<uint8_t> hash(crypto::kSHA256Length);
+    std::unique_ptr<crypto::SecureHash> sha256 =
+        crypto::SecureHash::Create(crypto::SecureHash::SHA256);
+    sha256->Update(key.data(), key.size());
+    sha256->Finish(hash.data(), hash.size());
+    if (hash != expected_hash)
+      return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
+  }
+
+  std::vector<uint8_t> sig(sig_size);
+  if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) !=
+      static_cast<int>(sig_size))
+    return VerifierResult::ERROR_HEADER_INVALID;
+  std::vector<std::unique_ptr<crypto::SignatureVerifier>> verifiers;
+  verifiers.push_back(std::make_unique<crypto::SignatureVerifier>());
+  if (!verifiers[0]->VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1, sig,
+                                key)) {
+    return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
+  }
+
+  if (!ReadHashAndVerifyArchive(file, hash, verifiers))
+    return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED;
+
+  const std::string public_key_bytes(key.begin(), key.end());
+  base::Base64Encode(public_key_bytes, public_key);
+  *crx_id = id_util::GenerateId(public_key_bytes);
+  return VerifierResult::OK_FULL;
+}
+
+}  // namespace
+
+VerifierResult Verify(
+    const base::FilePath& crx_path,
+    const VerifierFormat& format,
+    const std::vector<std::vector<uint8_t>>& required_key_hashes,
+    const std::vector<uint8_t>& required_file_hash,
+    std::string* public_key,
+    std::string* crx_id) {
+  std::string public_key_local;
+  std::string crx_id_local;
+  base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!file.IsValid())
+    return VerifierResult::ERROR_FILE_NOT_READABLE;
+
+  std::unique_ptr<crypto::SecureHash> file_hash =
+      crypto::SecureHash::Create(crypto::SecureHash::SHA256);
+
+  // Magic number.
+  bool diff = false;
+  char buffer[kCrxFileHeaderMagicSize] = {};
+  if (file.ReadAtCurrentPos(buffer, kCrxFileHeaderMagicSize) !=
+      kCrxFileHeaderMagicSize)
+    return VerifierResult::ERROR_HEADER_INVALID;
+  if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrxFileHeaderMagicSize))
+    diff = true;
+  else if (strncmp(buffer, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize))
+    return VerifierResult::ERROR_HEADER_INVALID;
+  file_hash->Update(buffer, sizeof(buffer));
+
+  // Version number.
+  const uint32_t version =
+      ReadAndHashLittleEndianUInt32(&file, file_hash.get());
+  VerifierResult result;
+  if (version == 2)
+    LOG(WARNING) << "File '" << crx_path
+                 << "' is in CRX2 format, which is deprecated and will not be "
+                    "supported in M78+";
+  if (format == VerifierFormat::CRX2_OR_CRX3 &&
+      (version == 2 || (diff && version == 0))) {
+    result = VerifyCrx2(&file, file_hash.get(), required_key_hashes,
+                        &public_key_local, &crx_id_local);
+  } else if (version == 3) {
+    bool require_publisher_key =
+        format == VerifierFormat::CRX3_WITH_PUBLISHER_PROOF ||
+        format == VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF;
+    result =
+        VerifyCrx3(&file, file_hash.get(), required_key_hashes,
+                   &public_key_local, &crx_id_local, require_publisher_key,
+                   format == VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF);
+  } else {
+    result = VerifierResult::ERROR_HEADER_INVALID;
+  }
+  if (result != VerifierResult::OK_FULL)
+    return result;
+
+  // Finalize file hash.
+  uint8_t final_hash[crypto::kSHA256Length] = {};
+  file_hash->Finish(final_hash, sizeof(final_hash));
+  if (!required_file_hash.empty()) {
+    if (required_file_hash.size() != crypto::kSHA256Length)
+      return VerifierResult::ERROR_EXPECTED_HASH_INVALID;
+    if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(),
+                                crypto::kSHA256Length))
+      return VerifierResult::ERROR_FILE_HASH_FAILED;
+  }
+
+  // All is well. Set the out-params and return.
+  if (public_key)
+    *public_key = public_key_local;
+  if (crx_id)
+    *crx_id = crx_id_local;
+  return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL;
+}
+
+}  // namespace crx_file
diff --git a/src/updater_components/crx_file/crx_verifier.h b/src/updater_components/crx_file/crx_verifier.h
new file mode 100644
index 0000000..b770626
--- /dev/null
+++ b/src/updater_components/crx_file/crx_verifier.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef COMPONENTS_CRX_FILE_CRX_VERIFIER_H_
+#define COMPONENTS_CRX_FILE_CRX_VERIFIER_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace crx_file {
+
+enum class VerifierFormat {
+  CRX2_OR_CRX3,                    // Accept Crx2 or Crx3.
+  CRX3,                            // Accept only Crx3.
+  CRX3_WITH_TEST_PUBLISHER_PROOF,  // Accept only Crx3 with a test or production
+                                   // publisher proof.
+  CRX3_WITH_PUBLISHER_PROOF,       // Accept only Crx3 with a production
+                                   // publisher proof.
+};
+
+enum class VerifierResult {
+  OK_FULL,   // The file verifies as a correct full CRX file.
+  OK_DELTA,  // The file verifies as a correct differential CRX file.
+  ERROR_FILE_NOT_READABLE,      // Cannot open the CRX file.
+  ERROR_HEADER_INVALID,         // Failed to parse or understand CRX header.
+  ERROR_EXPECTED_HASH_INVALID,  // Expected hash is not well-formed.
+  ERROR_FILE_HASH_FAILED,       // The file's actual hash != the expected hash.
+  ERROR_SIGNATURE_INITIALIZATION_FAILED,  // A signature or key is malformed.
+  ERROR_SIGNATURE_VERIFICATION_FAILED,    // A signature doesn't match.
+  ERROR_REQUIRED_PROOF_MISSING,           // RequireKeyProof was unsatisfied.
+};
+
+// Verify the file at |crx_path| as a valid Crx of |format|. The Crx must be
+// well-formed, contain no invalid proofs, match the |required_file_hash| (if
+// non-empty), and contain a proof with each of the |required_key_hashes|.
+// If and only if this function returns OK_FULL or OK_DELTA, and only if
+// |public_key| / |crx_id| are non-null, they will be updated to contain the
+// public key (PEM format, without the header/footer) and crx id (encoded in
+// base16 using the characters [a-p]).
+VerifierResult Verify(
+    const base::FilePath& crx_path,
+    const VerifierFormat& format,
+    const std::vector<std::vector<uint8_t>>& required_key_hashes,
+    const std::vector<uint8_t>& required_file_hash,
+    std::string* public_key,
+    std::string* crx_id);
+
+}  // namespace crx_file
+
+#endif  // COMPONENTS_CRX_FILE_CRX_VERIFIER_H_
diff --git a/src/updater_components/crx_file/crx_verifier_unittest.cc b/src/updater_components/crx_file/crx_verifier_unittest.cc
new file mode 100644
index 0000000..8028b7c
--- /dev/null
+++ b/src/updater_components/crx_file/crx_verifier_unittest.cc
@@ -0,0 +1,247 @@
+// 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 "components/crx_file/crx_verifier.h"
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+base::FilePath TestFile(const std::string& file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("crx_file")
+      .AppendASCII(file);
+}
+
+constexpr char kOjjHash[] = "ojjgnpkioondelmggbekfhllhdaimnho";
+constexpr char kOjjKey[] =
+    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA230uN7vYDEhdDlb4/"
+    "+pg2pfL8p0FFzCF/O146NB3D5dPKuLbnNphn0OUzOrDzR/Z1XLVDlDyiA6xnb+qeRp7H8n7Wk/"
+    "/gvVDNArZyForlVqWdaHLhl4dyZoNJwPKsggf30p/"
+    "MxCbNfy2rzFujzn2nguOrJKzWvNt0BFqssrBpzOQl69blBezE2ZYGOnYW8mPgQV29ekIgOfJk2"
+    "GgXoJBQQRRsjoPmUY7GDuEKudEB/"
+    "CmWh3+"
+    "mCsHBHFWbqtGhSN4YCAw3DYQzwdTcIVaIA8f2Uo4AZ4INKkrEPRL8o9mZDYtO2YHIQg8pMSRMa"
+    "6AawBNYi9tZScnmgl5L1qE6z5oIwIDAQAB";
+
+constexpr char kJlnHash[] = "jlnmailbicnpnbggmhfebbomaddckncf";
+constexpr char kJlnKey[] =
+    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtYd4M8wBjlPsc/wxS1/uXKMD6GtI7/"
+    "0GLxRe6UXTYtDk91u/"
+    "FdJRK9jHws+"
+    "FO6Yi3XcJGtMvuojiB1j4bdiYBfvRgfTD4b7krWtWM2udKPBtHI9ikAT5aom5Bda8rCPNyaqXC"
+    "6Ax+KTgQpeeJglYu7TTd/"
+    "AePyvlRHtCKNkcvRQLY0b6hccALqoTzyTueDX12c8Htg76syEPbz7hSIPPfq6KEGvuVSxWAejy"
+    "/y6EhwAdXRLpegul9KmL94OY1G6dpycUKwyKeXOcB6Qj5iKNcOqJAaSLxoOZby4G3cI1BcQpp/"
+    "3vYccJ4qouDMfaanLe8CvFlLp4VOn833aJ8PYpLQIDAQAB";
+
+}  // namespace
+
+namespace crx_file {
+
+using CrxVerifierTest = testing::Test;
+
+TEST_F(CrxVerifierTest, ValidFullCrx2) {
+  const std::vector<std::vector<uint8_t>> keys;
+  const std::vector<uint8_t> hash;
+  std::string public_key;
+  std::string crx_id;
+
+  EXPECT_EQ(VerifierResult::OK_FULL,
+            Verify(TestFile("valid.crx2"), VerifierFormat::CRX2_OR_CRX3, keys,
+                   hash, &public_key, &crx_id));
+  EXPECT_EQ(std::string(kOjjHash), crx_id);
+  EXPECT_EQ(std::string(kOjjKey), public_key);
+}
+
+TEST_F(CrxVerifierTest, ValidFullCrx3) {
+  const std::vector<std::vector<uint8_t>> keys;
+  const std::vector<uint8_t> hash;
+  std::string public_key = "UNSET";
+  std::string crx_id = "UNSET";
+
+  EXPECT_EQ(VerifierResult::OK_FULL, Verify(TestFile("valid_no_publisher.crx3"),
+                                            VerifierFormat::CRX2_OR_CRX3, keys,
+                                            hash, &public_key, &crx_id));
+  EXPECT_EQ(std::string(kOjjHash), crx_id);
+  EXPECT_EQ(std::string(kOjjKey), public_key);
+
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::OK_FULL,
+            Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+                   keys, hash, &public_key, &crx_id));
+  EXPECT_EQ(std::string(kOjjHash), crx_id);
+  EXPECT_EQ(std::string(kOjjKey), public_key);
+}
+
+TEST_F(CrxVerifierTest, Crx3RejectsCrx2) {
+  const std::vector<std::vector<uint8_t>> keys;
+  const std::vector<uint8_t> hash;
+  std::string public_key = "UNSET";
+  std::string crx_id = "UNSET";
+
+  EXPECT_EQ(VerifierResult::ERROR_HEADER_INVALID,
+            Verify(TestFile("valid.crx2"), VerifierFormat::CRX3, keys, hash,
+                   &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, VerifiesFileHash) {
+  const std::vector<std::vector<uint8_t>> keys;
+  std::vector<uint8_t> hash;
+  EXPECT_TRUE(base::HexStringToBytes(
+      "d033c510f9e4ee081ccb60ea2bf530dc2e5cb0e71085b55503c8b13b74515fe4",
+      &hash));
+  std::string public_key = "UNSET";
+  std::string crx_id = "UNSET";
+
+  EXPECT_EQ(VerifierResult::OK_FULL, Verify(TestFile("valid_no_publisher.crx3"),
+                                            VerifierFormat::CRX2_OR_CRX3, keys,
+                                            hash, &public_key, &crx_id));
+  EXPECT_EQ(std::string(kOjjHash), crx_id);
+  EXPECT_EQ(std::string(kOjjKey), public_key);
+
+  hash.clear();
+  EXPECT_TRUE(base::HexStringToBytes(std::string(32, '0'), &hash));
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::ERROR_EXPECTED_HASH_INVALID,
+            Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+                   keys, hash, &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+
+  hash.clear();
+  EXPECT_TRUE(base::HexStringToBytes(std::string(64, '0'), &hash));
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::ERROR_FILE_HASH_FAILED,
+            Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+                   keys, hash, &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, ChecksRequiredKeyHashes) {
+  const std::vector<uint8_t> hash;
+
+  std::vector<uint8_t> good_key;
+  EXPECT_TRUE(base::HexStringToBytes(
+      "e996dfa8eed34bc6614a57bb7308cd7e519bcc690841e1969f7cb173ef16800a",
+      &good_key));
+  const std::vector<std::vector<uint8_t>> good_keys = {good_key};
+  std::string public_key = "UNSET";
+  std::string crx_id = "UNSET";
+  EXPECT_EQ(
+      VerifierResult::OK_FULL,
+      Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX2_OR_CRX3,
+             good_keys, hash, &public_key, &crx_id));
+  EXPECT_EQ(std::string(kOjjHash), crx_id);
+  EXPECT_EQ(std::string(kOjjKey), public_key);
+
+  std::vector<uint8_t> bad_key;
+  EXPECT_TRUE(base::HexStringToBytes(std::string(64, '0'), &bad_key));
+  const std::vector<std::vector<uint8_t>> bad_keys = {bad_key};
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+            Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+                   bad_keys, hash, &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, ChecksPinnedKey) {
+  const std::vector<uint8_t> hash;
+  const std::vector<std::vector<uint8_t>> keys;
+  std::string public_key = "UNSET";
+  std::string crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::OK_FULL,
+            Verify(TestFile("valid_publisher.crx3"),
+                   VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash,
+                   &public_key, &crx_id));
+  EXPECT_EQ(std::string(kOjjHash), crx_id);
+  EXPECT_EQ(std::string(kOjjKey), public_key);
+
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+            Verify(TestFile("valid_test_publisher.crx3"),
+                   VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash,
+                   &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+            Verify(TestFile("valid_no_publisher.crx3"),
+                   VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash,
+                   &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, ChecksPinnedKeyAcceptsTest) {
+  const std::vector<uint8_t> hash;
+  const std::vector<std::vector<uint8_t>> keys;
+  std::string public_key = "UNSET";
+  std::string crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::OK_FULL,
+            Verify(TestFile("valid_publisher.crx3"),
+                   VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF, keys, hash,
+                   &public_key, &crx_id));
+  EXPECT_EQ(std::string(kOjjHash), crx_id);
+  EXPECT_EQ(std::string(kOjjKey), public_key);
+
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::OK_FULL,
+            Verify(TestFile("valid_test_publisher.crx3"),
+                   VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF, keys, hash,
+                   &public_key, &crx_id));
+  EXPECT_EQ(std::string(kJlnHash), crx_id);
+  EXPECT_EQ(std::string(kJlnKey), public_key);
+
+  public_key = "UNSET";
+  crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+            Verify(TestFile("valid_no_publisher.crx3"),
+                   VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF, keys, hash,
+                   &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, NullptrSafe) {
+  const std::vector<uint8_t> hash;
+  const std::vector<std::vector<uint8_t>> keys;
+  EXPECT_EQ(VerifierResult::OK_FULL,
+            Verify(TestFile("valid_publisher.crx3"),
+                   VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash,
+                   nullptr, nullptr));
+}
+
+TEST_F(CrxVerifierTest, RequiresDeveloperKey) {
+  const std::vector<uint8_t> hash;
+  const std::vector<std::vector<uint8_t>> keys;
+  std::string public_key = "UNSET";
+  std::string crx_id = "UNSET";
+  EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+            Verify(TestFile("unsigned.crx3"), VerifierFormat::CRX2_OR_CRX3,
+                   keys, hash, &public_key, &crx_id));
+  EXPECT_EQ("UNSET", crx_id);
+  EXPECT_EQ("UNSET", public_key);
+}
+
+}  // namespace crx_file
diff --git a/src/updater_components/crx_file/id_util.cc b/src/updater_components/crx_file/id_util.cc
new file mode 100644
index 0000000..a6250c1
--- /dev/null
+++ b/src/updater_components/crx_file/id_util.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crx_file/id_util.h"
+
+#include <stdint.h>
+
+#include "base/files/file_path.h"
+#include "base/hash/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+#include "crypto/sha2.h"
+
+namespace {
+
+// Converts a normal hexadecimal string into the alphabet used by extensions.
+// We use the characters 'a'-'p' instead of '0'-'f' to avoid ever having a
+// completely numeric host, since some software interprets that as an IP
+// address.
+static void ConvertHexadecimalToIDAlphabet(std::string* id) {
+  for (auto& ch : *id) {
+    int val;
+    if (base::HexStringToInt(base::StringPiece(&ch, 1), &val)) {
+      ch = 'a' + val;
+    } else {
+      ch = 'a';
+    }
+  }
+}
+
+}  // namespace
+
+namespace crx_file {
+namespace id_util {
+
+// First 16 bytes of SHA256 hashed public key.
+const size_t kIdSize = 16;
+
+std::string GenerateId(base::StringPiece input) {
+  uint8_t hash[kIdSize];
+  crypto::SHA256HashString(input, hash, sizeof(hash));
+  return GenerateIdFromHash(hash, sizeof(hash));
+}
+
+std::string GenerateIdFromHash(const uint8_t* hash, size_t hash_size) {
+  CHECK_GE(hash_size, kIdSize);
+  std::string result = base::HexEncode(hash, kIdSize);
+  ConvertHexadecimalToIDAlphabet(&result);
+  return result;
+}
+
+std::string GenerateIdFromHex(const std::string& input) {
+  std::string output = input;
+  ConvertHexadecimalToIDAlphabet(&output);
+  return output;
+}
+
+std::string GenerateIdForPath(const base::FilePath& path) {
+  base::FilePath new_path = MaybeNormalizePath(path);
+  const base::StringPiece path_bytes(
+      reinterpret_cast<const char*>(new_path.value().data()),
+      new_path.value().size() * sizeof(base::FilePath::CharType));
+  return GenerateId(path_bytes);
+}
+
+std::string HashedIdInHex(const std::string& id) {
+  const std::string id_hash = base::SHA1HashString(id);
+  DCHECK_EQ(base::kSHA1Length, id_hash.length());
+  return base::HexEncode(id_hash.c_str(), id_hash.length());
+}
+
+base::FilePath MaybeNormalizePath(const base::FilePath& path) {
+#if defined(OS_WIN)
+  // Normalize any drive letter to upper-case. We do this for consistency with
+  // net_utils::FilePathToFileURL(), which does the same thing, to make string
+  // comparisons simpler.
+  base::FilePath::StringType path_str = path.value();
+  if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' &&
+      path_str[1] == L':')
+    path_str[0] = towupper(path_str[0]);
+
+  return base::FilePath(path_str);
+#else
+  return path;
+#endif
+}
+
+bool IdIsValid(const std::string& id) {
+  // Verify that the id is legal.
+  if (id.size() != (crx_file::id_util::kIdSize * 2))
+    return false;
+
+  for (size_t i = 0; i < id.size(); i++) {
+    const char ch = base::ToLowerASCII(id[i]);
+    if (ch < 'a' || ch > 'p')
+      return false;
+  }
+
+  return true;
+}
+
+}  // namespace id_util
+}  // namespace crx_file
diff --git a/src/updater_components/crx_file/id_util.h b/src/updater_components/crx_file/id_util.h
new file mode 100644
index 0000000..6370560
--- /dev/null
+++ b/src/updater_components/crx_file/id_util.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRX_FILE_ID_UTIL_H_
+#define COMPONENTS_CRX_FILE_ID_UTIL_H_
+
+#include "base/strings/string_piece.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+namespace base {
+class FilePath;
+}
+
+namespace crx_file {
+namespace id_util {
+
+// The number of bytes in a legal id.
+extern const size_t kIdSize;
+
+// Generates an extension ID from arbitrary input. The same input string will
+// always generate the same output ID.
+std::string GenerateId(base::StringPiece input);
+
+// Generates an ID from a HEX string. The same input string will always generate
+// the same output ID.
+std::string GenerateIdFromHex(const std::string& input);
+
+// Generates an ID from the first |kIdSize| bytes of a SHA256 hash.
+// |hash_size| must be at least |kIdSize|.
+std::string GenerateIdFromHash(const uint8_t* hash, size_t hash_size);
+
+// Generates an ID for an extension in the given path.
+// Used while developing extensions, before they have a key.
+std::string GenerateIdForPath(const base::FilePath& path);
+
+// Returns the hash of an extension ID in hex.
+std::string HashedIdInHex(const std::string& id);
+
+// Normalizes the path for use by the extension. On Windows, this will make
+// sure the drive letter is uppercase.
+base::FilePath MaybeNormalizePath(const base::FilePath& path);
+
+// Checks if |id| is a valid extension-id. Extension-ids are used for anything
+// that comes in a CRX file, including apps, extensions, and components.
+bool IdIsValid(const std::string& id);
+
+}  // namespace id_util
+}  // namespace crx_file
+
+#endif  // COMPONENTS_CRX_FILE_ID_UTIL_H_
diff --git a/src/updater_components/crx_file/id_util_unittest.cc b/src/updater_components/crx_file/id_util_unittest.cc
new file mode 100644
index 0000000..ee31110
--- /dev/null
+++ b/src/updater_components/crx_file/id_util_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crx_file/id_util.h"
+
+#include <stdint.h>
+
+#include "base/stl_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crx_file {
+namespace id_util {
+
+TEST(IDUtilTest, GenerateID) {
+  const uint8_t public_key_info[] = {
+      0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+      0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
+      0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, 0x20, 0xdc, 0x7c, 0x9b,
+      0x0c, 0xdc, 0x51, 0x61, 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
+      0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, 0x85, 0x7b, 0x0c, 0x04,
+      0x13, 0x3f, 0x8d, 0xf4, 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
+      0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, 0x9e, 0x36, 0x74, 0x30,
+      0xda, 0x8a, 0x31, 0x4f, 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
+      0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, 0xb2, 0x65, 0x7a, 0x89,
+      0x4e, 0xb6, 0x47, 0xff, 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
+      0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, 0x96, 0xd0, 0xd6, 0x14,
+      0x6f, 0x13, 0x8d, 0xc5, 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
+      0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, 0xe7, 0x1f, 0x0f, 0xe6,
+      0x0f, 0x02, 0x03, 0x01, 0x00, 0x01};
+  std::string extension_id =
+      GenerateId(std::string(reinterpret_cast<const char*>(&public_key_info[0]),
+                             base::size(public_key_info)));
+  EXPECT_EQ("melddjfinppjdikinhbgehiennejpfhp", extension_id);
+
+  EXPECT_EQ("daibjpdaanagajckigeiigphanababab",
+            GenerateIdFromHash(public_key_info, sizeof(public_key_info)));
+
+  EXPECT_EQ("jpignaibiiemhngfjkcpokkamffknabf", GenerateId("test"));
+
+  EXPECT_EQ("ncocknphbhhlhkikpnnlmbcnbgdempcd", GenerateId("_"));
+
+  EXPECT_EQ("a", GenerateIdFromHex("_"));
+
+  EXPECT_EQ(
+      "bjbdkfoakgmkndalgpadobhgbhhoanhongcmfnghaakjmggnkffgnhmdpfngkeho",
+      GenerateIdFromHex(
+          "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e"));
+
+  EXPECT_EQ(
+      "jimneklojkjdibfkgiiophfhjhbdgcfi",
+      GenerateId("this_string_is_longer_than_a_single_sha256_hash_digest"));
+}
+
+}  // namespace id_util
+}  // namespace crx_file
diff --git a/src/updater_components/prefs/BUILD.gn b/src/updater_components/prefs/BUILD.gn
new file mode 100644
index 0000000..1954a7f
--- /dev/null
+++ b/src/updater_components/prefs/BUILD.gn
@@ -0,0 +1,107 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+component("prefs") {
+  sources = [
+    "command_line_pref_store.cc",
+    "command_line_pref_store.h",
+    "default_pref_store.cc",
+    "default_pref_store.h",
+    "in_memory_pref_store.cc",
+    "in_memory_pref_store.h",
+    "json_pref_store.cc",
+    "json_pref_store.h",
+    "overlay_user_pref_store.cc",
+    "overlay_user_pref_store.h",
+    "persistent_pref_store.cc",
+    "persistent_pref_store.h",
+    "pref_change_registrar.cc",
+    "pref_change_registrar.h",
+    "pref_filter.h",
+    "pref_member.cc",
+    "pref_member.h",
+    "pref_notifier.h",
+    "pref_notifier_impl.cc",
+    "pref_notifier_impl.h",
+    "pref_observer.h",
+    "pref_registry.cc",
+    "pref_registry.h",
+    "pref_registry_simple.cc",
+    "pref_registry_simple.h",
+    "pref_service.cc",
+    "pref_service.h",
+    "pref_service_factory.cc",
+    "pref_service_factory.h",
+    "pref_store.cc",
+    "pref_store.h",
+    "pref_value_map.cc",
+    "pref_value_map.h",
+    "pref_value_store.cc",
+    "pref_value_store.h",
+    "prefs_export.h",
+    "scoped_user_pref_update.cc",
+    "scoped_user_pref_update.h",
+    "value_map_pref_store.cc",
+    "value_map_pref_store.h",
+    "writeable_pref_store.cc",
+    "writeable_pref_store.h",
+  ]
+
+  defines = [ "COMPONENTS_PREFS_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//base/util/values:values_util",
+  ]
+}
+
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "mock_pref_change_callback.cc",
+    "mock_pref_change_callback.h",
+    "pref_store_observer_mock.cc",
+    "pref_store_observer_mock.h",
+    "testing_pref_service.cc",
+    "testing_pref_service.h",
+    "testing_pref_store.cc",
+    "testing_pref_store.h",
+  ]
+
+  public_deps = [
+    ":prefs",
+  ]
+  deps = [
+    "//base",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "default_pref_store_unittest.cc",
+    "in_memory_pref_store_unittest.cc",
+    "json_pref_store_unittest.cc",
+    "overlay_user_pref_store_unittest.cc",
+    "persistent_pref_store_unittest.cc",
+    "persistent_pref_store_unittest.h",
+    "pref_change_registrar_unittest.cc",
+    "pref_member_unittest.cc",
+    "pref_notifier_impl_unittest.cc",
+    "pref_service_unittest.cc",
+    "pref_value_map_unittest.cc",
+    "pref_value_store_unittest.cc",
+    "scoped_user_pref_update_unittest.cc",
+  ]
+
+  deps = [
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/src/updater_components/prefs/command_line_pref_store.cc b/src/updater_components/prefs/command_line_pref_store.cc
new file mode 100644
index 0000000..79fc10c
--- /dev/null
+++ b/src/updater_components/prefs/command_line_pref_store.cc
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/prefs/command_line_pref_store.h"
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+
+CommandLinePrefStore::CommandLinePrefStore(
+    const base::CommandLine* command_line)
+    : command_line_(command_line) {}
+
+CommandLinePrefStore::~CommandLinePrefStore() {}
+
+void CommandLinePrefStore::ApplyStringSwitches(
+    const CommandLinePrefStore::SwitchToPreferenceMapEntry string_switch[],
+    size_t size) {
+  for (size_t i = 0; i < size; ++i) {
+    if (command_line_->HasSwitch(string_switch[i].switch_name)) {
+      SetValue(string_switch[i].preference_path,
+               std::make_unique<base::Value>(command_line_->GetSwitchValueASCII(
+                   string_switch[i].switch_name)),
+               WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    }
+  }
+}
+
+void CommandLinePrefStore::ApplyPathSwitches(
+    const CommandLinePrefStore::SwitchToPreferenceMapEntry path_switch[],
+    size_t size) {
+  for (size_t i = 0; i < size; ++i) {
+    if (command_line_->HasSwitch(path_switch[i].switch_name)) {
+      SetValue(path_switch[i].preference_path,
+               std::make_unique<base::Value>(
+                   command_line_->GetSwitchValuePath(path_switch[i].switch_name)
+                       .value()),
+               WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    }
+  }
+}
+
+void CommandLinePrefStore::ApplyIntegerSwitches(
+    const CommandLinePrefStore::SwitchToPreferenceMapEntry integer_switch[],
+    size_t size) {
+  for (size_t i = 0; i < size; ++i) {
+    if (command_line_->HasSwitch(integer_switch[i].switch_name)) {
+      std::string str_value = command_line_->GetSwitchValueASCII(
+          integer_switch[i].switch_name);
+      int int_value = 0;
+      if (!base::StringToInt(str_value, &int_value)) {
+        LOG(ERROR) << "The value " << str_value << " of "
+                   << integer_switch[i].switch_name
+                   << " can not be converted to integer, ignoring!";
+        continue;
+      }
+      SetValue(integer_switch[i].preference_path,
+               std::make_unique<base::Value>(int_value),
+               WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    }
+  }
+}
+
+void CommandLinePrefStore::ApplyBooleanSwitches(
+      const CommandLinePrefStore::BooleanSwitchToPreferenceMapEntry
+      boolean_switch_map[], size_t size) {
+  for (size_t i = 0; i < size; ++i) {
+    if (command_line_->HasSwitch(boolean_switch_map[i].switch_name)) {
+      SetValue(boolean_switch_map[i].preference_path,
+               std::make_unique<base::Value>(boolean_switch_map[i].set_value),
+               WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+    }
+  }
+}
diff --git a/src/updater_components/prefs/command_line_pref_store.h b/src/updater_components/prefs/command_line_pref_store.h
new file mode 100644
index 0000000..03d5da9
--- /dev/null
+++ b/src/updater_components/prefs/command_line_pref_store.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PREFS_COMMAND_LINE_PREF_STORE_H_
+#define COMPONENTS_PREFS_COMMAND_LINE_PREF_STORE_H_
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "components/prefs/value_map_pref_store.h"
+
+// Base class for a PrefStore that maps command line switches to preferences.
+// The Apply...Switches() methods can be called by subclasses with their own
+// maps, or delegated to other code.
+class COMPONENTS_PREFS_EXPORT CommandLinePrefStore : public ValueMapPrefStore {
+ public:
+  struct SwitchToPreferenceMapEntry {
+    const char* switch_name;
+    const char* preference_path;
+  };
+
+  // |set_value| indicates what the preference should be set to if the switch
+  // is present.
+  struct BooleanSwitchToPreferenceMapEntry {
+    const char* switch_name;
+    const char* preference_path;
+    bool set_value;
+  };
+
+  // Apply command-line switches to the corresponding preferences of the switch
+  // map, where the value associated with the switch is a string.
+  void ApplyStringSwitches(
+      const SwitchToPreferenceMapEntry string_switch_map[], size_t size);
+
+  // Apply command-line switches to the corresponding preferences of the switch
+  // map, where the value associated with the switch is a path.
+  void ApplyPathSwitches(const SwitchToPreferenceMapEntry path_switch_map[],
+                         size_t size);
+
+  // Apply command-line switches to the corresponding preferences of the switch
+  // map, where the value associated with the switch is an integer.
+  void ApplyIntegerSwitches(
+      const SwitchToPreferenceMapEntry integer_switch_map[], size_t size);
+
+  // Apply command-line switches to the corresponding preferences of the
+  // boolean switch map.
+  void ApplyBooleanSwitches(
+      const BooleanSwitchToPreferenceMapEntry boolean_switch_map[],
+      size_t size);
+
+
+ protected:
+  explicit CommandLinePrefStore(const base::CommandLine* command_line);
+  ~CommandLinePrefStore() override;
+
+  const base::CommandLine* command_line() { return command_line_; }
+
+ private:
+  // Weak reference.
+  const base::CommandLine* command_line_;
+
+  DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_COMMAND_LINE_PREF_STORE_H_
diff --git a/src/updater_components/prefs/default_pref_store.cc b/src/updater_components/prefs/default_pref_store.cc
new file mode 100644
index 0000000..d8d13ec
--- /dev/null
+++ b/src/updater_components/prefs/default_pref_store.cc
@@ -0,0 +1,59 @@
+// 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 "components/prefs/default_pref_store.h"
+
+#include <utility>
+
+#include "base/logging.h"
+
+using base::Value;
+
+DefaultPrefStore::DefaultPrefStore() {}
+
+bool DefaultPrefStore::GetValue(const std::string& key,
+                                const Value** result) const {
+  return prefs_.GetValue(key, result);
+}
+
+std::unique_ptr<base::DictionaryValue> DefaultPrefStore::GetValues() const {
+  return prefs_.AsDictionaryValue();
+}
+
+void DefaultPrefStore::AddObserver(PrefStore::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void DefaultPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool DefaultPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
+}
+
+void DefaultPrefStore::SetDefaultValue(const std::string& key, Value value) {
+  DCHECK(!GetValue(key, nullptr));
+  prefs_.SetValue(key, std::move(value));
+}
+
+void DefaultPrefStore::ReplaceDefaultValue(const std::string& key,
+                                           Value value) {
+  DCHECK(GetValue(key, nullptr));
+  bool notify = prefs_.SetValue(key, std::move(value));
+  if (notify) {
+    for (Observer& observer : observers_)
+      observer.OnPrefValueChanged(key);
+  }
+}
+
+DefaultPrefStore::const_iterator DefaultPrefStore::begin() const {
+  return prefs_.begin();
+}
+
+DefaultPrefStore::const_iterator DefaultPrefStore::end() const {
+  return prefs_.end();
+}
+
+DefaultPrefStore::~DefaultPrefStore() {}
diff --git a/src/updater_components/prefs/default_pref_store.h b/src/updater_components/prefs/default_pref_store.h
new file mode 100644
index 0000000..17ba3ec
--- /dev/null
+++ b/src/updater_components/prefs/default_pref_store.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_
+#define COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/values.h"
+#include "components/prefs/pref_store.h"
+#include "components/prefs/pref_value_map.h"
+#include "components/prefs/prefs_export.h"
+
+// Used within a PrefRegistry to keep track of default preference values.
+class COMPONENTS_PREFS_EXPORT DefaultPrefStore : public PrefStore {
+ public:
+  typedef PrefValueMap::const_iterator const_iterator;
+
+  DefaultPrefStore();
+
+  // PrefStore implementation:
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+
+  // Sets a |value| for |key|. Should only be called if a value has not been
+  // set yet; otherwise call ReplaceDefaultValue().
+  void SetDefaultValue(const std::string& key, base::Value value);
+
+  // Replaces the the value for |key| with a new value. Should only be called
+  // if a value has alreday been set; otherwise call SetDefaultValue().
+  void ReplaceDefaultValue(const std::string& key, base::Value value);
+
+  const_iterator begin() const;
+  const_iterator end() const;
+
+ private:
+  ~DefaultPrefStore() override;
+
+  PrefValueMap prefs_;
+
+  base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultPrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_
diff --git a/src/updater_components/prefs/default_pref_store_unittest.cc b/src/updater_components/prefs/default_pref_store_unittest.cc
new file mode 100644
index 0000000..bc40c4c
--- /dev/null
+++ b/src/updater_components/prefs/default_pref_store_unittest.cc
@@ -0,0 +1,66 @@
+// 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 "base/macros.h"
+#include "components/prefs/default_pref_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Value;
+
+namespace {
+
+class MockPrefStoreObserver : public PrefStore::Observer {
+ public:
+  explicit MockPrefStoreObserver(DefaultPrefStore* pref_store);
+  ~MockPrefStoreObserver() override;
+
+  int change_count() {
+    return change_count_;
+  }
+
+  // PrefStore::Observer implementation:
+  void OnPrefValueChanged(const std::string& key) override;
+  void OnInitializationCompleted(bool succeeded) override {}
+
+ private:
+  DefaultPrefStore* pref_store_;
+
+  int change_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockPrefStoreObserver);
+};
+
+MockPrefStoreObserver::MockPrefStoreObserver(DefaultPrefStore* pref_store)
+    : pref_store_(pref_store), change_count_(0) {
+  pref_store_->AddObserver(this);
+}
+
+MockPrefStoreObserver::~MockPrefStoreObserver() {
+  pref_store_->RemoveObserver(this);
+}
+
+void MockPrefStoreObserver::OnPrefValueChanged(const std::string& key) {
+  change_count_++;
+}
+
+}  // namespace
+
+TEST(DefaultPrefStoreTest, NotifyPrefValueChanged) {
+  scoped_refptr<DefaultPrefStore> pref_store(new DefaultPrefStore);
+  MockPrefStoreObserver observer(pref_store.get());
+  std::string kPrefKey("pref_key");
+
+  // Setting a default value shouldn't send a change notification.
+  pref_store->SetDefaultValue(kPrefKey, Value("foo"));
+  EXPECT_EQ(0, observer.change_count());
+
+  // Replacing the default value should send a change notification...
+  pref_store->ReplaceDefaultValue(kPrefKey, Value("bar"));
+  EXPECT_EQ(1, observer.change_count());
+
+  // But only if the value actually changed.
+  pref_store->ReplaceDefaultValue(kPrefKey, Value("bar"));
+  EXPECT_EQ(1, observer.change_count());
+}
+
diff --git a/src/updater_components/prefs/in_memory_pref_store.cc b/src/updater_components/prefs/in_memory_pref_store.cc
new file mode 100644
index 0000000..20d1eea
--- /dev/null
+++ b/src/updater_components/prefs/in_memory_pref_store.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/prefs/in_memory_pref_store.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/values.h"
+
+InMemoryPrefStore::InMemoryPrefStore() {}
+
+InMemoryPrefStore::~InMemoryPrefStore() {}
+
+bool InMemoryPrefStore::GetValue(const std::string& key,
+                                 const base::Value** value) const {
+  return prefs_.GetValue(key, value);
+}
+
+std::unique_ptr<base::DictionaryValue> InMemoryPrefStore::GetValues() const {
+  return prefs_.AsDictionaryValue();
+}
+
+bool InMemoryPrefStore::GetMutableValue(const std::string& key,
+                                        base::Value** value) {
+  return prefs_.GetValue(key, value);
+}
+
+void InMemoryPrefStore::AddObserver(PrefStore::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void InMemoryPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool InMemoryPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
+}
+
+bool InMemoryPrefStore::IsInitializationComplete() const {
+  return true;
+}
+
+void InMemoryPrefStore::SetValue(const std::string& key,
+                                 std::unique_ptr<base::Value> value,
+                                 uint32_t flags) {
+  DCHECK(value);
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value))))
+    ReportValueChanged(key, flags);
+}
+
+void InMemoryPrefStore::SetValueSilently(const std::string& key,
+                                      std::unique_ptr<base::Value> value,
+                                      uint32_t flags) {
+  DCHECK(value);
+  prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)));
+}
+
+void InMemoryPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+  if (prefs_.RemoveValue(key))
+    ReportValueChanged(key, flags);
+}
+
+bool InMemoryPrefStore::ReadOnly() const {
+  return false;
+}
+
+PersistentPrefStore::PrefReadError InMemoryPrefStore::GetReadError() const {
+  return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+PersistentPrefStore::PrefReadError InMemoryPrefStore::ReadPrefs() {
+  return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+void InMemoryPrefStore::ReportValueChanged(const std::string& key,
+                                           uint32_t flags) {
+  for (Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+}
+
+bool InMemoryPrefStore::IsInMemoryPrefStore() const {
+  return true;
+}
diff --git a/src/updater_components/prefs/in_memory_pref_store.h b/src/updater_components/prefs/in_memory_pref_store.h
new file mode 100644
index 0000000..c623adb
--- /dev/null
+++ b/src/updater_components/prefs/in_memory_pref_store.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PREFS_IN_MEMORY_PREF_STORE_H_
+#define COMPONENTS_PREFS_IN_MEMORY_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_value_map.h"
+
+// A light-weight prefstore implementation that keeps preferences
+// in a memory backed store. This is not a persistent prefstore -- we
+// subclass the PersistentPrefStore here since it is needed by the
+// PrefService, which in turn is needed by various components.
+class COMPONENTS_PREFS_EXPORT InMemoryPrefStore : public PersistentPrefStore {
+ public:
+  InMemoryPrefStore();
+
+  // PrefStore implementation.
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+  bool IsInitializationComplete() const override;
+
+  // PersistentPrefStore implementation.
+  bool GetMutableValue(const std::string& key, base::Value** result) override;
+  void ReportValueChanged(const std::string& key, uint32_t flags) override;
+  void SetValue(const std::string& key,
+                std::unique_ptr<base::Value> value,
+                uint32_t flags) override;
+  void SetValueSilently(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) override;
+  void RemoveValue(const std::string& key, uint32_t flags) override;
+  bool ReadOnly() const override;
+  PrefReadError GetReadError() const override;
+  PersistentPrefStore::PrefReadError ReadPrefs() override;
+  void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override {}
+  void SchedulePendingLossyWrites() override {}
+  void ClearMutableValues() override {}
+  void OnStoreDeletionFromDisk() override {}
+  bool IsInMemoryPrefStore() const override;
+
+ protected:
+  ~InMemoryPrefStore() override;
+
+ private:
+  // Stores the preference values.
+  PrefValueMap prefs_;
+
+  base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(InMemoryPrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_IN_MEMORY_PREF_STORE_H_
diff --git a/src/updater_components/prefs/in_memory_pref_store_unittest.cc b/src/updater_components/prefs/in_memory_pref_store_unittest.cc
new file mode 100644
index 0000000..dffe5fa
--- /dev/null
+++ b/src/updater_components/prefs/in_memory_pref_store_unittest.cc
@@ -0,0 +1,113 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/prefs/in_memory_pref_store.h"
+
+#include <memory>
+
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "components/prefs/persistent_pref_store_unittest.h"
+#include "components/prefs/pref_store_observer_mock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const char kTestPref[] = "test.pref";
+
+class InMemoryPrefStoreTest : public testing::Test {
+ public:
+  InMemoryPrefStoreTest() { }
+
+  void SetUp() override { store_ = new InMemoryPrefStore(); }
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<InMemoryPrefStore> store_;
+  PrefStoreObserverMock observer_;
+};
+
+TEST_F(InMemoryPrefStoreTest, SetGetValue) {
+  const base::Value* value = nullptr;
+  base::Value* mutable_value = nullptr;
+  EXPECT_FALSE(store_->GetValue(kTestPref, &value));
+  EXPECT_FALSE(store_->GetMutableValue(kTestPref, &mutable_value));
+
+  store_->SetValue(kTestPref, std::make_unique<base::Value>(42),
+                   WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(store_->GetValue(kTestPref, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+  EXPECT_TRUE(store_->GetMutableValue(kTestPref, &mutable_value));
+  EXPECT_TRUE(base::Value(42).Equals(mutable_value));
+
+  store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_FALSE(store_->GetValue(kTestPref, &value));
+  EXPECT_FALSE(store_->GetMutableValue(kTestPref, &mutable_value));
+}
+
+TEST_F(InMemoryPrefStoreTest, GetSetObserver) {
+  // Starts with no observers.
+  EXPECT_FALSE(store_->HasObservers());
+
+  // Add one.
+  store_->AddObserver(&observer_);
+  EXPECT_TRUE(store_->HasObservers());
+
+  // Remove only observer.
+  store_->RemoveObserver(&observer_);
+  EXPECT_FALSE(store_->HasObservers());
+}
+
+TEST_F(InMemoryPrefStoreTest, CallObserver) {
+  // With observer included.
+  store_->AddObserver(&observer_);
+
+  // Triggers on SetValue.
+  store_->SetValue(kTestPref, std::make_unique<base::Value>(42),
+                   WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  observer_.VerifyAndResetChangedKey(kTestPref);
+
+  // And RemoveValue.
+  store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  observer_.VerifyAndResetChangedKey(kTestPref);
+
+  // But not SetValueSilently.
+  store_->SetValueSilently(kTestPref, std::make_unique<base::Value>(42),
+                           WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_EQ(0u, observer_.changed_keys.size());
+
+  // On multiple RemoveValues only the first one triggers observer.
+  store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  observer_.VerifyAndResetChangedKey(kTestPref);
+  store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_EQ(0u, observer_.changed_keys.size());
+
+  // Doesn't make call on removed observers.
+  store_->RemoveObserver(&observer_);
+  store_->SetValue(kTestPref, std::make_unique<base::Value>(42),
+                   WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  store_->RemoveValue(kTestPref, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_EQ(0u, observer_.changed_keys.size());
+}
+
+TEST_F(InMemoryPrefStoreTest, Initialization) {
+  EXPECT_TRUE(store_->IsInitializationComplete());
+}
+
+TEST_F(InMemoryPrefStoreTest, ReadOnly) {
+  EXPECT_FALSE(store_->ReadOnly());
+}
+
+TEST_F(InMemoryPrefStoreTest, GetReadError) {
+  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, store_->GetReadError());
+}
+
+TEST_F(InMemoryPrefStoreTest, ReadPrefs) {
+  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, store_->ReadPrefs());
+}
+
+TEST_F(InMemoryPrefStoreTest, CommitPendingWriteWithCallback) {
+  TestCommitPendingWriteWithCallback(store_.get(), &task_environment_);
+}
+
+}  // namespace
diff --git a/src/updater_components/prefs/ios/BUILD.gn b/src/updater_components/prefs/ios/BUILD.gn
new file mode 100644
index 0000000..82aa654
--- /dev/null
+++ b/src/updater_components/prefs/ios/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2018 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.
+
+source_set("ios") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "pref_observer_bridge.h",
+    "pref_observer_bridge.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/prefs",
+  ]
+}
diff --git a/src/updater_components/prefs/ios/pref_observer_bridge.h b/src/updater_components/prefs/ios/pref_observer_bridge.h
new file mode 100644
index 0000000..425ef87
--- /dev/null
+++ b/src/updater_components/prefs/ios/pref_observer_bridge.h
@@ -0,0 +1,32 @@
+// Copyright 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.
+
+#ifndef COMPONENTS_PREFS_IOS_PREF_OBSERVER_BRIDGE_H_
+#define COMPONENTS_PREFS_IOS_PREF_OBSERVER_BRIDGE_H_
+
+#import <Foundation/Foundation.h>
+
+#include <string>
+
+class PrefChangeRegistrar;
+
+@protocol PrefObserverDelegate
+- (void)onPreferenceChanged:(const std::string&)preferenceName;
+@end
+
+class PrefObserverBridge {
+ public:
+  explicit PrefObserverBridge(id<PrefObserverDelegate> delegate);
+  virtual ~PrefObserverBridge();
+
+  virtual void ObserveChangesForPreference(const std::string& pref_name,
+                                           PrefChangeRegistrar* registrar);
+
+ private:
+  virtual void OnPreferenceChanged(const std::string& pref_name);
+
+  __weak id<PrefObserverDelegate> delegate_ = nil;
+};
+
+#endif  // COMPONENTS_PREFS_IOS_PREF_OBSERVER_BRIDGE_H_
diff --git a/src/updater_components/prefs/ios/pref_observer_bridge.mm b/src/updater_components/prefs/ios/pref_observer_bridge.mm
new file mode 100644
index 0000000..852309f
--- /dev/null
+++ b/src/updater_components/prefs/ios/pref_observer_bridge.mm
@@ -0,0 +1,29 @@
+// Copyright 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.
+
+#import "components/prefs/ios/pref_observer_bridge.h"
+
+#include "base/bind.h"
+#include "components/prefs/pref_change_registrar.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+PrefObserverBridge::PrefObserverBridge(id<PrefObserverDelegate> delegate)
+    : delegate_(delegate) {}
+
+PrefObserverBridge::~PrefObserverBridge() {}
+
+void PrefObserverBridge::ObserveChangesForPreference(
+    const std::string& pref_name,
+    PrefChangeRegistrar* registrar) {
+  PrefChangeRegistrar::NamedChangeCallback callback = base::BindRepeating(
+      &PrefObserverBridge::OnPreferenceChanged, base::Unretained(this));
+  registrar->Add(pref_name.c_str(), callback);
+}
+
+void PrefObserverBridge::OnPreferenceChanged(const std::string& pref_name) {
+  [delegate_ onPreferenceChanged:pref_name];
+}
diff --git a/src/updater_components/prefs/json_pref_store.cc b/src/updater_components/prefs/json_pref_store.cc
new file mode 100644
index 0000000..0dcdb17
--- /dev/null
+++ b/src/updater_components/prefs/json_pref_store.cc
@@ -0,0 +1,519 @@
+// 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 "components/prefs/json_pref_store.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/default_clock.h"
+#include "base/values.h"
+#include "components/prefs/pref_filter.h"
+
+// Result returned from internal read tasks.
+struct JsonPrefStore::ReadResult {
+ public:
+  ReadResult();
+  ~ReadResult();
+
+  std::unique_ptr<base::Value> value;
+  PrefReadError error;
+  bool no_dir;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ReadResult);
+};
+
+JsonPrefStore::ReadResult::ReadResult()
+    : error(PersistentPrefStore::PREF_READ_ERROR_NONE), no_dir(false) {
+}
+
+JsonPrefStore::ReadResult::~ReadResult() {
+}
+
+namespace {
+
+// Some extensions we'll tack on to copies of the Preferences files.
+const base::FilePath::CharType kBadExtension[] = FILE_PATH_LITERAL("bad");
+
+PersistentPrefStore::PrefReadError HandleReadErrors(
+    const base::Value* value,
+    const base::FilePath& path,
+    int error_code,
+    const std::string& error_msg) {
+  if (!value) {
+    DVLOG(1) << "Error while loading JSON file: " << error_msg
+             << ", file: " << path.value();
+    switch (error_code) {
+      case JSONFileValueDeserializer::JSON_ACCESS_DENIED:
+        return PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
+      case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE:
+        return PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
+      case JSONFileValueDeserializer::JSON_FILE_LOCKED:
+        return PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
+      case JSONFileValueDeserializer::JSON_NO_SUCH_FILE:
+        return PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
+      default:
+        // JSON errors indicate file corruption of some sort.
+        // Since the file is corrupt, move it to the side and continue with
+        // empty preferences.  This will result in them losing their settings.
+        // We keep the old file for possible support and debugging assistance
+        // as well as to detect if they're seeing these errors repeatedly.
+        // TODO(erikkay) Instead, use the last known good file.
+        base::FilePath bad = path.ReplaceExtension(kBadExtension);
+
+        // If they've ever had a parse error before, put them in another bucket.
+        // TODO(erikkay) if we keep this error checking for very long, we may
+        // want to differentiate between recent and long ago errors.
+        bool bad_existed = base::PathExists(bad);
+        base::Move(path, bad);
+        return bad_existed ? PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT
+                           : PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
+    }
+  }
+  if (!value->is_dict())
+    return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
+  return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+// Records a sample for |size| in the Settings.JsonDataReadSizeKilobytes
+// histogram suffixed with the base name of the JSON file under |path|.
+void RecordJsonDataSizeHistogram(const base::FilePath& path, size_t size) {
+  std::string spaceless_basename;
+  base::ReplaceChars(path.BaseName().MaybeAsASCII(), " ", "_",
+                     &spaceless_basename);
+
+  // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  // This histogram is expired but the code was intentionally left behind so
+  // it can be re-enabled on Stable in a single config tweak if needed.
+  base::HistogramBase* histogram = base::Histogram::FactoryGet(
+      "Settings.JsonDataReadSizeKilobytes." + spaceless_basename, 1, 10000, 50,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram->Add(static_cast<int>(size) / 1024);
+}
+
+std::unique_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk(
+    const base::FilePath& path) {
+  int error_code;
+  std::string error_msg;
+  std::unique_ptr<JsonPrefStore::ReadResult> read_result(
+      new JsonPrefStore::ReadResult);
+  JSONFileValueDeserializer deserializer(path);
+  read_result->value = deserializer.Deserialize(&error_code, &error_msg);
+  read_result->error =
+      HandleReadErrors(read_result->value.get(), path, error_code, error_msg);
+  read_result->no_dir = !base::PathExists(path.DirName());
+
+  if (read_result->error == PersistentPrefStore::PREF_READ_ERROR_NONE)
+    RecordJsonDataSizeHistogram(path, deserializer.get_last_read_size());
+
+  return read_result;
+}
+
+}  // namespace
+
+JsonPrefStore::JsonPrefStore(
+    const base::FilePath& pref_filename,
+    std::unique_ptr<PrefFilter> pref_filter,
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner)
+    : path_(pref_filename),
+      file_task_runner_(std::move(file_task_runner)),
+      prefs_(new base::DictionaryValue()),
+      read_only_(false),
+      writer_(pref_filename, file_task_runner_),
+      pref_filter_(std::move(pref_filter)),
+      initialized_(false),
+      filtering_in_progress_(false),
+      pending_lossy_write_(false),
+      read_error_(PREF_READ_ERROR_NONE),
+      has_pending_write_reply_(false) {
+  DCHECK(!path_.empty());
+}
+
+bool JsonPrefStore::GetValue(const std::string& key,
+                             const base::Value** result) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::Value* tmp = nullptr;
+  if (!prefs_->Get(key, &tmp))
+    return false;
+
+  if (result)
+    *result = tmp;
+  return true;
+}
+
+std::unique_ptr<base::DictionaryValue> JsonPrefStore::GetValues() const {
+  return prefs_->CreateDeepCopy();
+}
+
+void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  observers_.AddObserver(observer);
+}
+
+void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  observers_.RemoveObserver(observer);
+}
+
+bool JsonPrefStore::HasObservers() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return observers_.might_have_observers();
+}
+
+bool JsonPrefStore::IsInitializationComplete() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return initialized_;
+}
+
+bool JsonPrefStore::GetMutableValue(const std::string& key,
+                                    base::Value** result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return prefs_->Get(key, result);
+}
+
+void JsonPrefStore::SetValue(const std::string& key,
+                             std::unique_ptr<base::Value> value,
+                             uint32_t flags) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  DCHECK(value);
+  base::Value* old_value = nullptr;
+  prefs_->Get(key, &old_value);
+  if (!old_value || !value->Equals(old_value)) {
+    prefs_->Set(key, std::move(value));
+    ReportValueChanged(key, flags);
+  }
+}
+
+void JsonPrefStore::SetValueSilently(const std::string& key,
+                                     std::unique_ptr<base::Value> value,
+                                     uint32_t flags) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  DCHECK(value);
+  base::Value* old_value = nullptr;
+  prefs_->Get(key, &old_value);
+  if (!old_value || !value->Equals(old_value)) {
+    prefs_->Set(key, std::move(value));
+    ScheduleWrite(flags);
+  }
+}
+
+void JsonPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (prefs_->RemovePath(key, nullptr))
+    ReportValueChanged(key, flags);
+}
+
+void JsonPrefStore::RemoveValueSilently(const std::string& key,
+                                        uint32_t flags) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  prefs_->RemovePath(key, nullptr);
+  ScheduleWrite(flags);
+}
+
+bool JsonPrefStore::ReadOnly() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return read_only_;
+}
+
+PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return read_error_;
+}
+
+PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  OnFileRead(ReadPrefsFromDisk(path_));
+  return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE
+                                : read_error_;
+}
+
+void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  initialized_ = false;
+  error_delegate_.reset(error_delegate);
+
+  // Weakly binds the read task so that it doesn't kick in during shutdown.
+  base::PostTaskAndReplyWithResult(
+      file_task_runner_.get(), FROM_HERE, base::Bind(&ReadPrefsFromDisk, path_),
+      base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr()));
+}
+
+void JsonPrefStore::CommitPendingWrite(
+    base::OnceClosure reply_callback,
+    base::OnceClosure synchronous_done_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Schedule a write for any lossy writes that are outstanding to ensure that
+  // they get flushed when this function is called.
+  SchedulePendingLossyWrites();
+
+  if (writer_.HasPendingWrite() && !read_only_)
+    writer_.DoScheduledWrite();
+
+  // Since disk operations occur on |file_task_runner_|, the reply of a task
+  // posted to |file_task_runner_| will run after currently pending disk
+  // operations. Also, by definition of PostTaskAndReply(), the reply (in the
+  // |reply_callback| case will run on the current sequence.
+
+  if (synchronous_done_callback) {
+    file_task_runner_->PostTask(FROM_HERE,
+                                std::move(synchronous_done_callback));
+  }
+
+  if (reply_callback) {
+    file_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
+                                        std::move(reply_callback));
+  }
+}
+
+void JsonPrefStore::SchedulePendingLossyWrites() {
+  if (pending_lossy_write_)
+    writer_.ScheduleWrite(this);
+}
+
+void JsonPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (pref_filter_)
+    pref_filter_->FilterUpdate(key);
+
+  for (PrefStore::Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+
+  ScheduleWrite(flags);
+}
+
+void JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback(
+    bool write_success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  has_pending_write_reply_ = false;
+  if (!on_next_successful_write_reply_.is_null()) {
+    base::Closure on_successful_write =
+        std::move(on_next_successful_write_reply_);
+    if (write_success) {
+      on_successful_write.Run();
+    } else {
+      RegisterOnNextSuccessfulWriteReply(on_successful_write);
+    }
+  }
+}
+
+// static
+void JsonPrefStore::PostWriteCallback(
+    const base::Callback<void(bool success)>& on_next_write_callback,
+    const base::Callback<void(bool success)>& on_next_write_reply,
+    scoped_refptr<base::SequencedTaskRunner> reply_task_runner,
+    bool write_success) {
+  if (!on_next_write_callback.is_null())
+    on_next_write_callback.Run(write_success);
+
+  // We can't run |on_next_write_reply| on the current thread. Bounce back to
+  // the |reply_task_runner| which is the correct sequenced thread.
+  reply_task_runner->PostTask(
+      FROM_HERE, base::BindOnce(on_next_write_reply, write_success));
+}
+
+void JsonPrefStore::RegisterOnNextSuccessfulWriteReply(
+    const base::Closure& on_next_successful_write_reply) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(on_next_successful_write_reply_.is_null());
+
+  on_next_successful_write_reply_ = on_next_successful_write_reply;
+
+  // If there are pending callbacks, avoid erasing them; the reply will be used
+  // as we set |on_next_successful_write_reply_|. Otherwise, setup a reply with
+  // an empty callback.
+  if (!has_pending_write_reply_) {
+    has_pending_write_reply_ = true;
+    writer_.RegisterOnNextWriteCallbacks(
+        base::Closure(),
+        base::Bind(
+            &PostWriteCallback, base::Callback<void(bool success)>(),
+            base::Bind(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
+                       AsWeakPtr()),
+            base::SequencedTaskRunnerHandle::Get()));
+  }
+}
+
+void JsonPrefStore::RegisterOnNextWriteSynchronousCallbacks(
+    OnWriteCallbackPair callbacks) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  has_pending_write_reply_ = true;
+
+  writer_.RegisterOnNextWriteCallbacks(
+      callbacks.first,
+      base::Bind(
+          &PostWriteCallback, callbacks.second,
+          base::Bind(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
+                     AsWeakPtr()),
+          base::SequencedTaskRunnerHandle::Get()));
+}
+
+void JsonPrefStore::ClearMutableValues() {
+  NOTIMPLEMENTED();
+}
+
+void JsonPrefStore::OnStoreDeletionFromDisk() {
+  if (pref_filter_)
+    pref_filter_->OnStoreDeletionFromDisk();
+}
+
+void JsonPrefStore::OnFileRead(std::unique_ptr<ReadResult> read_result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  DCHECK(read_result);
+
+  std::unique_ptr<base::DictionaryValue> unfiltered_prefs(
+      new base::DictionaryValue);
+
+  read_error_ = read_result->error;
+
+  bool initialization_successful = !read_result->no_dir;
+
+  if (initialization_successful) {
+    switch (read_error_) {
+      case PREF_READ_ERROR_ACCESS_DENIED:
+      case PREF_READ_ERROR_FILE_OTHER:
+      case PREF_READ_ERROR_FILE_LOCKED:
+      case PREF_READ_ERROR_JSON_TYPE:
+      case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
+        read_only_ = true;
+        break;
+      case PREF_READ_ERROR_NONE:
+        DCHECK(read_result->value);
+        unfiltered_prefs.reset(
+            static_cast<base::DictionaryValue*>(read_result->value.release()));
+        break;
+      case PREF_READ_ERROR_NO_FILE:
+        // If the file just doesn't exist, maybe this is first run.  In any case
+        // there's no harm in writing out default prefs in this case.
+      case PREF_READ_ERROR_JSON_PARSE:
+      case PREF_READ_ERROR_JSON_REPEAT:
+        break;
+      case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
+        // This is a special error code to be returned by ReadPrefs when it
+        // can't complete synchronously, it should never be returned by the read
+        // operation itself.
+      case PREF_READ_ERROR_MAX_ENUM:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  if (pref_filter_) {
+    filtering_in_progress_ = true;
+    const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
+        base::Bind(
+            &JsonPrefStore::FinalizeFileRead, AsWeakPtr(),
+            initialization_successful));
+    pref_filter_->FilterOnLoad(post_filter_on_load_callback,
+                               std::move(unfiltered_prefs));
+  } else {
+    FinalizeFileRead(initialization_successful, std::move(unfiltered_prefs),
+                     false);
+  }
+}
+
+JsonPrefStore::~JsonPrefStore() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CommitPendingWrite();
+}
+
+bool JsonPrefStore::SerializeData(std::string* output) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  pending_lossy_write_ = false;
+
+  if (pref_filter_) {
+    OnWriteCallbackPair callbacks =
+        pref_filter_->FilterSerializeData(prefs_.get());
+    if (!callbacks.first.is_null() || !callbacks.second.is_null())
+      RegisterOnNextWriteSynchronousCallbacks(callbacks);
+  }
+
+  JSONStringValueSerializer serializer(output);
+  // Not pretty-printing prefs shrinks pref file size by ~30%. To obtain
+  // readable prefs for debugging purposes, you can dump your prefs into any
+  // command-line or online JSON pretty printing tool.
+  serializer.set_pretty_print(false);
+  bool success = serializer.Serialize(*prefs_);
+  DCHECK(success);
+  return success;
+}
+
+void JsonPrefStore::FinalizeFileRead(
+    bool initialization_successful,
+    std::unique_ptr<base::DictionaryValue> prefs,
+    bool schedule_write) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  filtering_in_progress_ = false;
+
+  if (!initialization_successful) {
+    for (PrefStore::Observer& observer : observers_)
+      observer.OnInitializationCompleted(false);
+    return;
+  }
+
+  prefs_ = std::move(prefs);
+
+  initialized_ = true;
+
+  if (schedule_write)
+    ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS);
+
+  if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
+    error_delegate_->OnError(read_error_);
+
+  for (PrefStore::Observer& observer : observers_)
+    observer.OnInitializationCompleted(true);
+
+  return;
+}
+
+void JsonPrefStore::ScheduleWrite(uint32_t flags) {
+  if (read_only_)
+    return;
+
+  if (flags & LOSSY_PREF_WRITE_FLAG)
+    pending_lossy_write_ = true;
+  else
+    writer_.ScheduleWrite(this);
+}
diff --git a/src/updater_components/prefs/json_pref_store.h b/src/updater_components/prefs/json_pref_store.h
new file mode 100644
index 0000000..089ce0b
--- /dev/null
+++ b/src/updater_components/prefs/json_pref_store.h
@@ -0,0 +1,203 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_JSON_PREF_STORE_H_
+#define COMPONENTS_PREFS_JSON_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/files/important_file_writer.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "base/task/post_task.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_filter.h"
+#include "components/prefs/prefs_export.h"
+
+class PrefFilter;
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+class JsonPrefStoreCallbackTest;
+class JsonPrefStoreLossyWriteTest;
+class SequencedTaskRunner;
+class WriteCallbacksObserver;
+class Value;
+}
+
+// A writable PrefStore implementation that is used for user preferences.
+class COMPONENTS_PREFS_EXPORT JsonPrefStore
+    : public PersistentPrefStore,
+      public base::ImportantFileWriter::DataSerializer,
+      public base::SupportsWeakPtr<JsonPrefStore> {
+ public:
+  struct ReadResult;
+
+  // A pair of callbacks to call before and after the preference file is written
+  // to disk.
+  using OnWriteCallbackPair =
+      std::pair<base::Closure, base::Callback<void(bool success)>>;
+
+  // |pref_filename| is the path to the file to read prefs from. It is incorrect
+  // to create multiple JsonPrefStore with the same |pref_filename|.
+  // |file_task_runner| is used for asynchronous reads and writes. It must
+  // have the base::TaskShutdownBehavior::BLOCK_SHUTDOWN and base::MayBlock()
+  // traits. Unless external tasks need to run on the same sequence as
+  // JsonPrefStore tasks, keep the default value.
+  // The initial read is done synchronously, the TaskPriority is thus only used
+  // for flushes to disks and BEST_EFFORT is therefore appropriate. Priority of
+  // remaining BEST_EFFORT+BLOCK_SHUTDOWN tasks is bumped by the ThreadPool on
+  // shutdown. However, some shutdown use cases happen without
+  // ThreadPoolInstance::Shutdown() (e.g. ChromeRestartRequest::Start() and
+  // BrowserProcessImpl::EndSession()) and we must thus unfortunately make this
+  // USER_VISIBLE until we solve https://crbug.com/747495 to allow bumping
+  // priority of a sequence on demand.
+  JsonPrefStore(const base::FilePath& pref_filename,
+                std::unique_ptr<PrefFilter> pref_filter = nullptr,
+                scoped_refptr<base::SequencedTaskRunner> file_task_runner =
+                    base::CreateSequencedTaskRunner(
+                        {base::ThreadPool(), base::MayBlock(),
+                         base::TaskPriority::USER_VISIBLE,
+                         base::TaskShutdownBehavior::BLOCK_SHUTDOWN}));
+
+  // PrefStore overrides:
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+  bool IsInitializationComplete() const override;
+
+  // PersistentPrefStore overrides:
+  bool GetMutableValue(const std::string& key, base::Value** result) override;
+  void SetValue(const std::string& key,
+                std::unique_ptr<base::Value> value,
+                uint32_t flags) override;
+  void SetValueSilently(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) override;
+  void RemoveValue(const std::string& key, uint32_t flags) override;
+  bool ReadOnly() const override;
+  PrefReadError GetReadError() const override;
+  // Note this method may be asynchronous if this instance has a |pref_filter_|
+  // in which case it will return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE.
+  // See details in pref_filter.h.
+  PrefReadError ReadPrefs() override;
+  void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
+  void CommitPendingWrite(
+      base::OnceClosure reply_callback = base::OnceClosure(),
+      base::OnceClosure synchronous_done_callback =
+          base::OnceClosure()) override;
+  void SchedulePendingLossyWrites() override;
+  void ReportValueChanged(const std::string& key, uint32_t flags) override;
+
+  // Just like RemoveValue(), but doesn't notify observers. Used when doing some
+  // cleanup that shouldn't otherwise alert observers.
+  void RemoveValueSilently(const std::string& key, uint32_t flags);
+
+  // Registers |on_next_successful_write_reply| to be called once, on the next
+  // successful write event of |writer_|.
+  // |on_next_successful_write_reply| will be called on the thread from which
+  // this method is called and does not need to be thread safe.
+  void RegisterOnNextSuccessfulWriteReply(
+      const base::Closure& on_next_successful_write_reply);
+
+  void ClearMutableValues() override;
+
+  void OnStoreDeletionFromDisk() override;
+
+ private:
+  friend class base::JsonPrefStoreCallbackTest;
+  friend class base::JsonPrefStoreLossyWriteTest;
+  friend class base::WriteCallbacksObserver;
+
+  ~JsonPrefStore() override;
+
+  // If |write_success| is true, runs |on_next_successful_write_|.
+  // Otherwise, re-registers |on_next_successful_write_|.
+  void RunOrScheduleNextSuccessfulWriteCallback(bool write_success);
+
+  // Handles the result of a write with result |write_success|. Runs
+  // |on_next_write_callback| on the current thread and posts
+  // |on_next_write_reply| on |reply_task_runner|.
+  static void PostWriteCallback(
+      const base::Callback<void(bool success)>& on_next_write_callback,
+      const base::Callback<void(bool success)>& on_next_write_reply,
+      scoped_refptr<base::SequencedTaskRunner> reply_task_runner,
+      bool write_success);
+
+  // Registers the |callbacks| pair to be called once synchronously before and
+  // after, respectively, the next write event of |writer_|.
+  // Both callbacks must be thread-safe.
+  void RegisterOnNextWriteSynchronousCallbacks(OnWriteCallbackPair callbacks);
+
+  // This method is called after the JSON file has been read.  It then hands
+  // |value| (or an empty dictionary in some read error cases) to the
+  // |pref_filter| if one is set. It also gives a callback pointing at
+  // FinalizeFileRead() to that |pref_filter_| which is then responsible for
+  // invoking it when done. If there is no |pref_filter_|, FinalizeFileRead()
+  // is invoked directly.
+  void OnFileRead(std::unique_ptr<ReadResult> read_result);
+
+  // ImportantFileWriter::DataSerializer overrides:
+  bool SerializeData(std::string* output) override;
+
+  // This method is called after the JSON file has been read and the result has
+  // potentially been intercepted and modified by |pref_filter_|.
+  // |initialization_successful| is pre-determined by OnFileRead() and should
+  // be used when reporting OnInitializationCompleted().
+  // |schedule_write| indicates whether a write should be immediately scheduled
+  // (typically because the |pref_filter_| has already altered the |prefs|) --
+  // this will be ignored if this store is read-only.
+  void FinalizeFileRead(bool initialization_successful,
+                        std::unique_ptr<base::DictionaryValue> prefs,
+                        bool schedule_write);
+
+  // Schedule a write with the file writer as long as |flags| doesn't contain
+  // WriteablePrefStore::LOSSY_PREF_WRITE_FLAG.
+  void ScheduleWrite(uint32_t flags);
+
+  const base::FilePath path_;
+  const scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
+
+  std::unique_ptr<base::DictionaryValue> prefs_;
+
+  bool read_only_;
+
+  // Helper for safely writing pref data.
+  base::ImportantFileWriter writer_;
+
+  std::unique_ptr<PrefFilter> pref_filter_;
+  base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
+
+  std::unique_ptr<ReadErrorDelegate> error_delegate_;
+
+  bool initialized_;
+  bool filtering_in_progress_;
+  bool pending_lossy_write_;
+  PrefReadError read_error_;
+
+  std::set<std::string> keys_need_empty_value_;
+
+  bool has_pending_write_reply_ = true;
+  base::Closure on_next_successful_write_reply_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(JsonPrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_JSON_PREF_STORE_H_
diff --git a/src/updater_components/prefs/json_pref_store_unittest.cc b/src/updater_components/prefs/json_pref_store_unittest.cc
new file mode 100644
index 0000000..c747c36
--- /dev/null
+++ b/src/updater_components/prefs/json_pref_store_unittest.cc
@@ -0,0 +1,985 @@
+// 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 "components/prefs/json_pref_store.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "components/prefs/persistent_pref_store_unittest.h"
+#include "components/prefs/pref_filter.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+const char kHomePage[] = "homepage";
+
+const char kReadJson[] =
+    "{\n"
+    "  \"homepage\": \"http://www.cnn.com\",\n"
+    "  \"some_directory\": \"/usr/local/\",\n"
+    "  \"tabs\": {\n"
+    "    \"new_windows_in_tabs\": true,\n"
+    "    \"max_tabs\": 20\n"
+    "  }\n"
+    "}";
+
+const char kInvalidJson[] = "!@#$%^&";
+
+// Expected output for tests using RunBasicJsonPrefStoreTest().
+const char kWriteGolden[] =
+    "{\"homepage\":\"http://www.cnn.com\","
+     "\"long_int\":{\"pref\":\"214748364842\"},"
+     "\"some_directory\":\"/usr/sbin/\","
+     "\"tabs\":{\"max_tabs\":10,\"new_windows_in_tabs\":false}}";
+
+// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
+// to the |prefs| until explicitly asked to release them.
+class InterceptingPrefFilter : public PrefFilter {
+ public:
+  InterceptingPrefFilter();
+  InterceptingPrefFilter(OnWriteCallbackPair callback_pair);
+  ~InterceptingPrefFilter() override;
+
+  // PrefFilter implementation:
+  void FilterOnLoad(
+      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      std::unique_ptr<base::DictionaryValue> pref_store_contents) override;
+  void FilterUpdate(const std::string& path) override {}
+  OnWriteCallbackPair FilterSerializeData(
+      base::DictionaryValue* pref_store_contents) override {
+    return on_write_callback_pair_;
+  }
+  void OnStoreDeletionFromDisk() override {}
+
+  bool has_intercepted_prefs() const { return intercepted_prefs_ != nullptr; }
+
+  // Finalize an intercepted read, handing |intercepted_prefs_| back to its
+  // JsonPrefStore.
+  void ReleasePrefs();
+
+ private:
+  PostFilterOnLoadCallback post_filter_on_load_callback_;
+  std::unique_ptr<base::DictionaryValue> intercepted_prefs_;
+  OnWriteCallbackPair on_write_callback_pair_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter);
+};
+
+InterceptingPrefFilter::InterceptingPrefFilter() {}
+
+InterceptingPrefFilter::InterceptingPrefFilter(
+    OnWriteCallbackPair callback_pair) {
+  on_write_callback_pair_ = callback_pair;
+}
+
+InterceptingPrefFilter::~InterceptingPrefFilter() {}
+
+void InterceptingPrefFilter::FilterOnLoad(
+    const PostFilterOnLoadCallback& post_filter_on_load_callback,
+    std::unique_ptr<base::DictionaryValue> pref_store_contents) {
+  post_filter_on_load_callback_ = post_filter_on_load_callback;
+  intercepted_prefs_ = std::move(pref_store_contents);
+}
+
+void InterceptingPrefFilter::ReleasePrefs() {
+  EXPECT_FALSE(post_filter_on_load_callback_.is_null());
+  post_filter_on_load_callback_.Run(std::move(intercepted_prefs_), false);
+  post_filter_on_load_callback_.Reset();
+}
+
+class MockPrefStoreObserver : public PrefStore::Observer {
+ public:
+  MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
+  MOCK_METHOD1(OnInitializationCompleted, void (bool));
+};
+
+class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
+ public:
+  MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
+};
+
+enum class CommitPendingWriteMode {
+  // Basic mode.
+  WITHOUT_CALLBACK,
+  // With reply callback.
+  WITH_CALLBACK,
+  // With synchronous notify callback (synchronous after the write -- shouldn't
+  // require pumping messages to observe).
+  WITH_SYNCHRONOUS_CALLBACK,
+};
+
+base::test::TaskEnvironment::ThreadPoolExecutionMode GetExecutionMode(
+    CommitPendingWriteMode commit_mode) {
+  switch (commit_mode) {
+    case CommitPendingWriteMode::WITHOUT_CALLBACK:
+      FALLTHROUGH;
+    case CommitPendingWriteMode::WITH_CALLBACK:
+      return base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED;
+    case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK:
+      // Synchronous callbacks require async tasks to run on their own.
+      return base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC;
+  }
+}
+
+void CommitPendingWrite(JsonPrefStore* pref_store,
+                        CommitPendingWriteMode commit_pending_write_mode,
+                        base::test::TaskEnvironment* task_environment) {
+  switch (commit_pending_write_mode) {
+    case CommitPendingWriteMode::WITHOUT_CALLBACK: {
+      pref_store->CommitPendingWrite();
+      task_environment->RunUntilIdle();
+      break;
+    }
+    case CommitPendingWriteMode::WITH_CALLBACK: {
+      TestCommitPendingWriteWithCallback(pref_store, task_environment);
+      break;
+    }
+    case CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK: {
+      base::WaitableEvent written;
+      pref_store->CommitPendingWrite(
+          base::OnceClosure(),
+          base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written)));
+      written.Wait();
+      break;
+    }
+  }
+}
+
+class JsonPrefStoreTest
+    : public testing::TestWithParam<CommitPendingWriteMode> {
+ public:
+  JsonPrefStoreTest()
+      : task_environment_(base::test::TaskEnvironment::MainThreadType::DEFAULT,
+                          GetExecutionMode(GetParam())) {}
+
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+  }
+
+  base::test::TaskEnvironment task_environment_;
+
+  // The path to temporary directory used to contain the test operations.
+  base::ScopedTempDir temp_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreTest);
+};
+
+}  // namespace
+
+// Test fallback behavior for a nonexistent file.
+TEST_P(JsonPrefStoreTest, NonExistentFile) {
+  base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
+  ASSERT_FALSE(PathExists(bogus_input_file));
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
+  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
+            pref_store->ReadPrefs());
+  EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+// Test fallback behavior for an invalid file.
+TEST_P(JsonPrefStoreTest, InvalidFile) {
+  base::FilePath invalid_file = temp_dir_.GetPath().AppendASCII("invalid.json");
+  ASSERT_LT(0, base::WriteFile(invalid_file, kInvalidJson,
+                               base::size(kInvalidJson) - 1));
+
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(invalid_file);
+  EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
+            pref_store->ReadPrefs());
+  EXPECT_FALSE(pref_store->ReadOnly());
+
+  // The file should have been moved aside.
+  EXPECT_FALSE(PathExists(invalid_file));
+  base::FilePath moved_aside = temp_dir_.GetPath().AppendASCII("invalid.bad");
+  EXPECT_TRUE(PathExists(moved_aside));
+
+  std::string moved_aside_contents;
+  ASSERT_TRUE(base::ReadFileToString(moved_aside, &moved_aside_contents));
+  EXPECT_EQ(kInvalidJson, moved_aside_contents);
+}
+
+// This function is used to avoid code duplication while testing synchronous
+// and asynchronous version of the JsonPrefStore loading. It validates that the
+// given output file's contents matches kWriteGolden.
+void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
+                               const base::FilePath& output_file,
+                               CommitPendingWriteMode commit_pending_write_mode,
+                               base::test::TaskEnvironment* task_environment) {
+  const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
+  const char kMaxTabs[] = "tabs.max_tabs";
+  const char kLongIntPref[] = "long_int.pref";
+
+  std::string cnn("http://www.cnn.com");
+
+  const Value* actual;
+  EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
+  std::string string_value;
+  EXPECT_TRUE(actual->GetAsString(&string_value));
+  EXPECT_EQ(cnn, string_value);
+
+  const char kSomeDirectory[] = "some_directory";
+
+  EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
+  base::FilePath::StringType path;
+  EXPECT_TRUE(actual->GetAsString(&path));
+  EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
+  base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
+
+  pref_store->SetValue(kSomeDirectory,
+                       std::make_unique<Value>(some_path.value()),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
+  EXPECT_TRUE(actual->GetAsString(&path));
+  EXPECT_EQ(some_path.value(), path);
+
+  // Test reading some other data types from sub-dictionaries.
+  EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
+  bool boolean = false;
+  EXPECT_TRUE(actual->GetAsBoolean(&boolean));
+  EXPECT_TRUE(boolean);
+
+  pref_store->SetValue(kNewWindowsInTabs, std::make_unique<Value>(false),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
+  EXPECT_TRUE(actual->GetAsBoolean(&boolean));
+  EXPECT_FALSE(boolean);
+
+  EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
+  int integer = 0;
+  EXPECT_TRUE(actual->GetAsInteger(&integer));
+  EXPECT_EQ(20, integer);
+  pref_store->SetValue(kMaxTabs, std::make_unique<Value>(10),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
+  EXPECT_TRUE(actual->GetAsInteger(&integer));
+  EXPECT_EQ(10, integer);
+
+  pref_store->SetValue(
+      kLongIntPref,
+      std::make_unique<Value>(base::NumberToString(214748364842LL)),
+      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
+  EXPECT_TRUE(actual->GetAsString(&string_value));
+  int64_t value;
+  base::StringToInt64(string_value, &value);
+  EXPECT_EQ(214748364842LL, value);
+
+  // Serialize and compare to expected output.
+  CommitPendingWrite(pref_store, commit_pending_write_mode, task_environment);
+
+  std::string output_contents;
+  ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents));
+  EXPECT_EQ(kWriteGolden, output_contents);
+  ASSERT_TRUE(base::DeleteFile(output_file, false));
+}
+
+TEST_P(JsonPrefStoreTest, Basic) {
+  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
+  ASSERT_LT(0,
+            base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1));
+
+  // Test that the persistent value can be loaded.
+  ASSERT_TRUE(PathExists(input_file));
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
+  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+  EXPECT_FALSE(pref_store->ReadOnly());
+  EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+  // The JSON file looks like this:
+  // {
+  //   "homepage": "http://www.cnn.com",
+  //   "some_directory": "/usr/local/",
+  //   "tabs": {
+  //     "new_windows_in_tabs": true,
+  //     "max_tabs": 20
+  //   }
+  // }
+
+  RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+                            &task_environment_);
+}
+
+TEST_P(JsonPrefStoreTest, BasicAsync) {
+  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
+  ASSERT_LT(0,
+            base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1));
+
+  // Test that the persistent value can be loaded.
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(input_file);
+
+  {
+    MockPrefStoreObserver mock_observer;
+    pref_store->AddObserver(&mock_observer);
+
+    MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+    pref_store->ReadPrefsAsync(mock_error_delegate);
+
+    EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+    EXPECT_CALL(*mock_error_delegate,
+                OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+    task_environment_.RunUntilIdle();
+    pref_store->RemoveObserver(&mock_observer);
+
+    EXPECT_FALSE(pref_store->ReadOnly());
+    EXPECT_TRUE(pref_store->IsInitializationComplete());
+  }
+
+  // The JSON file looks like this:
+  // {
+  //   "homepage": "http://www.cnn.com",
+  //   "some_directory": "/usr/local/",
+  //   "tabs": {
+  //     "new_windows_in_tabs": true,
+  //     "max_tabs": 20
+  //   }
+  // }
+
+  RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+                            &task_environment_);
+}
+
+TEST_P(JsonPrefStoreTest, PreserveEmptyValues) {
+  FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
+
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
+
+  // Set some keys with empty values.
+  pref_store->SetValue("list", std::make_unique<base::ListValue>(),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  pref_store->SetValue("dict", std::make_unique<base::DictionaryValue>(),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  // Write to file.
+  CommitPendingWrite(pref_store.get(), GetParam(), &task_environment_);
+
+  // Reload.
+  pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
+  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+  ASSERT_FALSE(pref_store->ReadOnly());
+
+  // Check values.
+  const Value* result = nullptr;
+  EXPECT_TRUE(pref_store->GetValue("list", &result));
+  EXPECT_TRUE(ListValue().Equals(result));
+  EXPECT_TRUE(pref_store->GetValue("dict", &result));
+  EXPECT_TRUE(DictionaryValue().Equals(result));
+}
+
+// This test is just documenting some potentially non-obvious behavior. It
+// shouldn't be taken as normative.
+TEST_P(JsonPrefStoreTest, RemoveClearsEmptyParent) {
+  FilePath pref_file = temp_dir_.GetPath().AppendASCII("empty_values.json");
+
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(pref_file);
+
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+  dict->SetString("key", "value");
+  pref_store->SetValue("dict", std::move(dict),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  pref_store->RemoveValue("dict.key",
+                          WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  const base::Value* retrieved_dict = nullptr;
+  bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
+  EXPECT_FALSE(has_dict);
+}
+
+// Tests asynchronous reading of the file when there is no file.
+TEST_P(JsonPrefStoreTest, AsyncNonExistingFile) {
+  base::FilePath bogus_input_file = temp_dir_.GetPath().AppendASCII("read.txt");
+  ASSERT_FALSE(PathExists(bogus_input_file));
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(bogus_input_file);
+  MockPrefStoreObserver mock_observer;
+  pref_store->AddObserver(&mock_observer);
+
+  MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
+  pref_store->ReadPrefsAsync(mock_error_delegate);
+
+  EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+  EXPECT_CALL(*mock_error_delegate,
+              OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
+  task_environment_.RunUntilIdle();
+  pref_store->RemoveObserver(&mock_observer);
+
+  EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+TEST_P(JsonPrefStoreTest, ReadWithInterceptor) {
+  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
+  ASSERT_LT(0,
+            base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1));
+
+  std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
+      new InterceptingPrefFilter());
+  InterceptingPrefFilter* raw_intercepting_pref_filter_ =
+      intercepting_pref_filter.get();
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(
+      input_file, std::move(intercepting_pref_filter));
+
+  ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
+            pref_store->ReadPrefs());
+  EXPECT_FALSE(pref_store->ReadOnly());
+
+  // The store shouldn't be considered initialized until the interceptor
+  // returns.
+  EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+  EXPECT_FALSE(pref_store->IsInitializationComplete());
+  EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
+
+  raw_intercepting_pref_filter_->ReleasePrefs();
+
+  EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+  EXPECT_TRUE(pref_store->IsInitializationComplete());
+  EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
+
+  // The JSON file looks like this:
+  // {
+  //   "homepage": "http://www.cnn.com",
+  //   "some_directory": "/usr/local/",
+  //   "tabs": {
+  //     "new_windows_in_tabs": true,
+  //     "max_tabs": 20
+  //   }
+  // }
+
+  RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+                            &task_environment_);
+}
+
+TEST_P(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
+  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
+  ASSERT_LT(0,
+            base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1));
+
+  std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
+      new InterceptingPrefFilter());
+  InterceptingPrefFilter* raw_intercepting_pref_filter_ =
+      intercepting_pref_filter.get();
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(
+      input_file, std::move(intercepting_pref_filter));
+
+  MockPrefStoreObserver mock_observer;
+  pref_store->AddObserver(&mock_observer);
+
+  // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
+  MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+
+  {
+    pref_store->ReadPrefsAsync(mock_error_delegate);
+
+    EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
+    // EXPECT_CALL(*mock_error_delegate,
+    //             OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+    task_environment_.RunUntilIdle();
+
+    EXPECT_FALSE(pref_store->ReadOnly());
+    EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+    EXPECT_FALSE(pref_store->IsInitializationComplete());
+    EXPECT_FALSE(pref_store->GetValue(kHomePage, nullptr));
+  }
+
+  {
+    EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+    // EXPECT_CALL(*mock_error_delegate,
+    //             OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+
+    raw_intercepting_pref_filter_->ReleasePrefs();
+
+    EXPECT_FALSE(pref_store->ReadOnly());
+    EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+    EXPECT_TRUE(pref_store->IsInitializationComplete());
+    EXPECT_TRUE(pref_store->GetValue(kHomePage, nullptr));
+  }
+
+  pref_store->RemoveObserver(&mock_observer);
+
+  // The JSON file looks like this:
+  // {
+  //   "homepage": "http://www.cnn.com",
+  //   "some_directory": "/usr/local/",
+  //   "tabs": {
+  //     "new_windows_in_tabs": true,
+  //     "max_tabs": 20
+  //   }
+  // }
+
+  RunBasicJsonPrefStoreTest(pref_store.get(), input_file, GetParam(),
+                            &task_environment_);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    WithoutCallback,
+    JsonPrefStoreTest,
+    ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK));
+INSTANTIATE_TEST_SUITE_P(
+    WithCallback,
+    JsonPrefStoreTest,
+    ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK));
+INSTANTIATE_TEST_SUITE_P(
+    WithSynchronousCallback,
+    JsonPrefStoreTest,
+    ::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
+
+class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
+ public:
+  JsonPrefStoreLossyWriteTest() = default;
+
+ protected:
+  void SetUp() override {
+    JsonPrefStoreTest::SetUp();
+    test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
+  }
+
+  scoped_refptr<JsonPrefStore> CreatePrefStore() {
+    return base::MakeRefCounted<JsonPrefStore>(test_file_);
+  }
+
+  // Return the ImportantFileWriter for a given JsonPrefStore.
+  ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
+    return &(pref_store->writer_);
+  }
+
+  // Get the contents of kTestFile. Pumps the message loop before returning the
+  // result.
+  std::string GetTestFileContents() {
+    task_environment_.RunUntilIdle();
+    std::string file_contents;
+    ReadFileToString(test_file_, &file_contents);
+    return file_contents;
+  }
+
+ private:
+  base::FilePath test_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreLossyWriteTest);
+};
+
+TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
+  scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+  ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
+
+  // Set a normal pref and check that it gets scheduled to be written.
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  pref_store->SetValue("normal", std::make_unique<base::Value>("normal"),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  ASSERT_TRUE(file_writer->HasPendingWrite());
+  file_writer->DoScheduledWrite();
+  ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents());
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+
+  // Set a lossy pref and check that it is not scheduled to be written.
+  // SetValue/RemoveValue.
+  pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"),
+                       WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+
+  // SetValueSilently/RemoveValueSilently.
+  pref_store->SetValueSilently("lossy", std::make_unique<base::Value>("lossy"),
+                               WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  pref_store->RemoveValueSilently("lossy",
+                                  WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+
+  // ReportValueChanged.
+  pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"),
+                       WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  pref_store->ReportValueChanged("lossy",
+                                 WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+
+  // Call CommitPendingWrite and check that the lossy pref and the normal pref
+  // are there with the last values set above.
+  pref_store->CommitPendingWrite(base::OnceClosure());
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
+            GetTestFileContents());
+}
+
+TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
+  scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+  ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
+
+  // Set a lossy pref and check that it is not scheduled to be written.
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"),
+                       WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+
+  // Set a normal pref and check that it is scheduled to be written.
+  pref_store->SetValue("normal", std::make_unique<base::Value>("normal"),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  ASSERT_TRUE(file_writer->HasPendingWrite());
+
+  // Call DoScheduledWrite and check both prefs get written.
+  file_writer->DoScheduledWrite();
+  ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
+            GetTestFileContents());
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+}
+
+TEST_P(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
+  scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+  ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
+
+  // Set a normal pref and check that it is scheduled to be written.
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  pref_store->SetValue("normal", std::make_unique<base::Value>("normal"),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  ASSERT_TRUE(file_writer->HasPendingWrite());
+
+  // Set a lossy pref and check that the write is still scheduled.
+  pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"),
+                       WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_TRUE(file_writer->HasPendingWrite());
+
+  // Call DoScheduledWrite and check both prefs get written.
+  file_writer->DoScheduledWrite();
+  ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
+            GetTestFileContents());
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+}
+
+TEST_P(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
+  scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+  ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
+
+  // Set a lossy pref and check that it is not scheduled to be written.
+  pref_store->SetValue("lossy", std::make_unique<base::Value>("lossy"),
+                       WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+
+  // Schedule pending lossy writes and check that it is scheduled.
+  pref_store->SchedulePendingLossyWrites();
+  ASSERT_TRUE(file_writer->HasPendingWrite());
+
+  // Call CommitPendingWrite and check that the lossy pref is there with the
+  // last value set above.
+  pref_store->CommitPendingWrite(base::OnceClosure());
+  ASSERT_FALSE(file_writer->HasPendingWrite());
+  ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    WithoutCallback,
+    JsonPrefStoreLossyWriteTest,
+    ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK));
+INSTANTIATE_TEST_SUITE_P(
+    WithReply,
+    JsonPrefStoreLossyWriteTest,
+    ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK));
+INSTANTIATE_TEST_SUITE_P(
+    WithNotify,
+    JsonPrefStoreLossyWriteTest,
+    ::testing::Values(CommitPendingWriteMode::WITH_SYNCHRONOUS_CALLBACK));
+
+class SuccessfulWriteReplyObserver {
+ public:
+  SuccessfulWriteReplyObserver() = default;
+
+  // Returns true if a successful write was observed via on_successful_write()
+  // and resets the observation state to false regardless.
+  bool GetAndResetObservationState() {
+    bool was_successful_write_observed = successful_write_reply_observed_;
+    successful_write_reply_observed_ = false;
+    return was_successful_write_observed;
+  }
+
+  // Register OnWrite() to be called on the next write of |json_pref_store|.
+  void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
+
+  void OnSuccessfulWrite() {
+    EXPECT_FALSE(successful_write_reply_observed_);
+    successful_write_reply_observed_ = true;
+  }
+
+ private:
+  bool successful_write_reply_observed_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(SuccessfulWriteReplyObserver);
+};
+
+void SuccessfulWriteReplyObserver::ObserveNextWriteCallback(
+    JsonPrefStore* json_pref_store) {
+  json_pref_store->RegisterOnNextSuccessfulWriteReply(
+      base::Bind(&SuccessfulWriteReplyObserver::OnSuccessfulWrite,
+                 base::Unretained(this)));
+}
+
+enum WriteCallbackObservationState {
+  NOT_CALLED,
+  CALLED_WITH_ERROR,
+  CALLED_WITH_SUCCESS,
+};
+
+class WriteCallbacksObserver {
+ public:
+  WriteCallbacksObserver() = default;
+
+  // Register OnWrite() to be called on the next write of |json_pref_store|.
+  void ObserveNextWriteCallback(JsonPrefStore* json_pref_store);
+
+  // Returns whether OnPreWrite() was called, and resets the observation state
+  // to false.
+  bool GetAndResetPreWriteObservationState();
+
+  // Returns the |WriteCallbackObservationState| which was observed, then resets
+  // it to |NOT_CALLED|.
+  WriteCallbackObservationState GetAndResetPostWriteObservationState();
+
+  JsonPrefStore::OnWriteCallbackPair GetCallbackPair() {
+    return std::make_pair(
+        base::Bind(&WriteCallbacksObserver::OnPreWrite, base::Unretained(this)),
+        base::Bind(&WriteCallbacksObserver::OnPostWrite,
+                   base::Unretained(this)));
+  }
+
+  void OnPreWrite() {
+    EXPECT_FALSE(pre_write_called_);
+    pre_write_called_ = true;
+  }
+
+  void OnPostWrite(bool success) {
+    EXPECT_EQ(NOT_CALLED, post_write_observation_state_);
+    post_write_observation_state_ =
+        success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR;
+  }
+
+ private:
+  bool pre_write_called_ = false;
+  WriteCallbackObservationState post_write_observation_state_ = NOT_CALLED;
+
+  DISALLOW_COPY_AND_ASSIGN(WriteCallbacksObserver);
+};
+
+void WriteCallbacksObserver::ObserveNextWriteCallback(JsonPrefStore* writer) {
+  writer->RegisterOnNextWriteSynchronousCallbacks(GetCallbackPair());
+}
+
+bool WriteCallbacksObserver::GetAndResetPreWriteObservationState() {
+  bool observation_state = pre_write_called_;
+  pre_write_called_ = false;
+  return observation_state;
+}
+
+WriteCallbackObservationState
+WriteCallbacksObserver::GetAndResetPostWriteObservationState() {
+  WriteCallbackObservationState state = post_write_observation_state_;
+  pre_write_called_ = false;
+  post_write_observation_state_ = NOT_CALLED;
+  return state;
+}
+
+class JsonPrefStoreCallbackTest : public testing::Test {
+ public:
+  JsonPrefStoreCallbackTest() = default;
+
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    test_file_ = temp_dir_.GetPath().AppendASCII("test.json");
+  }
+
+  scoped_refptr<JsonPrefStore> CreatePrefStore() {
+    return base::MakeRefCounted<JsonPrefStore>(test_file_);
+  }
+
+  // Return the ImportantFileWriter for a given JsonPrefStore.
+  ImportantFileWriter* GetImportantFileWriter(JsonPrefStore* pref_store) {
+    return &(pref_store->writer_);
+  }
+
+  void TriggerFakeWriteForCallback(JsonPrefStore* pref_store, bool success) {
+    JsonPrefStore::PostWriteCallback(
+        base::Bind(&JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
+                   pref_store->AsWeakPtr()),
+        base::Bind(&WriteCallbacksObserver::OnPostWrite,
+                   base::Unretained(&write_callback_observer_)),
+        base::SequencedTaskRunnerHandle::Get(), success);
+  }
+
+  SuccessfulWriteReplyObserver successful_write_reply_observer_;
+  WriteCallbacksObserver write_callback_observer_;
+
+ protected:
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::DEFAULT,
+      base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
+
+  base::ScopedTempDir temp_dir_;
+
+ private:
+  base::FilePath test_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(JsonPrefStoreCallbackTest);
+};
+
+TEST_F(JsonPrefStoreCallbackTest, TestSerializeDataCallbacks) {
+  base::FilePath input_file = temp_dir_.GetPath().AppendASCII("write.json");
+  ASSERT_LT(0,
+            base::WriteFile(input_file, kReadJson, base::size(kReadJson) - 1));
+
+  std::unique_ptr<InterceptingPrefFilter> intercepting_pref_filter(
+      new InterceptingPrefFilter(write_callback_observer_.GetCallbackPair()));
+  auto pref_store = base::MakeRefCounted<JsonPrefStore>(
+      input_file, std::move(intercepting_pref_filter));
+  ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
+
+  EXPECT_EQ(NOT_CALLED,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+  pref_store->SetValue("normal", std::make_unique<base::Value>("normal"),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  file_writer->DoScheduledWrite();
+
+  // The observer should not be invoked right away.
+  EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
+  EXPECT_EQ(NOT_CALLED,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
+  EXPECT_EQ(CALLED_WITH_SUCCESS,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+}
+
+TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacks) {
+  scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+  ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
+
+  // Test RegisterOnNextWriteSynchronousCallbacks after
+  // RegisterOnNextSuccessfulWriteReply.
+  successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
+  write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
+  file_writer->WriteNow(std::make_unique<std::string>("foo"));
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
+  EXPECT_EQ(CALLED_WITH_SUCCESS,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  // Test RegisterOnNextSuccessfulWriteReply after
+  // RegisterOnNextWriteSynchronousCallbacks.
+  successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
+  write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
+  file_writer->WriteNow(std::make_unique<std::string>("foo"));
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
+  EXPECT_EQ(CALLED_WITH_SUCCESS,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  // Test RegisterOnNextSuccessfulWriteReply only.
+  successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
+  file_writer->WriteNow(std::make_unique<std::string>("foo"));
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_FALSE(write_callback_observer_.GetAndResetPreWriteObservationState());
+  EXPECT_EQ(NOT_CALLED,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  // Test RegisterOnNextWriteSynchronousCallbacks only.
+  write_callback_observer_.ObserveNextWriteCallback(pref_store.get());
+  file_writer->WriteNow(std::make_unique<std::string>("foo"));
+  task_environment_.RunUntilIdle();
+  EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
+  EXPECT_EQ(CALLED_WITH_SUCCESS,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+}
+
+TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksWithFakeFailure) {
+  scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+
+  // Confirm that the observers are invoked.
+  successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
+  TriggerFakeWriteForCallback(pref_store.get(), true);
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_EQ(CALLED_WITH_SUCCESS,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  // Confirm that the observation states were reset.
+  EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_EQ(NOT_CALLED,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  // Confirm that re-installing the observers works for another write.
+  successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
+  TriggerFakeWriteForCallback(pref_store.get(), true);
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_EQ(CALLED_WITH_SUCCESS,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  // Confirm that the successful observer is not invoked by an unsuccessful
+  // write, and that the synchronous observer is invoked.
+  successful_write_reply_observer_.ObserveNextWriteCallback(pref_store.get());
+  TriggerFakeWriteForCallback(pref_store.get(), false);
+  task_environment_.RunUntilIdle();
+  EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_EQ(CALLED_WITH_ERROR,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+
+  // Do a real write, and confirm that the successful observer was invoked after
+  // being set by |PostWriteCallback| by the last TriggerFakeWriteCallback.
+  ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store.get());
+  file_writer->WriteNow(std::make_unique<std::string>("foo"));
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_EQ(NOT_CALLED,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+}
+
+TEST_F(JsonPrefStoreCallbackTest, TestPostWriteCallbacksDuringProfileDeath) {
+  // Create a JsonPrefStore and attach observers to it, then delete it by making
+  // it go out of scope to simulate profile switch or Chrome shutdown.
+  {
+    scoped_refptr<JsonPrefStore> soon_out_of_scope_pref_store =
+        CreatePrefStore();
+    ImportantFileWriter* file_writer =
+        GetImportantFileWriter(soon_out_of_scope_pref_store.get());
+    successful_write_reply_observer_.ObserveNextWriteCallback(
+        soon_out_of_scope_pref_store.get());
+    write_callback_observer_.ObserveNextWriteCallback(
+        soon_out_of_scope_pref_store.get());
+    file_writer->WriteNow(std::make_unique<std::string>("foo"));
+  }
+  task_environment_.RunUntilIdle();
+  EXPECT_FALSE(successful_write_reply_observer_.GetAndResetObservationState());
+  EXPECT_TRUE(write_callback_observer_.GetAndResetPreWriteObservationState());
+  EXPECT_EQ(CALLED_WITH_SUCCESS,
+            write_callback_observer_.GetAndResetPostWriteObservationState());
+}
+
+}  // namespace base
diff --git a/src/updater_components/prefs/mock_pref_change_callback.cc b/src/updater_components/prefs/mock_pref_change_callback.cc
new file mode 100644
index 0000000..7ac2cd7
--- /dev/null
+++ b/src/updater_components/prefs/mock_pref_change_callback.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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 "components/prefs/mock_pref_change_callback.h"
+
+#include "base/bind.h"
+
+MockPrefChangeCallback::MockPrefChangeCallback(PrefService* prefs)
+    : prefs_(prefs) {
+}
+
+MockPrefChangeCallback::~MockPrefChangeCallback() {}
+
+PrefChangeRegistrar::NamedChangeCallback MockPrefChangeCallback::GetCallback() {
+  return base::Bind(&MockPrefChangeCallback::OnPreferenceChanged,
+                    base::Unretained(this));
+}
+
+void MockPrefChangeCallback::Expect(const std::string& pref_name,
+                                    const base::Value* value) {
+  EXPECT_CALL(*this, OnPreferenceChanged(pref_name))
+      .With(PrefValueMatches(prefs_, pref_name, value));
+}
diff --git a/src/updater_components/prefs/mock_pref_change_callback.h b/src/updater_components/prefs/mock_pref_change_callback.h
new file mode 100644
index 0000000..9c0aeec
--- /dev/null
+++ b/src/updater_components/prefs/mock_pref_change_callback.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_
+#define COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_
+
+#include <string>
+
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::Pointee;
+using testing::Property;
+using testing::Truly;
+
+// Matcher that checks whether the current value of the preference named
+// |pref_name| in |prefs| matches |value|. If |value| is NULL, the matcher
+// checks that the value is not set.
+MATCHER_P3(PrefValueMatches, prefs, pref_name, value, "") {
+  const PrefService::Preference* pref = prefs->FindPreference(pref_name);
+  if (!pref)
+    return false;
+
+  const base::Value* actual_value = pref->GetValue();
+  if (!actual_value)
+    return value == NULL;
+  if (!value)
+    return actual_value == NULL;
+  return value->Equals(actual_value);
+}
+
+// A mock for testing preference notifications and easy setup of expectations.
+class MockPrefChangeCallback {
+ public:
+  explicit MockPrefChangeCallback(PrefService* prefs);
+  virtual ~MockPrefChangeCallback();
+
+  PrefChangeRegistrar::NamedChangeCallback GetCallback();
+
+  MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
+
+  void Expect(const std::string& pref_name,
+              const base::Value* value);
+
+ private:
+  PrefService* prefs_;
+};
+
+#endif  // COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_
diff --git a/src/updater_components/prefs/overlay_user_pref_store.cc b/src/updater_components/prefs/overlay_user_pref_store.cc
new file mode 100644
index 0000000..0baa6c4
--- /dev/null
+++ b/src/updater_components/prefs/overlay_user_pref_store.cc
@@ -0,0 +1,247 @@
+// 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 "components/prefs/overlay_user_pref_store.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "components/prefs/in_memory_pref_store.h"
+
+// Allows us to monitor two pref stores and tell updates from them apart. It
+// essentially mimics a Callback for the Observer interface (e.g. it allows
+// binding additional arguments).
+class OverlayUserPrefStore::ObserverAdapter : public PrefStore::Observer {
+ public:
+  ObserverAdapter(bool ephemeral, OverlayUserPrefStore* parent)
+      : ephemeral_user_pref_store_(ephemeral), parent_(parent) {}
+
+  // Methods of PrefStore::Observer.
+  void OnPrefValueChanged(const std::string& key) override {
+    parent_->OnPrefValueChanged(ephemeral_user_pref_store_, key);
+  }
+  void OnInitializationCompleted(bool succeeded) override {
+    parent_->OnInitializationCompleted(ephemeral_user_pref_store_, succeeded);
+  }
+
+ private:
+  // Is the update for the ephemeral?
+  const bool ephemeral_user_pref_store_;
+  OverlayUserPrefStore* const parent_;
+};
+
+OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* persistent)
+    : OverlayUserPrefStore(new InMemoryPrefStore(), persistent) {}
+
+OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* ephemeral,
+                                           PersistentPrefStore* persistent)
+    : ephemeral_pref_store_observer_(
+          std::make_unique<OverlayUserPrefStore::ObserverAdapter>(true, this)),
+      persistent_pref_store_observer_(
+          std::make_unique<OverlayUserPrefStore::ObserverAdapter>(false, this)),
+      ephemeral_user_pref_store_(ephemeral),
+      persistent_user_pref_store_(persistent) {
+  DCHECK(ephemeral->IsInitializationComplete());
+  ephemeral_user_pref_store_->AddObserver(ephemeral_pref_store_observer_.get());
+  persistent_user_pref_store_->AddObserver(
+      persistent_pref_store_observer_.get());
+}
+
+bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const {
+  return ephemeral_user_pref_store_->GetValue(key, nullptr);
+}
+
+void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void OverlayUserPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool OverlayUserPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
+}
+
+bool OverlayUserPrefStore::IsInitializationComplete() const {
+  return persistent_user_pref_store_->IsInitializationComplete() &&
+         ephemeral_user_pref_store_->IsInitializationComplete();
+}
+
+bool OverlayUserPrefStore::GetValue(const std::string& key,
+                                    const base::Value** result) const {
+  // If the |key| shall NOT be stored in the ephemeral store, there must not
+  // be an entry.
+  DCHECK(!ShallBeStoredInPersistent(key) ||
+         !ephemeral_user_pref_store_->GetValue(key, nullptr));
+
+  if (ephemeral_user_pref_store_->GetValue(key, result))
+    return true;
+  return persistent_user_pref_store_->GetValue(key, result);
+}
+
+std::unique_ptr<base::DictionaryValue> OverlayUserPrefStore::GetValues() const {
+  auto values = ephemeral_user_pref_store_->GetValues();
+  auto persistent_values = persistent_user_pref_store_->GetValues();
+
+  // Output |values| are read from |ephemeral_user_pref_store_| (in-memory
+  // store). Then the values of preferences in |persistent_names_set_| are
+  // overwritten by the content of |persistent_user_pref_store_| (the persistent
+  // store).
+  for (const auto& key : persistent_names_set_) {
+    std::unique_ptr<base::Value> out_value;
+    persistent_values->Remove(key, &out_value);
+    if (out_value) {
+      values->Set(key, std::move(out_value));
+    }
+  }
+  return values;
+}
+
+bool OverlayUserPrefStore::GetMutableValue(const std::string& key,
+                                           base::Value** result) {
+  if (ShallBeStoredInPersistent(key))
+    return persistent_user_pref_store_->GetMutableValue(key, result);
+
+  written_ephemeral_names_.insert(key);
+  if (ephemeral_user_pref_store_->GetMutableValue(key, result))
+    return true;
+
+  // Try to create copy of persistent if the ephemeral does not contain a value.
+  base::Value* persistent_value = nullptr;
+  if (!persistent_user_pref_store_->GetMutableValue(key, &persistent_value))
+    return false;
+
+  ephemeral_user_pref_store_->SetValue(
+      key, persistent_value->CreateDeepCopy(),
+      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  ephemeral_user_pref_store_->GetMutableValue(key, result);
+  return true;
+}
+
+void OverlayUserPrefStore::SetValue(const std::string& key,
+                                    std::unique_ptr<base::Value> value,
+                                    uint32_t flags) {
+  if (ShallBeStoredInPersistent(key)) {
+    persistent_user_pref_store_->SetValue(key, std::move(value), flags);
+    return;
+  }
+
+  // TODO(https://crbug.com/861722): If we always store in in-memory storage
+  // and conditionally also stored in persistent one, we wouldn't have to do a
+  // complex merge in GetValues().
+  written_ephemeral_names_.insert(key);
+  ephemeral_user_pref_store_->SetValue(key, std::move(value), flags);
+}
+
+void OverlayUserPrefStore::SetValueSilently(const std::string& key,
+                                            std::unique_ptr<base::Value> value,
+                                            uint32_t flags) {
+  if (ShallBeStoredInPersistent(key)) {
+    persistent_user_pref_store_->SetValueSilently(key, std::move(value), flags);
+    return;
+  }
+
+  written_ephemeral_names_.insert(key);
+  ephemeral_user_pref_store_->SetValueSilently(key, std::move(value), flags);
+}
+
+void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+  if (ShallBeStoredInPersistent(key)) {
+    persistent_user_pref_store_->RemoveValue(key, flags);
+    return;
+  }
+
+  written_ephemeral_names_.insert(key);
+  ephemeral_user_pref_store_->RemoveValue(key, flags);
+}
+
+bool OverlayUserPrefStore::ReadOnly() const {
+  return false;
+}
+
+PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const {
+  return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() {
+  // We do not read intentionally.
+  OnInitializationCompleted(/* ephemeral */ false, true);
+  return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+void OverlayUserPrefStore::ReadPrefsAsync(
+    ReadErrorDelegate* error_delegate_raw) {
+  std::unique_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
+  // We do not read intentionally.
+  OnInitializationCompleted(/* ephemeral */ false, true);
+}
+
+void OverlayUserPrefStore::CommitPendingWrite(
+    base::OnceClosure reply_callback,
+    base::OnceClosure synchronous_done_callback) {
+  persistent_user_pref_store_->CommitPendingWrite(
+      std::move(reply_callback), std::move(synchronous_done_callback));
+  // We do not write our content intentionally.
+}
+
+void OverlayUserPrefStore::SchedulePendingLossyWrites() {
+  persistent_user_pref_store_->SchedulePendingLossyWrites();
+}
+
+void OverlayUserPrefStore::ReportValueChanged(const std::string& key,
+                                              uint32_t flags) {
+  for (PrefStore::Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+}
+
+void OverlayUserPrefStore::RegisterPersistentPref(const std::string& key) {
+  DCHECK(!key.empty()) << "Key is empty";
+  DCHECK(persistent_names_set_.find(key) == persistent_names_set_.end())
+      << "Key already registered: " << key;
+  persistent_names_set_.insert(key);
+}
+
+void OverlayUserPrefStore::ClearMutableValues() {
+  for (const auto& key : written_ephemeral_names_) {
+    ephemeral_user_pref_store_->RemoveValue(
+        key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  }
+}
+
+void OverlayUserPrefStore::OnStoreDeletionFromDisk() {
+  persistent_user_pref_store_->OnStoreDeletionFromDisk();
+}
+
+OverlayUserPrefStore::~OverlayUserPrefStore() {
+  ephemeral_user_pref_store_->RemoveObserver(
+      ephemeral_pref_store_observer_.get());
+  persistent_user_pref_store_->RemoveObserver(
+      persistent_pref_store_observer_.get());
+}
+
+void OverlayUserPrefStore::OnPrefValueChanged(bool ephemeral,
+                                              const std::string& key) {
+  if (ephemeral) {
+    ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
+  } else {
+    if (!ephemeral_user_pref_store_->GetValue(key, nullptr))
+      ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
+  }
+}
+
+void OverlayUserPrefStore::OnInitializationCompleted(bool ephemeral,
+                                                     bool succeeded) {
+  if (!IsInitializationComplete())
+    return;
+  for (PrefStore::Observer& observer : observers_)
+    observer.OnInitializationCompleted(succeeded);
+}
+
+bool OverlayUserPrefStore::ShallBeStoredInPersistent(
+    const std::string& key) const {
+  return persistent_names_set_.find(key) != persistent_names_set_.end();
+}
diff --git a/src/updater_components/prefs/overlay_user_pref_store.h b/src/updater_components/prefs/overlay_user_pref_store.h
new file mode 100644
index 0000000..9b7c95a
--- /dev/null
+++ b/src/updater_components/prefs/overlay_user_pref_store.h
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_
+#define COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_value_map.h"
+#include "components/prefs/prefs_export.h"
+
+// PersistentPrefStore that directs all write operations into an in-memory
+// PrefValueMap. Read operations are first answered by the PrefValueMap.
+// If the PrefValueMap does not contain a value for the requested key,
+// the look-up is passed on to an underlying PersistentPrefStore
+// |persistent_user_pref_store_|.
+class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore
+    : public PersistentPrefStore {
+ public:
+  explicit OverlayUserPrefStore(PersistentPrefStore* persistent);
+  // The |ephemeral| store must already be initialized.
+  OverlayUserPrefStore(PersistentPrefStore* ephemeral,
+                       PersistentPrefStore* persistent);
+
+  // Returns true if a value has been set for the |key| in this
+  // OverlayUserPrefStore, i.e. if it potentially overrides a value
+  // from the |persistent_user_pref_store_|.
+  virtual bool IsSetInOverlay(const std::string& key) const;
+
+  // Methods of PrefStore.
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+  bool IsInitializationComplete() const override;
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+
+  // Methods of PersistentPrefStore.
+  bool GetMutableValue(const std::string& key, base::Value** result) override;
+  void SetValue(const std::string& key,
+                std::unique_ptr<base::Value> value,
+                uint32_t flags) override;
+  void SetValueSilently(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) override;
+  void RemoveValue(const std::string& key, uint32_t flags) override;
+  bool ReadOnly() const override;
+  PrefReadError GetReadError() const override;
+  PrefReadError ReadPrefs() override;
+  void ReadPrefsAsync(ReadErrorDelegate* delegate) override;
+  void CommitPendingWrite(base::OnceClosure reply_callback,
+                          base::OnceClosure synchronous_done_callback) override;
+  void SchedulePendingLossyWrites() override;
+  void ReportValueChanged(const std::string& key, uint32_t flags) override;
+
+  // Registers preferences that should be stored in the persistent preferences
+  // (|persistent_user_pref_store_|).
+  void RegisterPersistentPref(const std::string& key);
+
+  void ClearMutableValues() override;
+  void OnStoreDeletionFromDisk() override;
+
+ protected:
+  ~OverlayUserPrefStore() override;
+
+ private:
+  typedef std::set<std::string> NamesSet;
+  class ObserverAdapter;
+
+  void OnPrefValueChanged(bool ephemeral, const std::string& key);
+  void OnInitializationCompleted(bool ephemeral, bool succeeded);
+
+  // Returns true if |key| corresponds to a preference that shall be stored in
+  // persistent PrefStore.
+  bool ShallBeStoredInPersistent(const std::string& key) const;
+
+  base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
+  std::unique_ptr<ObserverAdapter> ephemeral_pref_store_observer_;
+  std::unique_ptr<ObserverAdapter> persistent_pref_store_observer_;
+  scoped_refptr<PersistentPrefStore> ephemeral_user_pref_store_;
+  scoped_refptr<PersistentPrefStore> persistent_user_pref_store_;
+  NamesSet persistent_names_set_;
+  NamesSet written_ephemeral_names_;
+
+  DISALLOW_COPY_AND_ASSIGN(OverlayUserPrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_
diff --git a/src/updater_components/prefs/overlay_user_pref_store_unittest.cc b/src/updater_components/prefs/overlay_user_pref_store_unittest.cc
new file mode 100644
index 0000000..1c0c855
--- /dev/null
+++ b/src/updater_components/prefs/overlay_user_pref_store_unittest.cc
@@ -0,0 +1,282 @@
+// 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 "components/prefs/overlay_user_pref_store.h"
+
+#include <memory>
+
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "components/prefs/persistent_pref_store_unittest.h"
+#include "components/prefs/pref_store_observer_mock.h"
+#include "components/prefs/testing_pref_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Mock;
+using ::testing::StrEq;
+
+namespace base {
+namespace {
+
+const char kBrowserWindowPlacement[] = "browser.window_placement";
+const char kShowBookmarkBar[] = "bookmark_bar.show_on_all_tabs";
+const char kSharedKey[] = "sync_promo.show_on_first_run_allowed";
+
+const char* const regular_key = kBrowserWindowPlacement;
+const char* const persistent_key = kShowBookmarkBar;
+const char* const shared_key = kSharedKey;
+
+}  // namespace
+
+class OverlayUserPrefStoreTest : public testing::Test {
+ protected:
+  OverlayUserPrefStoreTest()
+      : underlay_(new TestingPrefStore()),
+        overlay_(new OverlayUserPrefStore(underlay_.get())) {
+    overlay_->RegisterPersistentPref(persistent_key);
+    overlay_->RegisterPersistentPref(shared_key);
+  }
+
+  ~OverlayUserPrefStoreTest() override {}
+
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<TestingPrefStore> underlay_;
+  scoped_refptr<OverlayUserPrefStore> overlay_;
+};
+
+TEST_F(OverlayUserPrefStoreTest, Observer) {
+  PrefStoreObserverMock obs;
+  overlay_->AddObserver(&obs);
+
+  // Check that underlay first value is reported.
+  underlay_->SetValue(regular_key, std::make_unique<Value>(42),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(regular_key);
+
+  // Check that underlay overwriting is reported.
+  underlay_->SetValue(regular_key, std::make_unique<Value>(43),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(regular_key);
+
+  // Check that overwriting change in overlay is reported.
+  overlay_->SetValue(regular_key, std::make_unique<Value>(44),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(regular_key);
+
+  // Check that hidden underlay change is not reported.
+  underlay_->SetValue(regular_key, std::make_unique<Value>(45),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(obs.changed_keys.empty());
+
+  // Check that overlay remove is reported.
+  overlay_->RemoveValue(regular_key,
+                        WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(regular_key);
+
+  // Check that underlay remove is reported.
+  underlay_->RemoveValue(regular_key,
+                         WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(regular_key);
+
+  // Check respecting of silence.
+  overlay_->SetValueSilently(regular_key, std::make_unique<Value>(46),
+                             WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(obs.changed_keys.empty());
+
+  overlay_->RemoveObserver(&obs);
+
+  // Check successful unsubscription.
+  underlay_->SetValue(regular_key, std::make_unique<Value>(47),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  overlay_->SetValue(regular_key, std::make_unique<Value>(48),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(obs.changed_keys.empty());
+}
+
+TEST_F(OverlayUserPrefStoreTest, GetAndSet) {
+  const Value* value = nullptr;
+  EXPECT_FALSE(overlay_->GetValue(regular_key, &value));
+  EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
+
+  underlay_->SetValue(regular_key, std::make_unique<Value>(42),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  // Value shines through:
+  EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+
+  EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+
+  overlay_->SetValue(regular_key, std::make_unique<Value>(43),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(43).Equals(value));
+
+  EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+
+  overlay_->RemoveValue(regular_key,
+                        WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  // Value shines through:
+  EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+
+  EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+}
+
+// Check that GetMutableValue does not return the dictionary of the underlay.
+TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) {
+  underlay_->SetValue(regular_key, std::make_unique<DictionaryValue>(),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  Value* modify = nullptr;
+  EXPECT_TRUE(overlay_->GetMutableValue(regular_key, &modify));
+  ASSERT_TRUE(modify);
+  ASSERT_TRUE(modify->is_dict());
+  static_cast<DictionaryValue*>(modify)->SetInteger(regular_key, 42);
+
+  Value* original_in_underlay = nullptr;
+  EXPECT_TRUE(underlay_->GetMutableValue(regular_key, &original_in_underlay));
+  ASSERT_TRUE(original_in_underlay);
+  ASSERT_TRUE(original_in_underlay->is_dict());
+  EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty());
+
+  Value* modified = nullptr;
+  EXPECT_TRUE(overlay_->GetMutableValue(regular_key, &modified));
+  ASSERT_TRUE(modified);
+  ASSERT_TRUE(modified->is_dict());
+  EXPECT_EQ(*modify, *modified);
+}
+
+// Here we consider a global preference that is not overlayed.
+TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
+  PrefStoreObserverMock obs;
+  overlay_->AddObserver(&obs);
+
+  const Value* value = nullptr;
+
+  // Check that underlay first value is reported.
+  underlay_->SetValue(persistent_key, std::make_unique<Value>(42),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(persistent_key);
+
+  // Check that underlay overwriting is reported.
+  underlay_->SetValue(persistent_key, std::make_unique<Value>(43),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(persistent_key);
+
+  // Check that we get this value from the overlay
+  EXPECT_TRUE(overlay_->GetValue(persistent_key, &value));
+  EXPECT_TRUE(base::Value(43).Equals(value));
+
+  // Check that overwriting change in overlay is reported.
+  overlay_->SetValue(persistent_key, std::make_unique<Value>(44),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(persistent_key);
+
+  // Check that we get this value from the overlay and the underlay.
+  EXPECT_TRUE(overlay_->GetValue(persistent_key, &value));
+  EXPECT_TRUE(base::Value(44).Equals(value));
+  EXPECT_TRUE(underlay_->GetValue(persistent_key, &value));
+  EXPECT_TRUE(base::Value(44).Equals(value));
+
+  // Check that overlay remove is reported.
+  overlay_->RemoveValue(persistent_key,
+                        WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  obs.VerifyAndResetChangedKey(persistent_key);
+
+  // Check that value was removed from overlay and underlay
+  EXPECT_FALSE(overlay_->GetValue(persistent_key, &value));
+  EXPECT_FALSE(underlay_->GetValue(persistent_key, &value));
+
+  // Check respecting of silence.
+  overlay_->SetValueSilently(persistent_key, std::make_unique<Value>(46),
+                             WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(obs.changed_keys.empty());
+
+  overlay_->RemoveObserver(&obs);
+
+  // Check successful unsubscription.
+  underlay_->SetValue(persistent_key, std::make_unique<Value>(47),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  overlay_->SetValue(persistent_key, std::make_unique<Value>(48),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  EXPECT_TRUE(obs.changed_keys.empty());
+}
+
+// Check that mutable values are removed correctly.
+TEST_F(OverlayUserPrefStoreTest, ClearMutableValues) {
+  // Set in overlay and underlay the same preference.
+  underlay_->SetValue(regular_key, std::make_unique<Value>(42),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  overlay_->SetValue(regular_key, std::make_unique<Value>(43),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  const Value* value = nullptr;
+  // Check that an overlay preference is returned.
+  EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(43).Equals(value));
+  overlay_->ClearMutableValues();
+
+  // Check that an underlay preference is returned.
+  EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+}
+
+// Check that mutable values are removed correctly when using a silent set.
+TEST_F(OverlayUserPrefStoreTest, ClearMutableValues_Silently) {
+  // Set in overlay and underlay the same preference.
+  underlay_->SetValueSilently(regular_key, std::make_unique<Value>(42),
+                              WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  overlay_->SetValueSilently(regular_key, std::make_unique<Value>(43),
+                             WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  const Value* value = nullptr;
+  // Check that an overlay preference is returned.
+  EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(43).Equals(value));
+  overlay_->ClearMutableValues();
+
+  // Check that an underlay preference is returned.
+  EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+}
+
+TEST_F(OverlayUserPrefStoreTest, GetValues) {
+  // To check merge behavior, create underlay and overlay so each has a key the
+  // other doesn't have and they have one key in common.
+  underlay_->SetValue(persistent_key, std::make_unique<Value>(42),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  overlay_->SetValue(regular_key, std::make_unique<Value>(43),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  underlay_->SetValue(shared_key, std::make_unique<Value>(42),
+                      WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  overlay_->SetValue(shared_key, std::make_unique<Value>(43),
+                     WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  auto values = overlay_->GetValues();
+  const Value* value = nullptr;
+  // Check that an overlay preference is returned.
+  ASSERT_TRUE(values->Get(persistent_key, &value));
+  EXPECT_TRUE(base::Value(42).Equals(value));
+
+  // Check that an underlay preference is returned.
+  ASSERT_TRUE(values->Get(regular_key, &value));
+  EXPECT_TRUE(base::Value(43).Equals(value));
+
+  // Check that the overlay is preferred.
+  ASSERT_TRUE(values->Get(shared_key, &value));
+  EXPECT_TRUE(base::Value(43).Equals(value));
+}
+
+TEST_F(OverlayUserPrefStoreTest, CommitPendingWriteWithCallback) {
+  TestCommitPendingWriteWithCallback(overlay_.get(), &task_environment_);
+}
+
+}  // namespace base
diff --git a/src/updater_components/prefs/persistent_pref_store.cc b/src/updater_components/prefs/persistent_pref_store.cc
new file mode 100644
index 0000000..9086c1e
--- /dev/null
+++ b/src/updater_components/prefs/persistent_pref_store.cc
@@ -0,0 +1,31 @@
+// 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 "components/prefs/persistent_pref_store.h"
+
+#include <utility>
+
+#include "base/threading/sequenced_task_runner_handle.h"
+
+void PersistentPrefStore::CommitPendingWrite(
+    base::OnceClosure reply_callback,
+    base::OnceClosure synchronous_done_callback) {
+  // Default behavior for PersistentPrefStore implementation that don't issue
+  // disk operations: schedule the callback immediately.
+  // |synchronous_done_callback| is allowed to be invoked synchronously (and
+  // must be here since we have no other way to post it which isn't the current
+  // sequence).
+
+  if (synchronous_done_callback)
+    std::move(synchronous_done_callback).Run();
+
+  if (reply_callback) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                     std::move(reply_callback));
+  }
+}
+
+bool PersistentPrefStore::IsInMemoryPrefStore() const {
+  return false;
+}
diff --git a/src/updater_components/prefs/persistent_pref_store.h b/src/updater_components/prefs/persistent_pref_store.h
new file mode 100644
index 0000000..967ccda
--- /dev/null
+++ b/src/updater_components/prefs/persistent_pref_store.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_
+#define COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/prefs/prefs_export.h"
+#include "components/prefs/writeable_pref_store.h"
+
+// This interface is complementary to the PrefStore interface, declaring
+// additional functionality that adds support for setting values and persisting
+// the data to some backing store.
+class COMPONENTS_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore {
+ public:
+  // Unique integer code for each type of error so we can report them
+  // distinctly in a histogram.
+  // NOTE: Don't change the explicit values of the enums as it will change the
+  // server's meaning of the histogram.
+  enum PrefReadError {
+    PREF_READ_ERROR_NONE = 0,
+    PREF_READ_ERROR_JSON_PARSE = 1,
+    PREF_READ_ERROR_JSON_TYPE = 2,
+    PREF_READ_ERROR_ACCESS_DENIED = 3,
+    PREF_READ_ERROR_FILE_OTHER = 4,
+    PREF_READ_ERROR_FILE_LOCKED = 5,
+    PREF_READ_ERROR_NO_FILE = 6,
+    PREF_READ_ERROR_JSON_REPEAT = 7,
+    // PREF_READ_ERROR_OTHER = 8,  // Deprecated.
+    PREF_READ_ERROR_FILE_NOT_SPECIFIED = 9,
+    // Indicates that ReadPrefs() couldn't complete synchronously and is waiting
+    // for an asynchronous task to complete first.
+    PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE = 10,
+    PREF_READ_ERROR_MAX_ENUM
+  };
+
+  class ReadErrorDelegate {
+   public:
+    virtual ~ReadErrorDelegate() {}
+
+    virtual void OnError(PrefReadError error) = 0;
+  };
+
+  // Whether the store is in a pseudo-read-only mode where changes are not
+  // actually persisted to disk.  This happens in some cases when there are
+  // read errors during startup.
+  virtual bool ReadOnly() const = 0;
+
+  // Gets the read error. Only valid if IsInitializationComplete() returns true.
+  virtual PrefReadError GetReadError() const = 0;
+
+  // Reads the preferences from disk. Notifies observers via
+  // "PrefStore::OnInitializationCompleted" when done.
+  virtual PrefReadError ReadPrefs() = 0;
+
+  // Reads the preferences from disk asynchronously. Notifies observers via
+  // "PrefStore::OnInitializationCompleted" when done. Also it fires
+  // |error_delegate| if it is not NULL and reading error has occurred.
+  // Owns |error_delegate|.
+  virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0;
+
+  // Lands pending writes to disk. |reply_callback| will be posted to the
+  // current sequence when changes have been written.
+  // |synchronous_done_callback| on the other hand will be invoked right away
+  // wherever the writes complete (could even be invoked synchronously if no
+  // writes need to occur); this is useful when the current thread cannot pump
+  // messages to observe the reply (e.g. nested loops banned on main thread
+  // during shutdown). |synchronous_done_callback| must be thread-safe.
+  virtual void CommitPendingWrite(
+      base::OnceClosure reply_callback = base::OnceClosure(),
+      base::OnceClosure synchronous_done_callback = base::OnceClosure());
+
+  // Schedule a write if there is any lossy data pending. Unlike
+  // CommitPendingWrite() this does not immediately sync to disk, instead it
+  // triggers an eventual write if there is lossy data pending and if there
+  // isn't one scheduled already.
+  virtual void SchedulePendingLossyWrites() = 0;
+
+  // It should be called only for Incognito pref store.
+  virtual void ClearMutableValues() = 0;
+
+  // Cleans preference data that may have been saved outside of the store.
+  virtual void OnStoreDeletionFromDisk() = 0;
+
+  // TODO(crbug.com/942491) Remove this after fixing the bug.
+  virtual bool IsInMemoryPrefStore() const;
+
+ protected:
+  ~PersistentPrefStore() override {}
+};
+
+#endif  // COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_
diff --git a/src/updater_components/prefs/persistent_pref_store_unittest.cc b/src/updater_components/prefs/persistent_pref_store_unittest.cc
new file mode 100644
index 0000000..9ea3f98
--- /dev/null
+++ b/src/updater_components/prefs/persistent_pref_store_unittest.cc
@@ -0,0 +1,26 @@
+// 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 "components/prefs/persistent_pref_store.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/sequence_checker_impl.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+void TestCommitPendingWriteWithCallback(
+    PersistentPrefStore* store,
+    base::test::TaskEnvironment* task_environment) {
+  base::RunLoop run_loop;
+  base::SequenceCheckerImpl sequence_checker;
+  store->CommitPendingWrite(base::BindOnce(
+      [](base::SequenceCheckerImpl* sequence_checker, base::RunLoop* run_loop) {
+        EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
+        run_loop->Quit();
+      },
+      base::Unretained(&sequence_checker), base::Unretained(&run_loop)));
+  task_environment->RunUntilIdle();
+  run_loop.Run();
+}
diff --git a/src/updater_components/prefs/persistent_pref_store_unittest.h b/src/updater_components/prefs/persistent_pref_store_unittest.h
new file mode 100644
index 0000000..121f942
--- /dev/null
+++ b/src/updater_components/prefs/persistent_pref_store_unittest.h
@@ -0,0 +1,24 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_
+#define COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_
+
+namespace base {
+namespace test {
+class TaskEnvironment;
+}
+}  // namespace base
+
+class PersistentPrefStore;
+
+// Calls CommitPendingWrite() on |store| with a callback. Verifies that the
+// callback runs on the appropriate sequence. |task_environment| is the
+// test's TaskEnvironment. This function is meant to be reused in the
+// tests of various PersistentPrefStore implementations.
+void TestCommitPendingWriteWithCallback(
+    PersistentPrefStore* store,
+    base::test::TaskEnvironment* task_environment);
+
+#endif  // COMPONENTS_PREFS_PERSISTENT_PREF_STORE_UNITTEST_H_
diff --git a/src/updater_components/prefs/pref_change_registrar.cc b/src/updater_components/prefs/pref_change_registrar.cc
new file mode 100644
index 0000000..da7de46
--- /dev/null
+++ b/src/updater_components/prefs/pref_change_registrar.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2010 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 "components/prefs/pref_change_registrar.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "components/prefs/pref_service.h"
+
+PrefChangeRegistrar::PrefChangeRegistrar() : service_(nullptr) {}
+
+PrefChangeRegistrar::~PrefChangeRegistrar() {
+  // If you see an invalid memory access in this destructor, this
+  // PrefChangeRegistrar might be subscribed to an OffTheRecordProfileImpl that
+  // has been destroyed. This should not happen any more but be warned.
+  // Feel free to contact battre@chromium.org in case this happens.
+  RemoveAll();
+}
+
+void PrefChangeRegistrar::Init(PrefService* service) {
+  DCHECK(IsEmpty() || service_ == service);
+  service_ = service;
+}
+
+void PrefChangeRegistrar::Add(const std::string& path,
+                              const base::Closure& obs) {
+  Add(path, base::Bind(&PrefChangeRegistrar::InvokeUnnamedCallback, obs));
+}
+
+void PrefChangeRegistrar::Add(const std::string& path,
+                              const NamedChangeCallback& obs) {
+  if (!service_) {
+    NOTREACHED();
+    return;
+  }
+  DCHECK(!IsObserved(path)) << "Already had pref, \"" << path
+                            << "\", registered.";
+
+  service_->AddPrefObserver(path, this);
+  observers_[path] = obs;
+}
+
+void PrefChangeRegistrar::Remove(const std::string& path) {
+  DCHECK(IsObserved(path));
+
+  observers_.erase(path);
+  service_->RemovePrefObserver(path, this);
+}
+
+void PrefChangeRegistrar::RemoveAll() {
+  for (ObserverMap::const_iterator it = observers_.begin();
+       it != observers_.end(); ++it) {
+    service_->RemovePrefObserver(it->first, this);
+  }
+
+  observers_.clear();
+}
+
+bool PrefChangeRegistrar::IsEmpty() const {
+  return observers_.empty();
+}
+
+bool PrefChangeRegistrar::IsObserved(const std::string& pref) {
+  return observers_.find(pref) != observers_.end();
+}
+
+bool PrefChangeRegistrar::IsManaged() {
+  for (ObserverMap::const_iterator it = observers_.begin();
+       it != observers_.end(); ++it) {
+    const PrefService::Preference* pref = service_->FindPreference(it->first);
+    if (pref && pref->IsManaged())
+      return true;
+  }
+  return false;
+}
+
+void PrefChangeRegistrar::OnPreferenceChanged(PrefService* service,
+                                              const std::string& pref) {
+  if (IsObserved(pref))
+    observers_[pref].Run(pref);
+}
+
+void PrefChangeRegistrar::InvokeUnnamedCallback(const base::Closure& callback,
+                                                const std::string& pref_name) {
+  callback.Run();
+}
+
+PrefService* PrefChangeRegistrar::prefs() {
+  return service_;
+}
+
+const PrefService* PrefChangeRegistrar::prefs() const {
+  return service_;
+}
diff --git a/src/updater_components/prefs/pref_change_registrar.h b/src/updater_components/prefs/pref_change_registrar.h
new file mode 100644
index 0000000..8aba1b3
--- /dev/null
+++ b/src/updater_components/prefs/pref_change_registrar.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2010 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 COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_
+#define COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/prefs/pref_observer.h"
+#include "components/prefs/prefs_export.h"
+
+class PrefService;
+
+// Automatically manages the registration of one or more pref change observers
+// with a PrefStore. Functions much like NotificationRegistrar, but specifically
+// manages observers of preference changes. When the Registrar is destroyed,
+// all registered observers are automatically unregistered with the PrefStore.
+class COMPONENTS_PREFS_EXPORT PrefChangeRegistrar final : public PrefObserver {
+ public:
+  // You can register this type of callback if you need to know the
+  // path of the preference that is changing.
+  using NamedChangeCallback = base::RepeatingCallback<void(const std::string&)>;
+
+  PrefChangeRegistrar();
+  ~PrefChangeRegistrar();
+
+  // Must be called before adding or removing observers. Can be called more
+  // than once as long as the value of |service| doesn't change.
+  void Init(PrefService* service);
+
+  // Adds a pref observer for the specified pref |path| and |obs| observer
+  // object. All registered observers will be automatically unregistered
+  // when the registrar's destructor is called.
+  //
+  // The second version binds a callback that will receive the path of
+  // the preference that is changing as its parameter.
+  //
+  // Only one observer may be registered per path.
+  void Add(const std::string& path, const base::Closure& obs);
+  void Add(const std::string& path, const NamedChangeCallback& obs);
+
+  // Removes the pref observer registered for |path|.
+  void Remove(const std::string& path);
+
+  // Removes all observers that have been previously added with a call to Add.
+  void RemoveAll();
+
+  // Returns true if no pref observers are registered.
+  bool IsEmpty() const;
+
+  // Check whether |pref| is in the set of preferences being observed.
+  bool IsObserved(const std::string& pref);
+
+  // Check whether any of the observed preferences has the managed bit set.
+  bool IsManaged();
+
+  // Return the PrefService for this registrar.
+  PrefService* prefs();
+  const PrefService* prefs() const;
+
+ private:
+  // PrefObserver:
+  void OnPreferenceChanged(PrefService* service,
+                           const std::string& pref_name) override;
+
+  static void InvokeUnnamedCallback(const base::Closure& callback,
+                                    const std::string& pref_name);
+
+  using ObserverMap = std::map<std::string, NamedChangeCallback>;
+
+  ObserverMap observers_;
+  PrefService* service_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefChangeRegistrar);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_
diff --git a/src/updater_components/prefs/pref_change_registrar_unittest.cc b/src/updater_components/prefs/pref_change_registrar_unittest.cc
new file mode 100644
index 0000000..b02b13a
--- /dev/null
+++ b/src/updater_components/prefs/pref_change_registrar_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2010 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 "components/prefs/pref_change_registrar.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "components/prefs/pref_observer.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::Eq;
+
+namespace base {
+namespace {
+
+const char kHomePage[] = "homepage";
+const char kHomePageIsNewTabPage[] = "homepage_is_newtabpage";
+const char kApplicationLocale[] = "intl.app_locale";
+
+// A mock provider that allows us to capture pref observer changes.
+class MockPrefService : public TestingPrefServiceSimple {
+ public:
+  MockPrefService() {}
+  ~MockPrefService() override {}
+
+  MOCK_METHOD2(AddPrefObserver, void(const std::string&, PrefObserver*));
+  MOCK_METHOD2(RemovePrefObserver, void(const std::string&, PrefObserver*));
+};
+
+}  // namespace
+
+class PrefChangeRegistrarTest : public testing::Test {
+ public:
+  PrefChangeRegistrarTest() {}
+  ~PrefChangeRegistrarTest() override {}
+
+ protected:
+  void SetUp() override;
+
+  base::Closure observer() const { return base::DoNothing(); }
+
+  MockPrefService* service() const { return service_.get(); }
+
+ private:
+  std::unique_ptr<MockPrefService> service_;
+};
+
+void PrefChangeRegistrarTest::SetUp() {
+  service_.reset(new MockPrefService());
+}
+
+TEST_F(PrefChangeRegistrarTest, AddAndRemove) {
+  PrefChangeRegistrar registrar;
+  registrar.Init(service());
+
+  // Test adding.
+  EXPECT_CALL(*service(),
+              AddPrefObserver(Eq(std::string("test.pref.1")), &registrar));
+  EXPECT_CALL(*service(),
+              AddPrefObserver(Eq(std::string("test.pref.2")), &registrar));
+  registrar.Add("test.pref.1", observer());
+  registrar.Add("test.pref.2", observer());
+  EXPECT_FALSE(registrar.IsEmpty());
+
+  // Test removing.
+  Mock::VerifyAndClearExpectations(service());
+  EXPECT_CALL(*service(),
+              RemovePrefObserver(Eq(std::string("test.pref.1")), &registrar));
+  EXPECT_CALL(*service(),
+              RemovePrefObserver(Eq(std::string("test.pref.2")), &registrar));
+  registrar.Remove("test.pref.1");
+  registrar.Remove("test.pref.2");
+  EXPECT_TRUE(registrar.IsEmpty());
+
+  // Explicitly check the expectations now to make sure that the Removes
+  // worked (rather than the registrar destructor doing the work).
+  Mock::VerifyAndClearExpectations(service());
+}
+
+TEST_F(PrefChangeRegistrarTest, AutoRemove) {
+  PrefChangeRegistrar registrar;
+  registrar.Init(service());
+
+  // Setup of auto-remove.
+  EXPECT_CALL(*service(),
+              AddPrefObserver(Eq(std::string("test.pref.1")), &registrar));
+  registrar.Add("test.pref.1", observer());
+  Mock::VerifyAndClearExpectations(service());
+  EXPECT_FALSE(registrar.IsEmpty());
+
+  // Test auto-removing.
+  EXPECT_CALL(*service(),
+              RemovePrefObserver(Eq(std::string("test.pref.1")), &registrar));
+}
+
+TEST_F(PrefChangeRegistrarTest, RemoveAll) {
+  PrefChangeRegistrar registrar;
+  registrar.Init(service());
+
+  EXPECT_CALL(*service(),
+              AddPrefObserver(Eq(std::string("test.pref.1")), &registrar));
+  EXPECT_CALL(*service(),
+              AddPrefObserver(Eq(std::string("test.pref.2")), &registrar));
+  registrar.Add("test.pref.1", observer());
+  registrar.Add("test.pref.2", observer());
+  Mock::VerifyAndClearExpectations(service());
+
+  EXPECT_CALL(*service(),
+              RemovePrefObserver(Eq(std::string("test.pref.1")), &registrar));
+  EXPECT_CALL(*service(),
+              RemovePrefObserver(Eq(std::string("test.pref.2")), &registrar));
+  registrar.RemoveAll();
+  EXPECT_TRUE(registrar.IsEmpty());
+
+  // Explicitly check the expectations now to make sure that the RemoveAll
+  // worked (rather than the registrar destructor doing the work).
+  Mock::VerifyAndClearExpectations(service());
+}
+
+class ObserveSetOfPreferencesTest : public testing::Test {
+ public:
+  void SetUp() override {
+    pref_service_.reset(new TestingPrefServiceSimple);
+    PrefRegistrySimple* registry = pref_service_->registry();
+    registry->RegisterStringPref(kHomePage, "http://google.com");
+    registry->RegisterBooleanPref(kHomePageIsNewTabPage, false);
+    registry->RegisterStringPref(kApplicationLocale, std::string());
+  }
+
+  PrefChangeRegistrar* CreatePrefChangeRegistrar() {
+    PrefChangeRegistrar* pref_set = new PrefChangeRegistrar();
+    base::Closure callback = base::DoNothing();
+    pref_set->Init(pref_service_.get());
+    pref_set->Add(kHomePage, callback);
+    pref_set->Add(kHomePageIsNewTabPage, callback);
+    return pref_set;
+  }
+
+  MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
+
+  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+};
+
+TEST_F(ObserveSetOfPreferencesTest, IsObserved) {
+  std::unique_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar());
+  EXPECT_TRUE(pref_set->IsObserved(kHomePage));
+  EXPECT_TRUE(pref_set->IsObserved(kHomePageIsNewTabPage));
+  EXPECT_FALSE(pref_set->IsObserved(kApplicationLocale));
+}
+
+TEST_F(ObserveSetOfPreferencesTest, IsManaged) {
+  std::unique_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar());
+  EXPECT_FALSE(pref_set->IsManaged());
+  pref_service_->SetManagedPref(kHomePage,
+                                std::make_unique<Value>("http://crbug.com"));
+  EXPECT_TRUE(pref_set->IsManaged());
+  pref_service_->SetManagedPref(kHomePageIsNewTabPage,
+                                std::make_unique<Value>(true));
+  EXPECT_TRUE(pref_set->IsManaged());
+  pref_service_->RemoveManagedPref(kHomePage);
+  EXPECT_TRUE(pref_set->IsManaged());
+  pref_service_->RemoveManagedPref(kHomePageIsNewTabPage);
+  EXPECT_FALSE(pref_set->IsManaged());
+}
+
+TEST_F(ObserveSetOfPreferencesTest, Observe) {
+  using testing::_;
+  using testing::Mock;
+
+  PrefChangeRegistrar pref_set;
+  PrefChangeRegistrar::NamedChangeCallback callback = base::Bind(
+      &ObserveSetOfPreferencesTest::OnPreferenceChanged,
+      base::Unretained(this));
+  pref_set.Init(pref_service_.get());
+  pref_set.Add(kHomePage, callback);
+  pref_set.Add(kHomePageIsNewTabPage, callback);
+
+  EXPECT_CALL(*this, OnPreferenceChanged(kHomePage));
+  pref_service_->SetUserPref(kHomePage,
+                             std::make_unique<Value>("http://crbug.com"));
+  Mock::VerifyAndClearExpectations(this);
+
+  EXPECT_CALL(*this, OnPreferenceChanged(kHomePageIsNewTabPage));
+  pref_service_->SetUserPref(kHomePageIsNewTabPage,
+                             std::make_unique<Value>(true));
+  Mock::VerifyAndClearExpectations(this);
+
+  EXPECT_CALL(*this, OnPreferenceChanged(_)).Times(0);
+  pref_service_->SetUserPref(kApplicationLocale,
+                             std::make_unique<Value>("en_US.utf8"));
+  Mock::VerifyAndClearExpectations(this);
+}
+
+}  // namespace base
diff --git a/src/updater_components/prefs/pref_filter.h b/src/updater_components/prefs/pref_filter.h
new file mode 100644
index 0000000..3d52136
--- /dev/null
+++ b/src/updater_components/prefs/pref_filter.h
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PREFS_PREF_FILTER_H_
+#define COMPONENTS_PREFS_PREF_FILTER_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "components/prefs/prefs_export.h"
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+// Filters preferences as they are loaded from disk or updated at runtime.
+// Currently supported only by JsonPrefStore.
+class COMPONENTS_PREFS_EXPORT PrefFilter {
+ public:
+  // A pair of pre-write and post-write callbacks.
+  using OnWriteCallbackPair =
+      std::pair<base::Closure, base::Callback<void(bool success)>>;
+
+  // A callback to be invoked when |prefs| have been read (and possibly
+  // pre-modified) and are now ready to be handed back to this callback's
+  // builder. |schedule_write| indicates whether a write should be immediately
+  // scheduled (typically because the |prefs| were pre-modified).
+  using PostFilterOnLoadCallback =
+      base::Callback<void(std::unique_ptr<base::DictionaryValue> prefs,
+                          bool schedule_write)>;
+
+  virtual ~PrefFilter() {}
+
+  // This method is given ownership of the |pref_store_contents| read from disk
+  // before the underlying PersistentPrefStore gets to use them. It must hand
+  // them back via |post_filter_on_load_callback|, but may modify them first.
+  // Note: This method is asynchronous, which may make calls like
+  // PersistentPrefStore::ReadPrefs() asynchronous. The owner of filtered
+  // PersistentPrefStores should handle this to make the reads look synchronous
+  // to external users (see SegregatedPrefStore::ReadPrefs() for an example).
+  virtual void FilterOnLoad(
+      const PostFilterOnLoadCallback& post_filter_on_load_callback,
+      std::unique_ptr<base::DictionaryValue> pref_store_contents) = 0;
+
+  // Receives notification when a pref store value is changed, before Observers
+  // are notified.
+  virtual void FilterUpdate(const std::string& path) = 0;
+
+  // Receives notification when the pref store is about to serialize data
+  // contained in |pref_store_contents| to a string. Modifications to
+  // |pref_store_contents| will be persisted to disk and also affect the
+  // in-memory state.
+  // If the returned callbacks are non-null, they will be registered to be
+  // invoked synchronously after the next write (from the I/O TaskRunner so they
+  // must not be bound to thread-unsafe member state).
+  virtual OnWriteCallbackPair FilterSerializeData(
+      base::DictionaryValue* pref_store_contents) = 0;
+
+  // Cleans preference data that may have been saved outside of the store.
+  virtual void OnStoreDeletionFromDisk() = 0;
+};
+
+#endif  // COMPONENTS_PREFS_PREF_FILTER_H_
diff --git a/src/updater_components/prefs/pref_member.cc b/src/updater_components/prefs/pref_member.cc
new file mode 100644
index 0000000..a8fa12c
--- /dev/null
+++ b/src/updater_components/prefs/pref_member.cc
@@ -0,0 +1,218 @@
+// 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 "components/prefs/pref_member.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/value_conversions.h"
+#include "components/prefs/pref_service.h"
+
+using base::SequencedTaskRunner;
+
+namespace subtle {
+
+PrefMemberBase::PrefMemberBase() : prefs_(nullptr), setting_value_(false) {}
+
+PrefMemberBase::~PrefMemberBase() {
+  Destroy();
+}
+
+void PrefMemberBase::Init(const std::string& pref_name,
+                          PrefService* prefs,
+                          const NamedChangeCallback& observer) {
+  observer_ = observer;
+  Init(pref_name, prefs);
+}
+
+void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs) {
+  DCHECK(prefs);
+  DCHECK(pref_name_.empty());  // Check that Init is only called once.
+  prefs_ = prefs;
+  pref_name_ = pref_name;
+  // Check that the preference is registered.
+  DCHECK(prefs_->FindPreference(pref_name_)) << pref_name << " not registered.";
+
+  // Add ourselves as a pref observer so we can keep our local value in sync.
+  prefs_->AddPrefObserver(pref_name, this);
+}
+
+void PrefMemberBase::Destroy() {
+  if (prefs_ && !pref_name_.empty()) {
+    prefs_->RemovePrefObserver(pref_name_, this);
+    prefs_ = nullptr;
+  }
+}
+
+void PrefMemberBase::MoveToSequence(
+    scoped_refptr<SequencedTaskRunner> task_runner) {
+  VerifyValuePrefName();
+  // Load the value from preferences if it hasn't been loaded so far.
+  if (!internal())
+    UpdateValueFromPref(base::Closure());
+  internal()->MoveToSequence(std::move(task_runner));
+}
+
+void PrefMemberBase::OnPreferenceChanged(PrefService* service,
+                                         const std::string& pref_name) {
+  VerifyValuePrefName();
+  UpdateValueFromPref((!setting_value_ && !observer_.is_null()) ?
+      base::Bind(observer_, pref_name) : base::Closure());
+}
+
+void PrefMemberBase::UpdateValueFromPref(const base::Closure& callback) const {
+  VerifyValuePrefName();
+  const PrefService::Preference* pref = prefs_->FindPreference(pref_name_);
+  DCHECK(pref);
+  if (!internal())
+    CreateInternal();
+  internal()->UpdateValue(pref->GetValue()->DeepCopy(),
+                          pref->IsManaged(),
+                          pref->IsUserModifiable(),
+                          callback);
+}
+
+void PrefMemberBase::VerifyPref() const {
+  VerifyValuePrefName();
+  if (!internal())
+    UpdateValueFromPref(base::Closure());
+}
+
+void PrefMemberBase::InvokeUnnamedCallback(const base::Closure& callback,
+                                           const std::string& pref_name) {
+  callback.Run();
+}
+
+PrefMemberBase::Internal::Internal()
+    : owning_task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      is_managed_(false),
+      is_user_modifiable_(false) {}
+PrefMemberBase::Internal::~Internal() { }
+
+bool PrefMemberBase::Internal::IsOnCorrectSequence() const {
+  return owning_task_runner_->RunsTasksInCurrentSequence();
+}
+
+void PrefMemberBase::Internal::UpdateValue(base::Value* v,
+                                           bool is_managed,
+                                           bool is_user_modifiable,
+                                           base::OnceClosure callback) const {
+  std::unique_ptr<base::Value> value(v);
+  base::ScopedClosureRunner closure_runner(std::move(callback));
+  if (IsOnCorrectSequence()) {
+    bool rv = UpdateValueInternal(*value);
+    DCHECK(rv);
+    is_managed_ = is_managed;
+    is_user_modifiable_ = is_user_modifiable;
+  } else {
+    bool may_run = owning_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&PrefMemberBase::Internal::UpdateValue, this,
+                       value.release(), is_managed, is_user_modifiable,
+                       closure_runner.Release()));
+    DCHECK(may_run);
+  }
+}
+
+void PrefMemberBase::Internal::MoveToSequence(
+    scoped_refptr<SequencedTaskRunner> task_runner) {
+  CheckOnCorrectSequence();
+  owning_task_runner_ = std::move(task_runner);
+}
+
+bool PrefMemberVectorStringUpdate(const base::Value& value,
+                                  std::vector<std::string>* string_vector) {
+  if (!value.is_list())
+    return false;
+  const base::ListValue* list = static_cast<const base::ListValue*>(&value);
+
+  std::vector<std::string> local_vector;
+  for (auto it = list->begin(); it != list->end(); ++it) {
+    std::string string_value;
+    if (!it->GetAsString(&string_value))
+      return false;
+
+    local_vector.push_back(string_value);
+  }
+
+  string_vector->swap(local_vector);
+  return true;
+}
+
+}  // namespace subtle
+
+template <>
+void PrefMember<bool>::UpdatePref(const bool& value) {
+  prefs()->SetBoolean(pref_name(), value);
+}
+
+template <>
+bool PrefMember<bool>::Internal::UpdateValueInternal(
+    const base::Value& value) const {
+  return value.GetAsBoolean(&value_);
+}
+
+template <>
+void PrefMember<int>::UpdatePref(const int& value) {
+  prefs()->SetInteger(pref_name(), value);
+}
+
+template <>
+bool PrefMember<int>::Internal::UpdateValueInternal(
+    const base::Value& value) const {
+  return value.GetAsInteger(&value_);
+}
+
+template <>
+void PrefMember<double>::UpdatePref(const double& value) {
+  prefs()->SetDouble(pref_name(), value);
+}
+
+template <>
+bool PrefMember<double>::Internal::UpdateValueInternal(const base::Value& value)
+    const {
+  return value.GetAsDouble(&value_);
+}
+
+template <>
+void PrefMember<std::string>::UpdatePref(const std::string& value) {
+  prefs()->SetString(pref_name(), value);
+}
+
+template <>
+bool PrefMember<std::string>::Internal::UpdateValueInternal(
+    const base::Value& value)
+    const {
+  return value.GetAsString(&value_);
+}
+
+template <>
+void PrefMember<base::FilePath>::UpdatePref(const base::FilePath& value) {
+  prefs()->SetFilePath(pref_name(), value);
+}
+
+template <>
+bool PrefMember<base::FilePath>::Internal::UpdateValueInternal(
+    const base::Value& value)
+    const {
+  return base::GetValueAsFilePath(value, &value_);
+}
+
+template <>
+void PrefMember<std::vector<std::string> >::UpdatePref(
+    const std::vector<std::string>& value) {
+  base::ListValue list_value;
+  list_value.AppendStrings(value);
+  prefs()->Set(pref_name(), list_value);
+}
+
+template <>
+bool PrefMember<std::vector<std::string> >::Internal::UpdateValueInternal(
+    const base::Value& value) const {
+  return subtle::PrefMemberVectorStringUpdate(value, &value_);
+}
diff --git a/src/updater_components/prefs/pref_member.h b/src/updater_components/prefs/pref_member.h
new file mode 100644
index 0000000..f2166e6
--- /dev/null
+++ b/src/updater_components/prefs/pref_member.h
@@ -0,0 +1,355 @@
+// 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.
+//
+// A helper class that stays in sync with a preference (bool, int, real,
+// string or filepath).  For example:
+//
+// class MyClass {
+//  public:
+//   MyClass(PrefService* prefs) {
+//     my_string_.Init(prefs::kHomePage, prefs);
+//   }
+//  private:
+//   StringPrefMember my_string_;
+// };
+//
+// my_string_ should stay in sync with the prefs::kHomePage pref and will
+// update if either the pref changes or if my_string_.SetValue is called.
+//
+// An optional observer can be passed into the Init method which can be used to
+// notify MyClass of changes. Note that if you use SetValue(), the observer
+// will not be notified.
+
+#ifndef COMPONENTS_PREFS_PREF_MEMBER_H_
+#define COMPONENTS_PREFS_PREF_MEMBER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/values.h"
+#include "components/prefs/pref_observer.h"
+#include "components/prefs/prefs_export.h"
+
+class PrefService;
+
+namespace subtle {
+
+class COMPONENTS_PREFS_EXPORT PrefMemberBase : public PrefObserver {
+ public:
+  // Type of callback you can register if you need to know the name of
+  // the pref that is changing.
+  typedef base::Callback<void(const std::string&)> NamedChangeCallback;
+
+  PrefService* prefs() { return prefs_; }
+  const PrefService* prefs() const { return prefs_; }
+
+ protected:
+  class COMPONENTS_PREFS_EXPORT Internal
+      : public base::RefCountedThreadSafe<Internal> {
+   public:
+    Internal();
+
+    // Update the value, either by calling |UpdateValueInternal| directly
+    // or by dispatching to the right sequence.
+    // Takes ownership of |value|.
+    void UpdateValue(base::Value* value,
+                     bool is_managed,
+                     bool is_user_modifiable,
+                     base::OnceClosure callback) const;
+
+    void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner);
+
+    // See PrefMember<> for description.
+    bool IsManaged() const {
+      return is_managed_;
+    }
+
+    bool IsUserModifiable() const {
+      return is_user_modifiable_;
+    }
+
+   protected:
+    friend class base::RefCountedThreadSafe<Internal>;
+    virtual ~Internal();
+
+    void CheckOnCorrectSequence() const { DCHECK(IsOnCorrectSequence()); }
+
+   private:
+    // This method actually updates the value. It should only be called from
+    // the sequence the PrefMember is on.
+    virtual bool UpdateValueInternal(const base::Value& value) const = 0;
+
+    bool IsOnCorrectSequence() const;
+
+    scoped_refptr<base::SequencedTaskRunner> owning_task_runner_;
+    mutable bool is_managed_;
+    mutable bool is_user_modifiable_;
+
+    DISALLOW_COPY_AND_ASSIGN(Internal);
+  };
+
+  PrefMemberBase();
+  virtual ~PrefMemberBase();
+
+  // See PrefMember<> for description.
+  void Init(const std::string& pref_name,
+            PrefService* prefs,
+            const NamedChangeCallback& observer);
+  void Init(const std::string& pref_name, PrefService* prefs);
+
+  virtual void CreateInternal() const = 0;
+
+  // See PrefMember<> for description.
+  void Destroy();
+
+  void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner);
+
+  // PrefObserver
+  void OnPreferenceChanged(PrefService* service,
+                           const std::string& pref_name) override;
+
+  void VerifyValuePrefName() const {
+    DCHECK(!pref_name_.empty());
+  }
+
+  // This method is used to do the actual sync with the preference.
+  // Note: it is logically const, because it doesn't modify the state
+  // seen by the outside world. It is just doing a lazy load behind the scenes.
+  void UpdateValueFromPref(const base::Closure& callback) const;
+
+  // Verifies the preference name, and lazily loads the preference value if
+  // it hasn't been loaded yet.
+  void VerifyPref() const;
+
+  const std::string& pref_name() const { return pref_name_; }
+
+  virtual Internal* internal() const = 0;
+
+  // Used to allow registering plain base::Closure callbacks.
+  static void InvokeUnnamedCallback(const base::Closure& callback,
+                                    const std::string& pref_name);
+
+ private:
+  // Ordered the members to compact the class instance.
+  std::string pref_name_;
+  NamedChangeCallback observer_;
+  PrefService* prefs_;
+
+ protected:
+  bool setting_value_;
+};
+
+// This function implements StringListPrefMember::UpdateValue().
+// It is exposed here for testing purposes.
+bool COMPONENTS_PREFS_EXPORT PrefMemberVectorStringUpdate(
+    const base::Value& value,
+    std::vector<std::string>* string_vector);
+
+}  // namespace subtle
+
+template <typename ValueType>
+class PrefMember : public subtle::PrefMemberBase {
+ public:
+  // Defer initialization to an Init method so it's easy to make this class be
+  // a member variable.
+  PrefMember() {}
+  virtual ~PrefMember() {}
+
+  // Do the actual initialization of the class.  Use the two-parameter
+  // version if you don't want any notifications of changes.  This
+  // method should only be called on the UI thread.
+  void Init(const std::string& pref_name,
+            PrefService* prefs,
+            const NamedChangeCallback& observer) {
+    subtle::PrefMemberBase::Init(pref_name, prefs, observer);
+  }
+  void Init(const std::string& pref_name,
+            PrefService* prefs,
+            const base::Closure& observer) {
+    subtle::PrefMemberBase::Init(
+        pref_name, prefs,
+        base::Bind(&PrefMemberBase::InvokeUnnamedCallback, observer));
+  }
+  void Init(const std::string& pref_name, PrefService* prefs) {
+    subtle::PrefMemberBase::Init(pref_name, prefs);
+  }
+
+  // Unsubscribes the PrefMember from the PrefService. After calling this
+  // function, the PrefMember may not be used any more on the UI thread.
+  // Assuming |MoveToSequence| was previously called, |GetValue|, |IsManaged|,
+  // and |IsUserModifiable| can still be called from the other sequence but
+  // the results will no longer update from the PrefService.
+  // This method should only be called on the UI thread.
+  void Destroy() {
+    subtle::PrefMemberBase::Destroy();
+  }
+
+  // Moves the PrefMember to another sequence, allowing read accesses from
+  // there. Changes from the PrefService will be propagated asynchronously
+  // via PostTask.
+  // This method should only be used from the sequence the PrefMember is
+  // currently on, which is the UI thread by default.
+  void MoveToSequence(scoped_refptr<base::SequencedTaskRunner> task_runner) {
+    subtle::PrefMemberBase::MoveToSequence(task_runner);
+  }
+
+  // Check whether the pref is managed, i.e. controlled externally through
+  // enterprise configuration management (e.g. windows group policy). Returns
+  // false for unknown prefs.
+  // This method should only be used from the sequence the PrefMember is
+  // currently on, which is the UI thread unless changed by |MoveToSequence|.
+  bool IsManaged() const {
+    VerifyPref();
+    return internal_->IsManaged();
+  }
+
+  // Checks whether the pref can be modified by the user. This returns false
+  // when the pref is managed by a policy or an extension, and when a command
+  // line flag overrides the pref.
+  // This method should only be used from the sequence the PrefMember is
+  // currently on, which is the UI thread unless changed by |MoveToSequence|.
+  bool IsUserModifiable() const {
+    VerifyPref();
+    return internal_->IsUserModifiable();
+  }
+
+  // Retrieve the value of the member variable.
+  // This method should only be used from the sequence the PrefMember is
+  // currently on, which is the UI thread unless changed by |MoveToSequence|.
+  ValueType GetValue() const {
+    VerifyPref();
+    return internal_->value();
+  }
+
+  // Provided as a convenience.
+  ValueType operator*() const {
+    return GetValue();
+  }
+
+  // Set the value of the member variable.
+  // This method should only be called on the UI thread.
+  void SetValue(const ValueType& value) {
+    VerifyValuePrefName();
+    setting_value_ = true;
+    UpdatePref(value);
+    setting_value_ = false;
+  }
+
+  // Returns the pref name.
+  const std::string& GetPrefName() const {
+    return pref_name();
+  }
+
+ private:
+  class Internal : public subtle::PrefMemberBase::Internal {
+   public:
+    Internal() : value_(ValueType()) {}
+
+    ValueType value() {
+      CheckOnCorrectSequence();
+      return value_;
+    }
+
+   protected:
+    ~Internal() override {}
+
+    COMPONENTS_PREFS_EXPORT bool UpdateValueInternal(
+        const base::Value& value) const override;
+
+    // We cache the value of the pref so we don't have to keep walking the pref
+    // tree.
+    mutable ValueType value_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Internal);
+  };
+
+  Internal* internal() const override { return internal_.get(); }
+  void CreateInternal() const override { internal_ = new Internal(); }
+
+  // This method is used to do the actual sync with pref of the specified type.
+  void COMPONENTS_PREFS_EXPORT UpdatePref(const ValueType& value);
+
+  mutable scoped_refptr<Internal> internal_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefMember);
+};
+
+// Declaration of template specialization need to be repeated here
+// specifically for each specialization (rather than just once above)
+// or at least one of our compilers won't be happy in all cases.
+// Specifically, it was failing on ChromeOS with a complaint about
+// PrefMember<FilePath>::UpdateValueInternal not being defined when
+// built in a chroot with the following parameters:
+//
+// FEATURES="noclean nostrip" USE="-chrome_debug -chrome_remoting
+// -chrome_internal -chrome_pdf component_build"
+// ~/trunk/goma/goma-wrapper cros_chrome_make --board=${BOARD}
+// --install --runhooks
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<bool>::UpdatePref(const bool& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool PrefMember<bool>::Internal::UpdateValueInternal(
+    const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<int>::UpdatePref(const int& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool PrefMember<int>::Internal::UpdateValueInternal(
+    const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void
+PrefMember<double>::UpdatePref(const double& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool PrefMember<double>::Internal::UpdateValueInternal(
+    const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<std::string>::UpdatePref(
+    const std::string& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool
+PrefMember<std::string>::Internal::UpdateValueInternal(
+    const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<base::FilePath>::UpdatePref(
+    const base::FilePath& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool
+PrefMember<base::FilePath>::Internal::UpdateValueInternal(
+    const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<std::vector<std::string>>::UpdatePref(
+    const std::vector<std::string>& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool
+PrefMember<std::vector<std::string>>::Internal::UpdateValueInternal(
+    const base::Value& value) const;
+
+typedef PrefMember<bool> BooleanPrefMember;
+typedef PrefMember<int> IntegerPrefMember;
+typedef PrefMember<double> DoublePrefMember;
+typedef PrefMember<std::string> StringPrefMember;
+typedef PrefMember<base::FilePath> FilePathPrefMember;
+// This preference member is expensive for large string arrays.
+typedef PrefMember<std::vector<std::string>> StringListPrefMember;
+
+#endif  // COMPONENTS_PREFS_PREF_MEMBER_H_
diff --git a/src/updater_components/prefs/pref_member_unittest.cc b/src/updater_components/prefs/pref_member_unittest.cc
new file mode 100644
index 0000000..126b18f
--- /dev/null
+++ b/src/updater_components/prefs/pref_member_unittest.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2011 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 "components/prefs/pref_member.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/post_task.h"
+#include "base/test/task_environment.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kBoolPref[] = "bool";
+const char kIntPref[] = "int";
+const char kDoublePref[] = "double";
+const char kStringPref[] = "string";
+const char kStringListPref[] = "string_list";
+
+void RegisterTestPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(kBoolPref, false);
+  registry->RegisterIntegerPref(kIntPref, 0);
+  registry->RegisterDoublePref(kDoublePref, 0.0);
+  registry->RegisterStringPref(kStringPref, "default");
+  registry->RegisterListPref(kStringListPref);
+}
+
+class GetPrefValueHelper
+    : public base::RefCountedThreadSafe<GetPrefValueHelper> {
+ public:
+  GetPrefValueHelper()
+      : value_(false),
+        task_runner_(base::CreateSequencedTaskRunner({base::ThreadPool()})) {}
+
+  void Init(const std::string& pref_name, PrefService* prefs) {
+    pref_.Init(pref_name, prefs);
+    pref_.MoveToSequence(task_runner_);
+  }
+
+  void Destroy() {
+    pref_.Destroy();
+  }
+
+  void FetchValue() {
+    base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+    ASSERT_TRUE(task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&GetPrefValueHelper::GetPrefValue, this, &event)));
+    event.Wait();
+  }
+
+  bool value() { return value_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<GetPrefValueHelper>;
+  ~GetPrefValueHelper() {}
+
+  void GetPrefValue(base::WaitableEvent* event) {
+    value_ = pref_.GetValue();
+    event->Signal();
+  }
+
+  BooleanPrefMember pref_;
+  bool value_;
+
+  // The sequence |pref_| runs on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+};
+
+class PrefMemberTestClass {
+ public:
+  explicit PrefMemberTestClass(PrefService* prefs)
+      : observe_cnt_(0), prefs_(prefs) {
+    str_.Init(kStringPref, prefs,
+              base::Bind(&PrefMemberTestClass::OnPreferenceChanged,
+                         base::Unretained(this)));
+  }
+
+  void OnPreferenceChanged(const std::string& pref_name) {
+    EXPECT_EQ(pref_name, kStringPref);
+    EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref));
+    ++observe_cnt_;
+  }
+
+  StringPrefMember str_;
+  int observe_cnt_;
+
+ private:
+  PrefService* prefs_;
+};
+
+}  // anonymous namespace
+
+class PrefMemberTest : public testing::Test {
+  base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(PrefMemberTest, BasicGetAndSet) {
+  TestingPrefServiceSimple prefs;
+  RegisterTestPrefs(prefs.registry());
+
+  // Test bool
+  BooleanPrefMember boolean;
+  boolean.Init(kBoolPref, &prefs);
+
+  // Check the defaults
+  EXPECT_FALSE(prefs.GetBoolean(kBoolPref));
+  EXPECT_FALSE(boolean.GetValue());
+  EXPECT_FALSE(*boolean);
+
+  // Try changing through the member variable.
+  boolean.SetValue(true);
+  EXPECT_TRUE(boolean.GetValue());
+  EXPECT_TRUE(prefs.GetBoolean(kBoolPref));
+  EXPECT_TRUE(*boolean);
+
+  // Try changing back through the pref.
+  prefs.SetBoolean(kBoolPref, false);
+  EXPECT_FALSE(prefs.GetBoolean(kBoolPref));
+  EXPECT_FALSE(boolean.GetValue());
+  EXPECT_FALSE(*boolean);
+
+  // Test int
+  IntegerPrefMember integer;
+  integer.Init(kIntPref, &prefs);
+
+  // Check the defaults
+  EXPECT_EQ(0, prefs.GetInteger(kIntPref));
+  EXPECT_EQ(0, integer.GetValue());
+  EXPECT_EQ(0, *integer);
+
+  // Try changing through the member variable.
+  integer.SetValue(5);
+  EXPECT_EQ(5, integer.GetValue());
+  EXPECT_EQ(5, prefs.GetInteger(kIntPref));
+  EXPECT_EQ(5, *integer);
+
+  // Try changing back through the pref.
+  prefs.SetInteger(kIntPref, 2);
+  EXPECT_EQ(2, prefs.GetInteger(kIntPref));
+  EXPECT_EQ(2, integer.GetValue());
+  EXPECT_EQ(2, *integer);
+
+  // Test double
+  DoublePrefMember double_member;
+  double_member.Init(kDoublePref, &prefs);
+
+  // Check the defaults
+  EXPECT_EQ(0.0, prefs.GetDouble(kDoublePref));
+  EXPECT_EQ(0.0, double_member.GetValue());
+  EXPECT_EQ(0.0, *double_member);
+
+  // Try changing through the member variable.
+  double_member.SetValue(1.0);
+  EXPECT_EQ(1.0, double_member.GetValue());
+  EXPECT_EQ(1.0, prefs.GetDouble(kDoublePref));
+  EXPECT_EQ(1.0, *double_member);
+
+  // Try changing back through the pref.
+  prefs.SetDouble(kDoublePref, 3.0);
+  EXPECT_EQ(3.0, prefs.GetDouble(kDoublePref));
+  EXPECT_EQ(3.0, double_member.GetValue());
+  EXPECT_EQ(3.0, *double_member);
+
+  // Test string
+  StringPrefMember string;
+  string.Init(kStringPref, &prefs);
+
+  // Check the defaults
+  EXPECT_EQ("default", prefs.GetString(kStringPref));
+  EXPECT_EQ("default", string.GetValue());
+  EXPECT_EQ("default", *string);
+
+  // Try changing through the member variable.
+  string.SetValue("foo");
+  EXPECT_EQ("foo", string.GetValue());
+  EXPECT_EQ("foo", prefs.GetString(kStringPref));
+  EXPECT_EQ("foo", *string);
+
+  // Try changing back through the pref.
+  prefs.SetString(kStringPref, "bar");
+  EXPECT_EQ("bar", prefs.GetString(kStringPref));
+  EXPECT_EQ("bar", string.GetValue());
+  EXPECT_EQ("bar", *string);
+
+  // Test string list
+  base::ListValue expected_list;
+  std::vector<std::string> expected_vector;
+  StringListPrefMember string_list;
+  string_list.Init(kStringListPref, &prefs);
+
+  // Check the defaults
+  EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+  EXPECT_EQ(expected_vector, string_list.GetValue());
+  EXPECT_EQ(expected_vector, *string_list);
+
+  // Try changing through the pref member.
+  expected_list.AppendString("foo");
+  expected_vector.push_back("foo");
+  string_list.SetValue(expected_vector);
+
+  EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+  EXPECT_EQ(expected_vector, string_list.GetValue());
+  EXPECT_EQ(expected_vector, *string_list);
+
+  // Try adding through the pref.
+  expected_list.AppendString("bar");
+  expected_vector.push_back("bar");
+  prefs.Set(kStringListPref, expected_list);
+
+  EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+  EXPECT_EQ(expected_vector, string_list.GetValue());
+  EXPECT_EQ(expected_vector, *string_list);
+
+  // Try removing through the pref.
+  expected_list.Remove(0, nullptr);
+  expected_vector.erase(expected_vector.begin());
+  prefs.Set(kStringListPref, expected_list);
+
+  EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+  EXPECT_EQ(expected_vector, string_list.GetValue());
+  EXPECT_EQ(expected_vector, *string_list);
+}
+
+TEST_F(PrefMemberTest, InvalidList) {
+  // Set the vector to an initial good value.
+  std::vector<std::string> expected_vector;
+  expected_vector.push_back("foo");
+
+  // Try to add a valid list first.
+  base::ListValue list;
+  list.AppendString("foo");
+  std::vector<std::string> vector;
+  EXPECT_TRUE(subtle::PrefMemberVectorStringUpdate(list, &vector));
+  EXPECT_EQ(expected_vector, vector);
+
+  // Now try to add an invalid list.  |vector| should not be changed.
+  list.AppendInteger(0);
+  EXPECT_FALSE(subtle::PrefMemberVectorStringUpdate(list, &vector));
+  EXPECT_EQ(expected_vector, vector);
+}
+
+TEST_F(PrefMemberTest, TwoPrefs) {
+  // Make sure two DoublePrefMembers stay in sync.
+  TestingPrefServiceSimple prefs;
+  RegisterTestPrefs(prefs.registry());
+
+  DoublePrefMember pref1;
+  pref1.Init(kDoublePref, &prefs);
+  DoublePrefMember pref2;
+  pref2.Init(kDoublePref, &prefs);
+
+  pref1.SetValue(2.3);
+  EXPECT_EQ(2.3, *pref2);
+
+  pref2.SetValue(3.5);
+  EXPECT_EQ(3.5, *pref1);
+
+  prefs.SetDouble(kDoublePref, 4.2);
+  EXPECT_EQ(4.2, *pref1);
+  EXPECT_EQ(4.2, *pref2);
+}
+
+TEST_F(PrefMemberTest, Observer) {
+  TestingPrefServiceSimple prefs;
+  RegisterTestPrefs(prefs.registry());
+
+  PrefMemberTestClass test_obj(&prefs);
+  EXPECT_EQ("default", *test_obj.str_);
+
+  // Calling SetValue should not fire the observer.
+  test_obj.str_.SetValue("hello");
+  EXPECT_EQ(0, test_obj.observe_cnt_);
+  EXPECT_EQ("hello", prefs.GetString(kStringPref));
+
+  // Changing the pref does fire the observer.
+  prefs.SetString(kStringPref, "world");
+  EXPECT_EQ(1, test_obj.observe_cnt_);
+  EXPECT_EQ("world", *(test_obj.str_));
+
+  // Not changing the value should not fire the observer.
+  prefs.SetString(kStringPref, "world");
+  EXPECT_EQ(1, test_obj.observe_cnt_);
+  EXPECT_EQ("world", *(test_obj.str_));
+
+  prefs.SetString(kStringPref, "hello");
+  EXPECT_EQ(2, test_obj.observe_cnt_);
+  EXPECT_EQ("hello", prefs.GetString(kStringPref));
+}
+
+TEST_F(PrefMemberTest, NoInit) {
+  // Make sure not calling Init on a PrefMember doesn't cause problems.
+  IntegerPrefMember pref;
+}
+
+TEST_F(PrefMemberTest, MoveToSequence) {
+  TestingPrefServiceSimple prefs;
+  scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper());
+  RegisterTestPrefs(prefs.registry());
+  helper->Init(kBoolPref, &prefs);
+
+  helper->FetchValue();
+  EXPECT_FALSE(helper->value());
+
+  prefs.SetBoolean(kBoolPref, true);
+
+  helper->FetchValue();
+  EXPECT_TRUE(helper->value());
+
+  helper->Destroy();
+
+  helper->FetchValue();
+  EXPECT_TRUE(helper->value());
+}
diff --git a/src/updater_components/prefs/pref_notifier.h b/src/updater_components/prefs/pref_notifier.h
new file mode 100644
index 0000000..2abc213
--- /dev/null
+++ b/src/updater_components/prefs/pref_notifier.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_NOTIFIER_H_
+#define COMPONENTS_PREFS_PREF_NOTIFIER_H_
+
+#include <string>
+
+// Delegate interface used by PrefValueStore to notify its owner about changes
+// to the preference values.
+// TODO(mnissler, danno): Move this declaration to pref_value_store.h once we've
+// cleaned up all public uses of this interface.
+class PrefNotifier {
+ public:
+  virtual ~PrefNotifier() {}
+
+  // Sends out a change notification for the preference identified by
+  // |pref_name|.
+  virtual void OnPreferenceChanged(const std::string& pref_name) = 0;
+
+  // Broadcasts the intialization completed notification.
+  virtual void OnInitializationCompleted(bool succeeded) = 0;
+};
+
+#endif  // COMPONENTS_PREFS_PREF_NOTIFIER_H_
diff --git a/src/updater_components/prefs/pref_notifier_impl.cc b/src/updater_components/prefs/pref_notifier_impl.cc
new file mode 100644
index 0000000..9c753e7
--- /dev/null
+++ b/src/updater_components/prefs/pref_notifier_impl.cc
@@ -0,0 +1,150 @@
+// 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 "components/prefs/pref_notifier_impl.h"
+
+#include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/strcat.h"
+#include "components/prefs/pref_service.h"
+
+PrefNotifierImpl::PrefNotifierImpl() : pref_service_(nullptr) {}
+
+PrefNotifierImpl::PrefNotifierImpl(PrefService* service)
+    : pref_service_(service) {
+}
+
+PrefNotifierImpl::~PrefNotifierImpl() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Verify that there are no pref observers when we shut down.
+  for (const auto& observer_list : pref_observers_) {
+    if (observer_list.second->begin() != observer_list.second->end()) {
+      // Generally, there should not be any subscribers left when the profile
+      // is destroyed because a) those may indicate that the subscriber class
+      // maintains an active pointer to the profile that might be used for
+      // accessing a destroyed profile and b) those subscribers will try to
+      // unsubscribe from a PrefService that has been destroyed with the
+      // profile.
+      // There is one exception that is safe: Static objects that are leaked
+      // on process termination, if these objects just subscribe to preferences
+      // and never access the profile after destruction. As these objects are
+      // leaked on termination, it is guaranteed that they don't attempt to
+      // unsubscribe.
+      const auto& pref_name = observer_list.first;
+      std::string message = base::StrCat(
+          {"Pref observer for ", pref_name, " found at shutdown."});
+      LOG(WARNING) << message;
+      DEBUG_ALIAS_FOR_CSTR(aliased_message, message.c_str(), 128);
+
+      // TODO(crbug.com/942491, 946668, 945772) The following code collects
+      // stacktraces that show how the profile is destroyed that owns
+      // preferences which are known to have subscriptions outliving the
+      // profile.
+      if (
+          // For GlobalMenuBarX11, crbug.com/946668
+          pref_name == "bookmark_bar.show_on_all_tabs" ||
+          // For BrowserWindowPropertyManager, crbug.com/942491
+          pref_name == "profile.icon_version" ||
+          // For BrowserWindowDefaultTouchBar, crbug.com/945772
+          pref_name == "default_search_provider_data.template_url_data") {
+        base::debug::DumpWithoutCrashing();
+      }
+    }
+  }
+
+  // Same for initialization observers.
+  if (!init_observers_.empty())
+    LOG(WARNING) << "Init observer found at shutdown.";
+
+  pref_observers_.clear();
+  init_observers_.clear();
+}
+
+void PrefNotifierImpl::AddPrefObserver(const std::string& path,
+                                       PrefObserver* obs) {
+  // Get the pref observer list associated with the path.
+  PrefObserverList* observer_list = nullptr;
+  auto observer_iterator = pref_observers_.find(path);
+  if (observer_iterator == pref_observers_.end()) {
+    observer_list = new PrefObserverList;
+    pref_observers_[path] = base::WrapUnique(observer_list);
+  } else {
+    observer_list = observer_iterator->second.get();
+  }
+
+  // Add the pref observer. ObserverList will DCHECK if it already is
+  // in the list.
+  observer_list->AddObserver(obs);
+}
+
+void PrefNotifierImpl::RemovePrefObserver(const std::string& path,
+                                          PrefObserver* obs) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto observer_iterator = pref_observers_.find(path);
+  if (observer_iterator == pref_observers_.end()) {
+    return;
+  }
+
+  PrefObserverList* observer_list = observer_iterator->second.get();
+  observer_list->RemoveObserver(obs);
+}
+
+void PrefNotifierImpl::AddPrefObserverAllPrefs(PrefObserver* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  all_prefs_pref_observers_.AddObserver(observer);
+}
+
+void PrefNotifierImpl::RemovePrefObserverAllPrefs(PrefObserver* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  all_prefs_pref_observers_.RemoveObserver(observer);
+}
+
+void PrefNotifierImpl::AddInitObserver(base::OnceCallback<void(bool)> obs) {
+  init_observers_.push_back(std::move(obs));
+}
+
+void PrefNotifierImpl::OnPreferenceChanged(const std::string& path) {
+  FireObservers(path);
+}
+
+void PrefNotifierImpl::OnInitializationCompleted(bool succeeded) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // We must move init_observers_ to a local variable before we run
+  // observers, or we can end up in this method re-entrantly before
+  // clearing the observers list.
+  PrefInitObserverList observers;
+  std::swap(observers, init_observers_);
+
+  for (auto& observer : observers)
+    std::move(observer).Run(succeeded);
+}
+
+void PrefNotifierImpl::FireObservers(const std::string& path) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Only send notifications for registered preferences.
+  if (!pref_service_->FindPreference(path))
+    return;
+
+  // Fire observers for any preference change.
+  for (auto& observer : all_prefs_pref_observers_)
+    observer.OnPreferenceChanged(pref_service_, path);
+
+  auto observer_iterator = pref_observers_.find(path);
+  if (observer_iterator == pref_observers_.end())
+    return;
+
+  for (PrefObserver& observer : *(observer_iterator->second))
+    observer.OnPreferenceChanged(pref_service_, path);
+}
+
+void PrefNotifierImpl::SetPrefService(PrefService* pref_service) {
+  DCHECK(pref_service_ == nullptr);
+  pref_service_ = pref_service;
+}
diff --git a/src/updater_components/prefs/pref_notifier_impl.h b/src/updater_components/prefs/pref_notifier_impl.h
new file mode 100644
index 0000000..c550103
--- /dev/null
+++ b/src/updater_components/prefs/pref_notifier_impl.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_
+#define COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_
+
+#include <list>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/pref_observer.h"
+#include "components/prefs/prefs_export.h"
+
+class PrefService;
+
+// The PrefNotifier implementation used by the PrefService.
+class COMPONENTS_PREFS_EXPORT PrefNotifierImpl : public PrefNotifier {
+ public:
+  PrefNotifierImpl();
+  explicit PrefNotifierImpl(PrefService* pref_service);
+  ~PrefNotifierImpl() override;
+
+  // If the pref at the given path changes, we call the observer's
+  // OnPreferenceChanged method.
+  void AddPrefObserver(const std::string& path, PrefObserver* observer);
+  void RemovePrefObserver(const std::string& path, PrefObserver* observer);
+
+  // These observers are called for any pref changes.
+  //
+  // AVOID ADDING THESE. See the long comment in the identically-named
+  // functions on PrefService for background.
+  void AddPrefObserverAllPrefs(PrefObserver* observer);
+  void RemovePrefObserverAllPrefs(PrefObserver* observer);
+
+  // We run the callback once, when initialization completes. The bool
+  // parameter will be set to true for successful initialization,
+  // false for unsuccessful.
+  void AddInitObserver(base::OnceCallback<void(bool)> observer);
+
+  void SetPrefService(PrefService* pref_service);
+
+  // PrefNotifier overrides.
+  void OnPreferenceChanged(const std::string& pref_name) override;
+
+ protected:
+  // PrefNotifier overrides.
+  void OnInitializationCompleted(bool succeeded) override;
+
+  // A map from pref names to a list of observers. Observers get fired in the
+  // order they are added. These should only be accessed externally for unit
+  // testing.
+  typedef base::ObserverList<PrefObserver>::Unchecked PrefObserverList;
+  typedef std::unordered_map<std::string, std::unique_ptr<PrefObserverList>>
+      PrefObserverMap;
+
+  typedef std::list<base::OnceCallback<void(bool)>> PrefInitObserverList;
+
+  const PrefObserverMap* pref_observers() const { return &pref_observers_; }
+
+ private:
+  // For the given pref_name, fire any observer of the pref. Virtual so it can
+  // be mocked for unit testing.
+  virtual void FireObservers(const std::string& path);
+
+  // Weak reference; the notifier is owned by the PrefService.
+  PrefService* pref_service_;
+
+  PrefObserverMap pref_observers_;
+  PrefInitObserverList init_observers_;
+
+  // Observers for changes to any preference.
+  PrefObserverList all_prefs_pref_observers_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefNotifierImpl);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_
diff --git a/src/updater_components/prefs/pref_notifier_impl_unittest.cc b/src/updater_components/prefs/pref_notifier_impl_unittest.cc
new file mode 100644
index 0000000..ee42891
--- /dev/null
+++ b/src/updater_components/prefs/pref_notifier_impl_unittest.cc
@@ -0,0 +1,218 @@
+// Copyright (c) 2011 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 "base/bind.h"
+#include "base/callback.h"
+#include "components/prefs/mock_pref_change_callback.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_observer.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_value_store.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Field;
+using testing::Invoke;
+using testing::Mock;
+using testing::Truly;
+
+namespace {
+
+const char kChangedPref[] = "changed_pref";
+const char kUnchangedPref[] = "unchanged_pref";
+
+class MockPrefInitObserver {
+ public:
+  MOCK_METHOD1(OnInitializationCompleted, void(bool));
+};
+
+// This is an unmodified PrefNotifierImpl, except we make
+// OnPreferenceChanged public for tests.
+class TestingPrefNotifierImpl : public PrefNotifierImpl {
+ public:
+  explicit TestingPrefNotifierImpl(PrefService* service)
+      : PrefNotifierImpl(service) {
+  }
+
+  // Make public for tests.
+  using PrefNotifierImpl::OnPreferenceChanged;
+};
+
+// Mock PrefNotifier that allows tracking of observers and notifications.
+class MockPrefNotifier : public PrefNotifierImpl {
+ public:
+  explicit MockPrefNotifier(PrefService* pref_service)
+      : PrefNotifierImpl(pref_service) {}
+  ~MockPrefNotifier() override {}
+
+  MOCK_METHOD1(FireObservers, void(const std::string& path));
+
+  size_t CountObserver(const std::string& path, PrefObserver* obs) {
+    auto observer_iterator = pref_observers()->find(path);
+    if (observer_iterator == pref_observers()->end())
+      return false;
+
+    size_t count = 0;
+    for (auto& existing_obs : *observer_iterator->second) {
+      if (&existing_obs == obs)
+        count++;
+    }
+
+    return count;
+  }
+
+  // Make public for tests below.
+  using PrefNotifierImpl::OnPreferenceChanged;
+  using PrefNotifierImpl::OnInitializationCompleted;
+};
+
+class PrefObserverMock : public PrefObserver {
+ public:
+  PrefObserverMock() {}
+  virtual ~PrefObserverMock() {}
+
+  MOCK_METHOD2(OnPreferenceChanged, void(PrefService*, const std::string&));
+};
+
+// Test fixture class.
+class PrefNotifierTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    pref_service_.registry()->RegisterBooleanPref(kChangedPref, true);
+    pref_service_.registry()->RegisterBooleanPref(kUnchangedPref, true);
+  }
+
+  TestingPrefServiceSimple pref_service_;
+
+  PrefObserverMock obs1_;
+  PrefObserverMock obs2_;
+};
+
+TEST_F(PrefNotifierTest, OnPreferenceChanged) {
+  MockPrefNotifier notifier(&pref_service_);
+  EXPECT_CALL(notifier, FireObservers(kChangedPref)).Times(1);
+  notifier.OnPreferenceChanged(kChangedPref);
+}
+
+TEST_F(PrefNotifierTest, OnInitializationCompleted) {
+  MockPrefNotifier notifier(&pref_service_);
+  MockPrefInitObserver observer;
+  notifier.AddInitObserver(
+      base::BindOnce(&MockPrefInitObserver::OnInitializationCompleted,
+                     base::Unretained(&observer)));
+  EXPECT_CALL(observer, OnInitializationCompleted(true));
+  notifier.OnInitializationCompleted(true);
+}
+
+TEST_F(PrefNotifierTest, AddAndRemovePrefObservers) {
+  const char pref_name[] = "homepage";
+  const char pref_name2[] = "proxy";
+
+  MockPrefNotifier notifier(&pref_service_);
+  notifier.AddPrefObserver(pref_name, &obs1_);
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+  // Re-adding the same observer for the same pref doesn't change anything.
+  // Skip this in debug mode, since it hits a DCHECK and death tests aren't
+  // thread-safe.
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+  notifier.AddPrefObserver(pref_name, &obs1_);
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+#endif
+
+  // Ensure that we can add the same observer to a different pref.
+  notifier.AddPrefObserver(pref_name2, &obs1_);
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+  // Ensure that we can add another observer to the same pref.
+  notifier.AddPrefObserver(pref_name, &obs2_);
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+  // Ensure that we can remove all observers, and that removing a non-existent
+  // observer is harmless.
+  notifier.RemovePrefObserver(pref_name, &obs1_);
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+  notifier.RemovePrefObserver(pref_name, &obs2_);
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+  notifier.RemovePrefObserver(pref_name, &obs1_);
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+  notifier.RemovePrefObserver(pref_name2, &obs1_);
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+}
+
+TEST_F(PrefNotifierTest, FireObservers) {
+  TestingPrefNotifierImpl notifier(&pref_service_);
+  notifier.AddPrefObserver(kChangedPref, &obs1_);
+  notifier.AddPrefObserver(kUnchangedPref, &obs1_);
+
+  EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref));
+  EXPECT_CALL(obs2_, OnPreferenceChanged(_, _)).Times(0);
+  notifier.OnPreferenceChanged(kChangedPref);
+  Mock::VerifyAndClearExpectations(&obs1_);
+  Mock::VerifyAndClearExpectations(&obs2_);
+
+  notifier.AddPrefObserver(kChangedPref, &obs2_);
+  notifier.AddPrefObserver(kUnchangedPref, &obs2_);
+
+  EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref));
+  EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref));
+  notifier.OnPreferenceChanged(kChangedPref);
+  Mock::VerifyAndClearExpectations(&obs1_);
+  Mock::VerifyAndClearExpectations(&obs2_);
+
+  // Make sure removing an observer from one pref doesn't affect anything else.
+  notifier.RemovePrefObserver(kChangedPref, &obs1_);
+
+  EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0);
+  EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref));
+  notifier.OnPreferenceChanged(kChangedPref);
+  Mock::VerifyAndClearExpectations(&obs1_);
+  Mock::VerifyAndClearExpectations(&obs2_);
+
+  // Make sure removing an observer entirely doesn't affect anything else.
+  notifier.RemovePrefObserver(kUnchangedPref, &obs1_);
+
+  EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0);
+  EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref));
+  notifier.OnPreferenceChanged(kChangedPref);
+  Mock::VerifyAndClearExpectations(&obs1_);
+  Mock::VerifyAndClearExpectations(&obs2_);
+
+  notifier.RemovePrefObserver(kChangedPref, &obs2_);
+  notifier.RemovePrefObserver(kUnchangedPref, &obs2_);
+}
+
+}  // namespace
diff --git a/src/updater_components/prefs/pref_observer.h b/src/updater_components/prefs/pref_observer.h
new file mode 100644
index 0000000..3f48931
--- /dev/null
+++ b/src/updater_components/prefs/pref_observer.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_PREF_OBSERVER_H_
+#define COMPONENTS_PREFS_PREF_OBSERVER_H_
+
+#include <string>
+
+class PrefService;
+
+// Used internally to the Prefs subsystem to pass preference change
+// notifications between PrefService, PrefNotifierImpl and
+// PrefChangeRegistrar.
+class PrefObserver {
+ public:
+  virtual void OnPreferenceChanged(PrefService* service,
+                                   const std::string& pref_name) = 0;
+};
+
+#endif  // COMPONENTS_PREFS_PREF_OBSERVER_H_
diff --git a/src/updater_components/prefs/pref_registry.cc b/src/updater_components/prefs/pref_registry.cc
new file mode 100644
index 0000000..6c6aab7
--- /dev/null
+++ b/src/updater_components/prefs/pref_registry.cc
@@ -0,0 +1,83 @@
+// 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 "components/prefs/pref_registry.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/pref_store.h"
+
+PrefRegistry::PrefRegistry()
+    : defaults_(base::MakeRefCounted<DefaultPrefStore>()) {}
+
+PrefRegistry::~PrefRegistry() {
+}
+
+uint32_t PrefRegistry::GetRegistrationFlags(
+    const std::string& pref_name) const {
+  const auto& it = registration_flags_.find(pref_name);
+  return it != registration_flags_.end() ? it->second : NO_REGISTRATION_FLAGS;
+}
+
+scoped_refptr<PrefStore> PrefRegistry::defaults() {
+  return defaults_.get();
+}
+
+PrefRegistry::const_iterator PrefRegistry::begin() const {
+  return defaults_->begin();
+}
+
+PrefRegistry::const_iterator PrefRegistry::end() const {
+  return defaults_->end();
+}
+
+void PrefRegistry::SetDefaultPrefValue(const std::string& pref_name,
+                                       base::Value value) {
+  const base::Value* current_value = nullptr;
+  DCHECK(defaults_->GetValue(pref_name, &current_value))
+      << "Setting default for unregistered pref: " << pref_name;
+  DCHECK(value.type() == current_value->type())
+      << "Wrong type for new default: " << pref_name;
+
+  defaults_->ReplaceDefaultValue(pref_name, std::move(value));
+}
+
+void PrefRegistry::SetDefaultForeignPrefValue(const std::string& path,
+                                              base::Value default_value,
+                                              uint32_t flags) {
+  auto erased = foreign_pref_keys_.erase(path);
+  DCHECK_EQ(1u, erased);
+  RegisterPreference(path, std::move(default_value), flags);
+}
+
+void PrefRegistry::RegisterPreference(const std::string& path,
+                                      base::Value default_value,
+                                      uint32_t flags) {
+  base::Value::Type orig_type = default_value.type();
+  DCHECK(orig_type != base::Value::Type::NONE &&
+         orig_type != base::Value::Type::BINARY) <<
+         "invalid preference type: " << orig_type;
+  DCHECK(!defaults_->GetValue(path, nullptr))
+      << "Trying to register a previously registered pref: " << path;
+  DCHECK(!base::Contains(registration_flags_, path))
+      << "Trying to register a previously registered pref: " << path;
+
+  defaults_->SetDefaultValue(path, std::move(default_value));
+  if (flags != NO_REGISTRATION_FLAGS)
+    registration_flags_[path] = flags;
+
+  OnPrefRegistered(path, flags);
+}
+
+void PrefRegistry::RegisterForeignPref(const std::string& path) {
+  bool inserted = foreign_pref_keys_.insert(path).second;
+  DCHECK(inserted);
+}
+
+void PrefRegistry::OnPrefRegistered(const std::string& path,
+                                    uint32_t flags) {}
diff --git a/src/updater_components/prefs/pref_registry.h b/src/updater_components/prefs/pref_registry.h
new file mode 100644
index 0000000..065bd65
--- /dev/null
+++ b/src/updater_components/prefs/pref_registry.h
@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_PREF_REGISTRY_H_
+#define COMPONENTS_PREFS_PREF_REGISTRY_H_
+
+#include <stdint.h>
+
+#include <set>
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/pref_value_map.h"
+#include "components/prefs/prefs_export.h"
+
+namespace base {
+class Value;
+}
+
+class DefaultPrefStore;
+class PrefStore;
+
+// Preferences need to be registered with a type and default value
+// before they are used.
+//
+// The way you use a PrefRegistry is that you register all required
+// preferences on it (via one of its subclasses), then pass it as a
+// construction parameter to PrefService.
+//
+// Currently, registrations after constructing the PrefService will
+// also work, but this is being deprecated.
+class COMPONENTS_PREFS_EXPORT PrefRegistry
+    : public base::RefCounted<PrefRegistry> {
+ public:
+  // Registration flags that can be specified which impact how the pref will
+  // behave or be stored. This will be passed in a bitmask when the pref is
+  // registered. Subclasses of PrefRegistry can specify their own flags. Care
+  // must be taken to ensure none of these overlap with the flags below.
+  enum PrefRegistrationFlags : uint32_t {
+    // No flags are specified.
+    NO_REGISTRATION_FLAGS = 0,
+
+    // The first 8 bits are reserved for subclasses of PrefRegistry to use.
+
+    // This marks the pref as "lossy". There is no strict time guarantee on when
+    // a lossy pref will be persisted to permanent storage when it is modified.
+    LOSSY_PREF = 1 << 8,
+
+    // Registering a pref as public allows other services to access it.
+    PUBLIC = 1 << 9,
+  };
+
+  typedef PrefValueMap::const_iterator const_iterator;
+  typedef std::unordered_map<std::string, uint32_t> PrefRegistrationFlagsMap;
+
+  PrefRegistry();
+
+  // Retrieve the set of registration flags for the given preference. The return
+  // value is a bitmask of PrefRegistrationFlags.
+  uint32_t GetRegistrationFlags(const std::string& pref_name) const;
+
+  // Gets the registered defaults.
+  scoped_refptr<PrefStore> defaults();
+
+  // Allows iteration over defaults.
+  const_iterator begin() const;
+  const_iterator end() const;
+
+  // Changes the default value for a preference.
+  //
+  // |pref_name| must be a previously registered preference.
+  void SetDefaultPrefValue(const std::string& pref_name, base::Value value);
+
+  // Registers a pref owned by another service for use with the current service.
+  // The owning service must register that pref with the |PUBLIC| flag.
+  void RegisterForeignPref(const std::string& path);
+
+  // Sets the default value and flags of a previously-registered foreign pref
+  // value.
+  void SetDefaultForeignPrefValue(const std::string& path,
+                                  base::Value default_value,
+                                  uint32_t flags);
+
+  const std::set<std::string>& foreign_pref_keys() const {
+    return foreign_pref_keys_;
+  }
+
+ protected:
+  friend class base::RefCounted<PrefRegistry>;
+  virtual ~PrefRegistry();
+
+  // Used by subclasses to register a default value and registration flags for
+  // a preference. |flags| is a bitmask of |PrefRegistrationFlags|.
+  void RegisterPreference(const std::string& path,
+                          base::Value default_value,
+                          uint32_t flags);
+
+  // Allows subclasses to hook into pref registration.
+  virtual void OnPrefRegistered(const std::string& path,
+                                uint32_t flags);
+
+  scoped_refptr<DefaultPrefStore> defaults_;
+
+  // A map of pref name to a bitmask of PrefRegistrationFlags.
+  PrefRegistrationFlagsMap registration_flags_;
+
+  std::set<std::string> foreign_pref_keys_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PrefRegistry);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_REGISTRY_H_
diff --git a/src/updater_components/prefs/pref_registry_simple.cc b/src/updater_components/prefs/pref_registry_simple.cc
new file mode 100644
index 0000000..bab05dd
--- /dev/null
+++ b/src/updater_components/prefs/pref_registry_simple.cc
@@ -0,0 +1,94 @@
+// 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 "components/prefs/pref_registry_simple.h"
+
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+
+PrefRegistrySimple::PrefRegistrySimple() = default;
+PrefRegistrySimple::~PrefRegistrySimple() = default;
+
+void PrefRegistrySimple::RegisterBooleanPref(const std::string& path,
+                                             bool default_value,
+                                             uint32_t flags) {
+  RegisterPreference(path, base::Value(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterIntegerPref(const std::string& path,
+                                             int default_value,
+                                             uint32_t flags) {
+  RegisterPreference(path, base::Value(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterDoublePref(const std::string& path,
+                                            double default_value,
+                                            uint32_t flags) {
+  RegisterPreference(path, base::Value(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterStringPref(const std::string& path,
+                                            const std::string& default_value,
+                                            uint32_t flags) {
+  RegisterPreference(path, base::Value(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterFilePathPref(
+    const std::string& path,
+    const base::FilePath& default_value,
+    uint32_t flags) {
+  RegisterPreference(path, base::Value(default_value.value()), flags);
+}
+
+void PrefRegistrySimple::RegisterListPref(const std::string& path,
+                                          uint32_t flags) {
+  RegisterPreference(path, base::Value(base::Value::Type::LIST), flags);
+}
+
+void PrefRegistrySimple::RegisterListPref(const std::string& path,
+                                          base::Value default_value,
+                                          uint32_t flags) {
+  RegisterPreference(path, std::move(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
+                                                uint32_t flags) {
+  RegisterPreference(path, base::Value(base::Value::Type::DICTIONARY), flags);
+}
+
+void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
+                                                base::Value default_value,
+                                                uint32_t flags) {
+  RegisterPreference(path, std::move(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterInt64Pref(const std::string& path,
+                                           int64_t default_value,
+                                           uint32_t flags) {
+  RegisterPreference(path, base::Value(base::NumberToString(default_value)),
+                     flags);
+}
+
+void PrefRegistrySimple::RegisterUint64Pref(const std::string& path,
+                                            uint64_t default_value,
+                                            uint32_t flags) {
+  RegisterPreference(path, base::Value(base::NumberToString(default_value)),
+                     flags);
+}
+
+void PrefRegistrySimple::RegisterTimePref(const std::string& path,
+                                          base::Time default_value,
+                                          uint32_t flags) {
+  RegisterInt64Pref(
+      path, default_value.ToDeltaSinceWindowsEpoch().InMicroseconds(), flags);
+}
+
+void PrefRegistrySimple::RegisterTimeDeltaPref(const std::string& path,
+                                               base::TimeDelta default_value,
+                                               uint32_t flags) {
+  RegisterInt64Pref(path, default_value.InMicroseconds(), flags);
+}
diff --git a/src/updater_components/prefs/pref_registry_simple.h b/src/updater_components/prefs/pref_registry_simple.h
new file mode 100644
index 0000000..b167e94
--- /dev/null
+++ b/src/updater_components/prefs/pref_registry_simple.h
@@ -0,0 +1,87 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_
+#define COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/prefs_export.h"
+
+namespace base {
+class Value;
+class FilePath;
+}
+
+// A simple implementation of PrefRegistry.
+class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry {
+ public:
+  PrefRegistrySimple();
+
+  // For each of these registration methods, |flags| is an optional bitmask of
+  // PrefRegistrationFlags.
+  void RegisterBooleanPref(const std::string& path,
+                           bool default_value,
+                           uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterIntegerPref(const std::string& path,
+                           int default_value,
+                           uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterDoublePref(const std::string& path,
+                          double default_value,
+                          uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterStringPref(const std::string& path,
+                          const std::string& default_value,
+                          uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterFilePathPref(const std::string& path,
+                            const base::FilePath& default_value,
+                            uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterListPref(const std::string& path,
+                        uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterListPref(const std::string& path,
+                        base::Value default_value,
+                        uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterDictionaryPref(const std::string& path,
+                              uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterDictionaryPref(const std::string& path,
+                              base::Value default_value,
+                              uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterInt64Pref(const std::string& path,
+                         int64_t default_value,
+                         uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterUint64Pref(const std::string& path,
+                          uint64_t default_value,
+                          uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterTimePref(const std::string& path,
+                        base::Time default_value,
+                        uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  void RegisterTimeDeltaPref(const std::string& path,
+                             base::TimeDelta default_value,
+                             uint32_t flags = NO_REGISTRATION_FLAGS);
+
+ protected:
+  ~PrefRegistrySimple() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PrefRegistrySimple);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_
diff --git a/src/updater_components/prefs/pref_service.cc b/src/updater_components/prefs/pref_service.cc
new file mode 100644
index 0000000..f0796fe
--- /dev/null
+++ b/src/updater_components/prefs/pref_service.cc
@@ -0,0 +1,732 @@
+// 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 "components/prefs/pref_service.h"
+
+#include <algorithm>
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/util/values/values_util.h"
+#include "base/value_conversions.h"
+#include "build/build_config.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_registry.h"
+
+namespace {
+
+class ReadErrorHandler : public PersistentPrefStore::ReadErrorDelegate {
+ public:
+  using ErrorCallback =
+      base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>;
+  explicit ReadErrorHandler(ErrorCallback cb) : callback_(cb) {}
+
+  void OnError(PersistentPrefStore::PrefReadError error) override {
+    callback_.Run(error);
+  }
+
+ private:
+  ErrorCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReadErrorHandler);
+};
+
+// Returns the WriteablePrefStore::PrefWriteFlags for the pref with the given
+// |path|.
+uint32_t GetWriteFlags(const PrefService::Preference* pref) {
+  uint32_t write_flags = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS;
+
+  if (!pref)
+    return write_flags;
+
+  if (pref->registration_flags() & PrefRegistry::LOSSY_PREF)
+    write_flags |= WriteablePrefStore::LOSSY_PREF_WRITE_FLAG;
+  return write_flags;
+}
+
+// For prefs names in |pref_store| that are not presented in |pref_changed_map|,
+// check if their values differ from those in pref_service->FindPreference() and
+// add the result into |pref_changed_map|.
+void CheckForNewPrefChangesInPrefStore(
+    std::map<std::string, bool>* pref_changed_map,
+    PrefStore* pref_store,
+    PrefService* pref_service) {
+  if (!pref_store)
+    return;
+  auto values = pref_store->GetValues();
+  for (const auto& item : values->DictItems()) {
+    // If the key already presents, skip it as a store with higher precedence
+    // already sets the entry.
+    if (pref_changed_map->find(item.first) != pref_changed_map->end())
+      continue;
+    const PrefService::Preference* pref =
+        pref_service->FindPreference(item.first);
+    if (!pref)
+      continue;
+    pref_changed_map->emplace(item.first, *(pref->GetValue()) != item.second);
+  }
+}
+
+}  // namespace
+
+PrefService::PrefService(
+    std::unique_ptr<PrefNotifierImpl> pref_notifier,
+    std::unique_ptr<PrefValueStore> pref_value_store,
+    scoped_refptr<PersistentPrefStore> user_prefs,
+    scoped_refptr<PrefRegistry> pref_registry,
+    base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
+        read_error_callback,
+    bool async)
+    : pref_notifier_(std::move(pref_notifier)),
+      pref_value_store_(std::move(pref_value_store)),
+      user_pref_store_(std::move(user_prefs)),
+      read_error_callback_(std::move(read_error_callback)),
+      pref_registry_(std::move(pref_registry)) {
+  pref_notifier_->SetPrefService(this);
+
+  DCHECK(pref_registry_);
+  DCHECK(pref_value_store_);
+
+  InitFromStorage(async);
+}
+
+PrefService::~PrefService() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(crbug.com/942491, 946668, 945772) The following code collects
+  // augments stack dumps created by ~PrefNotifierImpl() with information
+  // whether the profile owning the PrefService is an incognito profile.
+  // Delete this, once the bugs are closed.
+  const bool is_incognito_profile = user_pref_store_->IsInMemoryPrefStore();
+  base::debug::Alias(&is_incognito_profile);
+  // Export value of is_incognito_profile to a string so that `grep`
+  // is a sufficient tool to analyze crashdumps.
+  char is_incognito_profile_string[32];
+  strncpy(is_incognito_profile_string,
+          is_incognito_profile ? "is_incognito: yes" : "is_incognito: no",
+          sizeof(is_incognito_profile_string));
+  base::debug::Alias(&is_incognito_profile_string);
+}
+
+void PrefService::InitFromStorage(bool async) {
+  if (user_pref_store_->IsInitializationComplete()) {
+    read_error_callback_.Run(user_pref_store_->GetReadError());
+  } else if (!async) {
+    read_error_callback_.Run(user_pref_store_->ReadPrefs());
+  } else {
+    // Guarantee that initialization happens after this function returned.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&PersistentPrefStore::ReadPrefsAsync, user_pref_store_,
+                       new ReadErrorHandler(read_error_callback_)));
+  }
+}
+
+void PrefService::CommitPendingWrite(
+    base::OnceClosure reply_callback,
+    base::OnceClosure synchronous_done_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  user_pref_store_->CommitPendingWrite(std::move(reply_callback),
+                                       std::move(synchronous_done_callback));
+}
+
+void PrefService::SchedulePendingLossyWrites() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  user_pref_store_->SchedulePendingLossyWrites();
+}
+
+bool PrefService::GetBoolean(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  bool result = false;
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return result;
+  bool rv = value->GetAsBoolean(&result);
+  DCHECK(rv);
+  return result;
+}
+
+int PrefService::GetInteger(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  int result = 0;
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return result;
+  bool rv = value->GetAsInteger(&result);
+  DCHECK(rv);
+  return result;
+}
+
+double PrefService::GetDouble(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  double result = 0.0;
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return result;
+  bool rv = value->GetAsDouble(&result);
+  DCHECK(rv);
+  return result;
+}
+
+std::string PrefService::GetString(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  std::string result;
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return result;
+  bool rv = value->GetAsString(&result);
+  DCHECK(rv);
+  return result;
+}
+
+base::FilePath PrefService::GetFilePath(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::FilePath result;
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return base::FilePath(result);
+  bool rv = base::GetValueAsFilePath(*value, &result);
+  DCHECK(rv);
+  return result;
+}
+
+bool PrefService::HasPrefPath(const std::string& path) const {
+  const Preference* pref = FindPreference(path);
+  return pref && !pref->IsDefaultValue();
+}
+
+void PrefService::IteratePreferenceValues(
+    base::RepeatingCallback<void(const std::string& key,
+                                 const base::Value& value)> callback) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (const auto& it : *pref_registry_)
+    callback.Run(it.first, *GetPreferenceValue(it.first));
+}
+
+std::unique_ptr<base::DictionaryValue> PrefService::GetPreferenceValues(
+    IncludeDefaults include_defaults) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::unique_ptr<base::DictionaryValue> out(new base::DictionaryValue);
+  for (const auto& it : *pref_registry_) {
+    if (include_defaults == INCLUDE_DEFAULTS) {
+      out->Set(it.first, GetPreferenceValue(it.first)->CreateDeepCopy());
+    } else {
+      const Preference* pref = FindPreference(it.first);
+      if (pref->IsDefaultValue())
+        continue;
+      out->Set(it.first, pref->GetValue()->CreateDeepCopy());
+    }
+  }
+  return out;
+}
+
+const PrefService::Preference* PrefService::FindPreference(
+    const std::string& pref_name) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto it = prefs_map_.find(pref_name);
+  if (it != prefs_map_.end())
+    return &(it->second);
+  const base::Value* default_value = nullptr;
+  if (!pref_registry_->defaults()->GetValue(pref_name, &default_value))
+    return nullptr;
+  it = prefs_map_
+           .insert(std::make_pair(
+               pref_name, Preference(this, pref_name, default_value->type())))
+           .first;
+  return &(it->second);
+}
+
+bool PrefService::ReadOnly() const {
+  return user_pref_store_->ReadOnly();
+}
+
+PrefService::PrefInitializationStatus PrefService::GetInitializationStatus()
+    const {
+  if (!user_pref_store_->IsInitializationComplete())
+    return INITIALIZATION_STATUS_WAITING;
+
+  switch (user_pref_store_->GetReadError()) {
+    case PersistentPrefStore::PREF_READ_ERROR_NONE:
+      return INITIALIZATION_STATUS_SUCCESS;
+    case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
+      return INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE;
+    default:
+      return INITIALIZATION_STATUS_ERROR;
+  }
+}
+
+PrefService::PrefInitializationStatus
+PrefService::GetAllPrefStoresInitializationStatus() const {
+  if (!pref_value_store_->IsInitializationComplete())
+    return INITIALIZATION_STATUS_WAITING;
+
+  return GetInitializationStatus();
+}
+
+bool PrefService::IsManagedPreference(const std::string& pref_name) const {
+  const Preference* pref = FindPreference(pref_name);
+  return pref && pref->IsManaged();
+}
+
+bool PrefService::IsPreferenceManagedByCustodian(
+    const std::string& pref_name) const {
+  const Preference* pref = FindPreference(pref_name);
+  return pref && pref->IsManagedByCustodian();
+}
+
+bool PrefService::IsUserModifiablePreference(
+    const std::string& pref_name) const {
+  const Preference* pref = FindPreference(pref_name);
+  return pref && pref->IsUserModifiable();
+}
+
+const base::Value* PrefService::Get(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return nullptr;
+  return value;
+}
+
+const base::DictionaryValue* PrefService::GetDictionary(
+    const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return nullptr;
+  if (value->type() != base::Value::Type::DICTIONARY) {
+    NOTREACHED();
+    return nullptr;
+  }
+  return static_cast<const base::DictionaryValue*>(value);
+}
+
+const base::Value* PrefService::GetUserPrefValue(
+    const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const Preference* pref = FindPreference(path);
+  if (!pref) {
+    NOTREACHED() << "Trying to get an unregistered pref: " << path;
+    return nullptr;
+  }
+
+  // Look for an existing preference in the user store. If it doesn't
+  // exist, return NULL.
+  base::Value* value = nullptr;
+  if (!user_pref_store_->GetMutableValue(path, &value))
+    return nullptr;
+
+  if (value->type() != pref->GetType()) {
+    NOTREACHED() << "Pref value type doesn't match registered type.";
+    return nullptr;
+  }
+
+  return value;
+}
+
+void PrefService::SetDefaultPrefValue(const std::string& path,
+                                      base::Value value) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  pref_registry_->SetDefaultPrefValue(path, std::move(value));
+}
+
+const base::Value* PrefService::GetDefaultPrefValue(
+    const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Lookup the preference in the default store.
+  const base::Value* value = nullptr;
+  bool has_value = pref_registry_->defaults()->GetValue(path, &value);
+  DCHECK(has_value) << "Default value missing for pref: " << path;
+  return value;
+}
+
+const base::ListValue* PrefService::GetList(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return nullptr;
+  if (value->type() != base::Value::Type::LIST) {
+    NOTREACHED();
+    return nullptr;
+  }
+  return static_cast<const base::ListValue*>(value);
+}
+
+void PrefService::AddPrefObserver(const std::string& path, PrefObserver* obs) {
+  pref_notifier_->AddPrefObserver(path, obs);
+}
+
+void PrefService::RemovePrefObserver(const std::string& path,
+                                     PrefObserver* obs) {
+  pref_notifier_->RemovePrefObserver(path, obs);
+}
+
+void PrefService::AddPrefInitObserver(base::OnceCallback<void(bool)> obs) {
+  pref_notifier_->AddInitObserver(std::move(obs));
+}
+
+PrefRegistry* PrefService::DeprecatedGetPrefRegistry() {
+  return pref_registry_.get();
+}
+
+void PrefService::ClearPref(const std::string& path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const Preference* pref = FindPreference(path);
+  if (!pref) {
+    NOTREACHED() << "Trying to clear an unregistered pref: " << path;
+    return;
+  }
+  user_pref_store_->RemoveValue(path, GetWriteFlags(pref));
+}
+
+void PrefService::ClearMutableValues() {
+  user_pref_store_->ClearMutableValues();
+}
+
+void PrefService::OnStoreDeletionFromDisk() {
+  user_pref_store_->OnStoreDeletionFromDisk();
+}
+
+void PrefService::ChangePrefValueStore(
+    PrefStore* managed_prefs,
+    PrefStore* supervised_user_prefs,
+    PrefStore* extension_prefs,
+    PrefStore* recommended_prefs,
+    std::unique_ptr<PrefValueStore::Delegate> delegate) {
+  // Only adding new pref stores are supported.
+  DCHECK(!pref_value_store_->HasPrefStore(PrefValueStore::MANAGED_STORE) ||
+         !managed_prefs);
+  DCHECK(
+      !pref_value_store_->HasPrefStore(PrefValueStore::SUPERVISED_USER_STORE) ||
+      !supervised_user_prefs);
+  DCHECK(!pref_value_store_->HasPrefStore(PrefValueStore::EXTENSION_STORE) ||
+         !extension_prefs);
+  DCHECK(!pref_value_store_->HasPrefStore(PrefValueStore::RECOMMENDED_STORE) ||
+         !recommended_prefs);
+
+  // If some of the stores are already initialized, check for pref value changes
+  // according to store precedence.
+  std::map<std::string, bool> pref_changed_map;
+  CheckForNewPrefChangesInPrefStore(&pref_changed_map, managed_prefs, this);
+  CheckForNewPrefChangesInPrefStore(&pref_changed_map, supervised_user_prefs,
+                                    this);
+  CheckForNewPrefChangesInPrefStore(&pref_changed_map, extension_prefs, this);
+  CheckForNewPrefChangesInPrefStore(&pref_changed_map, recommended_prefs, this);
+
+  pref_value_store_ = pref_value_store_->CloneAndSpecialize(
+      managed_prefs, supervised_user_prefs, extension_prefs,
+      nullptr /* command_line_prefs */, nullptr /* user_prefs */,
+      recommended_prefs, nullptr /* default_prefs */, pref_notifier_.get(),
+      std::move(delegate));
+
+  // Notify |pref_notifier_| on all changed values.
+  for (const auto& kv : pref_changed_map) {
+    if (kv.second)
+      pref_notifier_.get()->OnPreferenceChanged(kv.first);
+  }
+}
+
+void PrefService::AddPrefObserverAllPrefs(PrefObserver* obs) {
+  pref_notifier_->AddPrefObserverAllPrefs(obs);
+}
+
+void PrefService::RemovePrefObserverAllPrefs(PrefObserver* obs) {
+  pref_notifier_->RemovePrefObserverAllPrefs(obs);
+}
+
+void PrefService::Set(const std::string& path, const base::Value& value) {
+  SetUserPrefValue(path, value.CreateDeepCopy());
+}
+
+void PrefService::SetBoolean(const std::string& path, bool value) {
+  SetUserPrefValue(path, std::make_unique<base::Value>(value));
+}
+
+void PrefService::SetInteger(const std::string& path, int value) {
+  SetUserPrefValue(path, std::make_unique<base::Value>(value));
+}
+
+void PrefService::SetDouble(const std::string& path, double value) {
+  SetUserPrefValue(path, std::make_unique<base::Value>(value));
+}
+
+void PrefService::SetString(const std::string& path, const std::string& value) {
+  SetUserPrefValue(path, std::make_unique<base::Value>(value));
+}
+
+void PrefService::SetFilePath(const std::string& path,
+                              const base::FilePath& value) {
+  SetUserPrefValue(
+      path, base::Value::ToUniquePtrValue(base::CreateFilePathValue(value)));
+}
+
+void PrefService::SetInt64(const std::string& path, int64_t value) {
+  SetUserPrefValue(path,
+                   base::Value::ToUniquePtrValue(util::Int64ToValue(value)));
+}
+
+int64_t PrefService::GetInt64(const std::string& path) const {
+  const base::Value* value = GetPreferenceValueChecked(path);
+  base::Optional<int64_t> integer = util::ValueToInt64(value);
+  DCHECK(integer);
+  return integer.value_or(0);
+}
+
+void PrefService::SetUint64(const std::string& path, uint64_t value) {
+  SetUserPrefValue(path,
+                   std::make_unique<base::Value>(base::NumberToString(value)));
+}
+
+uint64_t PrefService::GetUint64(const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const base::Value* value = GetPreferenceValueChecked(path);
+  if (!value)
+    return 0;
+  std::string result("0");
+  bool rv = value->GetAsString(&result);
+  DCHECK(rv);
+
+  uint64_t val;
+  base::StringToUint64(result, &val);
+  return val;
+}
+
+void PrefService::SetTime(const std::string& path, base::Time value) {
+  SetUserPrefValue(path,
+                   base::Value::ToUniquePtrValue(util::TimeToValue(value)));
+}
+
+base::Time PrefService::GetTime(const std::string& path) const {
+  const base::Value* value = GetPreferenceValueChecked(path);
+  base::Optional<base::Time> time = util::ValueToTime(value);
+  DCHECK(time);
+  return time.value_or(base::Time());
+}
+
+void PrefService::SetTimeDelta(const std::string& path, base::TimeDelta value) {
+  SetUserPrefValue(
+      path, base::Value::ToUniquePtrValue(util::TimeDeltaToValue(value)));
+}
+
+base::TimeDelta PrefService::GetTimeDelta(const std::string& path) const {
+  const base::Value* value = GetPreferenceValueChecked(path);
+  base::Optional<base::TimeDelta> time_delta = util::ValueToTimeDelta(value);
+  DCHECK(time_delta);
+  return time_delta.value_or(base::TimeDelta());
+}
+
+base::Value* PrefService::GetMutableUserPref(const std::string& path,
+                                             base::Value::Type type) {
+  CHECK(type == base::Value::Type::DICTIONARY ||
+        type == base::Value::Type::LIST);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const Preference* pref = FindPreference(path);
+  if (!pref) {
+    NOTREACHED() << "Trying to get an unregistered pref: " << path;
+    return nullptr;
+  }
+  if (pref->GetType() != type) {
+    NOTREACHED() << "Wrong type for GetMutableValue: " << path;
+    return nullptr;
+  }
+
+  // Look for an existing preference in the user store. Return it in case it
+  // exists and has the correct type.
+  base::Value* value = nullptr;
+  if (user_pref_store_->GetMutableValue(path, &value) &&
+      value->type() == type) {
+    return value;
+  }
+
+  // TODO(crbug.com/859477): Remove once root cause has been found.
+  if (value && value->type() != type) {
+    DEBUG_ALIAS_FOR_CSTR(path_copy, path.c_str(), 1024);
+    base::debug::DumpWithoutCrashing();
+  }
+
+  // If no user preference of the correct type exists, clone default value.
+  const base::Value* default_value = nullptr;
+  pref_registry_->defaults()->GetValue(path, &default_value);
+  // TODO(crbug.com/859477): Revert to DCHECK once root cause has been found.
+  if (default_value->type() != type) {
+    DEBUG_ALIAS_FOR_CSTR(path_copy, path.c_str(), 1024);
+    base::debug::DumpWithoutCrashing();
+  }
+  user_pref_store_->SetValueSilently(path, default_value->CreateDeepCopy(),
+                                     GetWriteFlags(pref));
+  user_pref_store_->GetMutableValue(path, &value);
+  return value;
+}
+
+void PrefService::ReportUserPrefChanged(const std::string& key) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  user_pref_store_->ReportValueChanged(key, GetWriteFlags(FindPreference(key)));
+}
+
+void PrefService::ReportUserPrefChanged(
+    const std::string& key,
+    std::set<std::vector<std::string>> path_components) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  user_pref_store_->ReportSubValuesChanged(key, std::move(path_components),
+                                           GetWriteFlags(FindPreference(key)));
+}
+
+void PrefService::SetUserPrefValue(const std::string& path,
+                                   std::unique_ptr<base::Value> new_value) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const Preference* pref = FindPreference(path);
+  if (!pref) {
+    NOTREACHED() << "Trying to write an unregistered pref: " << path;
+    return;
+  }
+  if (pref->GetType() != new_value->type()) {
+    NOTREACHED() << "Trying to set pref " << path << " of type "
+                 << pref->GetType() << " to value of type "
+                 << new_value->type();
+    return;
+  }
+
+  user_pref_store_->SetValue(path, std::move(new_value), GetWriteFlags(pref));
+}
+
+void PrefService::UpdateCommandLinePrefStore(PrefStore* command_line_store) {
+  pref_value_store_->UpdateCommandLinePrefStore(command_line_store);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PrefService::Preference
+
+PrefService::Preference::Preference(const PrefService* service,
+                                    std::string name,
+                                    base::Value::Type type)
+    : name_(std::move(name)),
+      type_(type),
+      // Cache the registration flags at creation time to avoid multiple map
+      // lookups later.
+      registration_flags_(service->pref_registry_->GetRegistrationFlags(name_)),
+      pref_service_(service) {}
+
+const base::Value* PrefService::Preference::GetValue() const {
+  return pref_service_->GetPreferenceValueChecked(name_);
+}
+
+const base::Value* PrefService::Preference::GetRecommendedValue() const {
+  DCHECK(pref_service_->FindPreference(name_))
+      << "Must register pref before getting its value";
+
+  const base::Value* found_value = nullptr;
+  if (pref_value_store()->GetRecommendedValue(name_, type_, &found_value)) {
+    DCHECK(found_value->type() == type_);
+    return found_value;
+  }
+
+  // The pref has no recommended value.
+  return nullptr;
+}
+
+bool PrefService::Preference::IsManaged() const {
+  return pref_value_store()->PrefValueInManagedStore(name_);
+}
+
+bool PrefService::Preference::IsManagedByCustodian() const {
+  return pref_value_store()->PrefValueInSupervisedStore(name_);
+}
+
+bool PrefService::Preference::IsRecommended() const {
+  return pref_value_store()->PrefValueFromRecommendedStore(name_);
+}
+
+bool PrefService::Preference::HasExtensionSetting() const {
+  return pref_value_store()->PrefValueInExtensionStore(name_);
+}
+
+bool PrefService::Preference::HasUserSetting() const {
+  return pref_value_store()->PrefValueInUserStore(name_);
+}
+
+bool PrefService::Preference::IsExtensionControlled() const {
+  return pref_value_store()->PrefValueFromExtensionStore(name_);
+}
+
+bool PrefService::Preference::IsUserControlled() const {
+  return pref_value_store()->PrefValueFromUserStore(name_);
+}
+
+bool PrefService::Preference::IsDefaultValue() const {
+  return pref_value_store()->PrefValueFromDefaultStore(name_);
+}
+
+bool PrefService::Preference::IsUserModifiable() const {
+  return pref_value_store()->PrefValueUserModifiable(name_);
+}
+
+bool PrefService::Preference::IsExtensionModifiable() const {
+  return pref_value_store()->PrefValueExtensionModifiable(name_);
+}
+
+const base::Value* PrefService::GetPreferenceValue(
+    const std::string& path) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(battre): This is a check for crbug.com/435208. After analyzing some
+  // crash dumps it looks like the PrefService is accessed even though it has
+  // been cleared already.
+  CHECK(pref_registry_);
+  CHECK(pref_registry_->defaults());
+  CHECK(pref_value_store_);
+
+  const base::Value* default_value = nullptr;
+  if (pref_registry_->defaults()->GetValue(path, &default_value)) {
+    const base::Value* found_value = nullptr;
+    base::Value::Type default_type = default_value->type();
+    if (pref_value_store_->GetValue(path, default_type, &found_value)) {
+      DCHECK(found_value->type() == default_type);
+      return found_value;
+    } else {
+      // Every registered preference has at least a default value.
+      NOTREACHED() << "no valid value found for registered pref " << path;
+    }
+  }
+
+  return nullptr;
+}
+
+const base::Value* PrefService::GetPreferenceValueChecked(
+    const std::string& path) const {
+  const base::Value* value = GetPreferenceValue(path);
+  DCHECK(value) << "Trying to read an unregistered pref: " << path;
+  return value;
+}
diff --git a/src/updater_components/prefs/pref_service.h b/src/updater_components/prefs/pref_service.h
new file mode 100644
index 0000000..9580b48
--- /dev/null
+++ b/src/updater_components/prefs/pref_service.h
@@ -0,0 +1,468 @@
+// 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.
+
+// This provides a way to access the application's current preferences.
+
+// Chromium settings and storage represent user-selected preferences and
+// information and MUST not be extracted, overwritten or modified except
+// through Chromium defined APIs.
+
+#ifndef COMPONENTS_PREFS_PREF_SERVICE_H_
+#define COMPONENTS_PREFS_PREF_SERVICE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_value_store.h"
+#include "components/prefs/prefs_export.h"
+
+class PrefNotifier;
+class PrefNotifierImpl;
+class PrefObserver;
+class PrefRegistry;
+class PrefStore;
+
+namespace base {
+class FilePath;
+}
+
+namespace prefs {
+class ScopedDictionaryPrefUpdate;
+}
+
+namespace subtle {
+class PrefMemberBase;
+class ScopedUserPrefUpdateBase;
+}
+
+// Base class for PrefServices. You can use the base class to read and
+// interact with preferences, but not to register new preferences; for
+// that see e.g. PrefRegistrySimple.
+//
+// Settings and storage accessed through this class represent
+// user-selected preferences and information and MUST not be
+// extracted, overwritten or modified except through the defined APIs.
+class COMPONENTS_PREFS_EXPORT PrefService {
+ public:
+  enum PrefInitializationStatus {
+    INITIALIZATION_STATUS_WAITING,
+    INITIALIZATION_STATUS_SUCCESS,
+    INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE,
+    INITIALIZATION_STATUS_ERROR
+  };
+
+  enum IncludeDefaults {
+    INCLUDE_DEFAULTS,
+    EXCLUDE_DEFAULTS,
+  };
+
+  // A helper class to store all the information associated with a preference.
+  class COMPONENTS_PREFS_EXPORT Preference {
+   public:
+    // The type of the preference is determined by the type with which it is
+    // registered. This type needs to be a boolean, integer, double, string,
+    // dictionary (a branch), or list.  You shouldn't need to construct this on
+    // your own; use the PrefService::Register*Pref methods instead.
+    Preference(const PrefService* service,
+               std::string name,
+               base::Value::Type type);
+    ~Preference() {}
+
+    // Returns the name of the Preference (i.e., the key, e.g.,
+    // browser.window_placement).
+    std::string name() const { return name_; }
+
+    // Returns the registered type of the preference.
+    base::Value::Type GetType() const { return type_; }
+
+    // Returns the value of the Preference, falling back to the registered
+    // default value if no other has been set.
+    const base::Value* GetValue() const;
+
+    // Returns the value recommended by the admin, if any.
+    const base::Value* GetRecommendedValue() const;
+
+    // Returns true if the Preference is managed, i.e. set by an admin policy.
+    // Since managed prefs have the highest priority, this also indicates
+    // whether the pref is actually being controlled by the policy setting.
+    bool IsManaged() const;
+
+    // Returns true if the Preference is controlled by the custodian of the
+    // supervised user. Since a supervised user is not expected to have an admin
+    // policy, this is the controlling pref if set.
+    bool IsManagedByCustodian() const;
+
+    // Returns true if the Preference's current value is one recommended by
+    // admin policy. Note that this will be false if any other higher-priority
+    // source overrides the value (e.g., the user has set a value).
+    bool IsRecommended() const;
+
+    // Returns true if the Preference has a value set by an extension, even if
+    // that value is being overridden by a higher-priority source.
+    bool HasExtensionSetting() const;
+
+    // Returns true if the Preference has a user setting, even if that value is
+    // being overridden by a higher-priority source.
+    bool HasUserSetting() const;
+
+    // Returns true if the Preference value is currently being controlled by an
+    // extension, and not by any higher-priority source.
+    bool IsExtensionControlled() const;
+
+    // Returns true if the Preference value is currently being controlled by a
+    // user setting, and not by any higher-priority source.
+    bool IsUserControlled() const;
+
+    // Returns true if the Preference is currently using its default value,
+    // and has not been set by any higher-priority source (even with the same
+    // value).
+    bool IsDefaultValue() const;
+
+    // Returns true if the user can change the Preference value, which is the
+    // case if no higher-priority source than the user store controls the
+    // Preference.
+    bool IsUserModifiable() const;
+
+    // Returns true if an extension can change the Preference value, which is
+    // the case if no higher-priority source than the extension store controls
+    // the Preference.
+    bool IsExtensionModifiable() const;
+
+    // Return the registration flags for this pref as a bitmask of
+    // PrefRegistry::PrefRegistrationFlags.
+    uint32_t registration_flags() const { return registration_flags_; }
+
+   private:
+    friend class PrefService;
+
+    PrefValueStore* pref_value_store() const {
+      return pref_service_->pref_value_store_.get();
+    }
+
+    const std::string name_;
+
+    const base::Value::Type type_;
+
+    const uint32_t registration_flags_;
+
+    // Reference to the PrefService in which this pref was created.
+    const PrefService* const pref_service_;
+  };
+
+  // You may wish to use PrefServiceFactory or one of its subclasses
+  // for simplified construction.
+  PrefService(std::unique_ptr<PrefNotifierImpl> pref_notifier,
+              std::unique_ptr<PrefValueStore> pref_value_store,
+              scoped_refptr<PersistentPrefStore> user_prefs,
+              scoped_refptr<PrefRegistry> pref_registry,
+              base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
+                  read_error_callback,
+              bool async);
+  virtual ~PrefService();
+
+  // Lands pending writes to disk. This should only be used if we need to save
+  // immediately (basically, during shutdown). |reply_callback| will be posted
+  // to the current sequence when changes have been written.
+  // |synchronous_done_callback| on the other hand will be invoked right away
+  // wherever the writes complete (could even be invoked synchronously if no
+  // writes need to occur); this is useful when the current thread cannot pump
+  // messages to observe the reply (e.g. nested loops banned on main thread
+  // during shutdown). |synchronous_done_callback| must be thread-safe.
+  void CommitPendingWrite(
+      base::OnceClosure reply_callback = base::OnceClosure(),
+      base::OnceClosure synchronous_done_callback = base::OnceClosure());
+
+  // Schedule a write if there is any lossy data pending. Unlike
+  // CommitPendingWrite() this does not immediately sync to disk, instead it
+  // triggers an eventual write if there is lossy data pending and if there
+  // isn't one scheduled already.
+  void SchedulePendingLossyWrites();
+
+  // Returns true if the preference for the given preference name is available
+  // and is managed.
+  bool IsManagedPreference(const std::string& pref_name) const;
+
+  // Returns true if the preference for the given preference name is available
+  // and is controlled by the parent/guardian of the child Account.
+  bool IsPreferenceManagedByCustodian(const std::string& pref_name) const;
+
+  // Returns |true| if a preference with the given name is available and its
+  // value can be changed by the user.
+  bool IsUserModifiablePreference(const std::string& pref_name) const;
+
+  // Look up a preference.  Returns NULL if the preference is not
+  // registered.
+  const PrefService::Preference* FindPreference(const std::string& path) const;
+
+  // If the path is valid and the value at the end of the path matches the type
+  // specified, it will return the specified value.  Otherwise, the default
+  // value (set when the pref was registered) will be returned.
+  bool GetBoolean(const std::string& path) const;
+  int GetInteger(const std::string& path) const;
+  double GetDouble(const std::string& path) const;
+  std::string GetString(const std::string& path) const;
+  base::FilePath GetFilePath(const std::string& path) const;
+
+  // Returns the branch if it exists, or the registered default value otherwise.
+  // Note that |path| must point to a registered preference. In that case, these
+  // functions will never return NULL.
+  const base::Value* Get(const std::string& path) const;
+  const base::DictionaryValue* GetDictionary(const std::string& path) const;
+  const base::ListValue* GetList(const std::string& path) const;
+
+  // Removes a user pref and restores the pref to its default value.
+  void ClearPref(const std::string& path);
+
+  // If the path is valid (i.e., registered), update the pref value in the user
+  // prefs.
+  // To set the value of dictionary or list values in the pref tree use
+  // Set(), but to modify the value of a dictionary or list use either
+  // ListPrefUpdate or DictionaryPrefUpdate from scoped_user_pref_update.h.
+  void Set(const std::string& path, const base::Value& value);
+  void SetBoolean(const std::string& path, bool value);
+  void SetInteger(const std::string& path, int value);
+  void SetDouble(const std::string& path, double value);
+  void SetString(const std::string& path, const std::string& value);
+  void SetFilePath(const std::string& path, const base::FilePath& value);
+
+  // Int64 helper methods that actually store the given value as a string.
+  // Note that if obtaining the named value via GetDictionary or GetList, the
+  // Value type will be Type::STRING.
+  void SetInt64(const std::string& path, int64_t value);
+  int64_t GetInt64(const std::string& path) const;
+
+  // As above, but for unsigned values.
+  void SetUint64(const std::string& path, uint64_t value);
+  uint64_t GetUint64(const std::string& path) const;
+
+  // Time helper methods that actually store the given value as a string, which
+  // represents the number of microseconds elapsed (absolute for TimeDelta and
+  // relative to Windows epoch for Time variants). Note that if obtaining the
+  // named value via GetDictionary or GetList, the Value type will be
+  // Type::STRING.
+  void SetTime(const std::string& path, base::Time value);
+  base::Time GetTime(const std::string& path) const;
+  void SetTimeDelta(const std::string& path, base::TimeDelta value);
+  base::TimeDelta GetTimeDelta(const std::string& path) const;
+
+  // Returns the value of the given preference, from the user pref store. If
+  // the preference is not set in the user pref store, returns NULL.
+  const base::Value* GetUserPrefValue(const std::string& path) const;
+
+  // Changes the default value for a preference.
+  //
+  // Will cause a pref change notification to be fired if this causes
+  // the effective value to change.
+  void SetDefaultPrefValue(const std::string& path, base::Value value);
+
+  // Returns the default value of the given preference. |path| must point to a
+  // registered preference. In that case, will never return nullptr, so callers
+  // do not need to check this.
+  const base::Value* GetDefaultPrefValue(const std::string& path) const;
+
+  // Returns true if a value has been set for the specified path.
+  // NOTE: this is NOT the same as FindPreference. In particular
+  // FindPreference returns whether RegisterXXX has been invoked, where as
+  // this checks if a value exists for the path.
+  bool HasPrefPath(const std::string& path) const;
+
+  // Issues a callback for every preference value. The preferences must not be
+  // mutated during iteration.
+  void IteratePreferenceValues(
+      base::RepeatingCallback<void(const std::string& key,
+                                   const base::Value& value)> callback) const;
+
+  // Returns a dictionary with effective preference values. This is an expensive
+  // operation which does a deep copy. Use only if you really need the results
+  // in a base::Value (for example, for JSON serialization). Otherwise use
+  // IteratePreferenceValues above to avoid the copies.
+  //
+  // If INCLUDE_DEFAULTS is requested, preferences set to their default values
+  // will be included. Otherwise, these will be omitted from the returned
+  // dictionary.
+  std::unique_ptr<base::DictionaryValue> GetPreferenceValues(
+      IncludeDefaults include_defaults) const;
+
+  bool ReadOnly() const;
+
+  // Returns the initialization state, taking only user prefs into account.
+  PrefInitializationStatus GetInitializationStatus() const;
+
+  // Returns the initialization state, taking all pref stores into account.
+  PrefInitializationStatus GetAllPrefStoresInitializationStatus() const;
+
+  // Tell our PrefValueStore to update itself to |command_line_store|.
+  // Takes ownership of the store.
+  virtual void UpdateCommandLinePrefStore(PrefStore* command_line_store);
+
+  // We run the callback once, when initialization completes. The bool
+  // parameter will be set to true for successful initialization,
+  // false for unsuccessful.
+  void AddPrefInitObserver(base::OnceCallback<void(bool)> callback);
+
+  // Returns the PrefRegistry object for this service. You should not
+  // use this; the intent is for no registrations to take place after
+  // PrefService has been constructed.
+  //
+  // Instead of using this method, the recommended approach is to
+  // register all preferences for a class Xyz up front in a static
+  // Xyz::RegisterPrefs function, which gets invoked early in the
+  // application's start-up, before a PrefService is created.
+  //
+  // As an example, prefs registration in Chrome is triggered by the
+  // functions chrome::RegisterPrefs (for global preferences) and
+  // chrome::RegisterProfilePrefs (for user-specific preferences)
+  // implemented in chrome/browser/prefs/browser_prefs.cc.
+  PrefRegistry* DeprecatedGetPrefRegistry();
+
+  // Clears mutable values.
+  void ClearMutableValues();
+
+  // Invoked when the store is deleted from disk. Allows this PrefService
+  // to tangentially cleanup data it may have saved outside the store.
+  void OnStoreDeletionFromDisk();
+
+  // Add new pref stores to the existing PrefValueStore. Only adding new
+  // stores are allowed. If a corresponding store already exists, calling this
+  // will cause DCHECK failures. If the newly added stores already contain
+  // values, PrefNotifier associated with this object will be notified with
+  // these values. |delegate| can be passed to observe events of the new
+  // PrefValueStore.
+  // TODO(qinmin): packaging all the input params in a struct, and do the same
+  // for the constructor.
+  void ChangePrefValueStore(
+      PrefStore* managed_prefs,
+      PrefStore* supervised_user_prefs,
+      PrefStore* extension_prefs,
+      PrefStore* recommended_prefs,
+      std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr);
+
+  // A low level function for registering an observer for every single
+  // preference changed notification. The caller must ensure that the observer
+  // remains valid as long as it is registered. Pointer ownership is not
+  // transferred.
+  //
+  // Almost all calling code should use a PrefChangeRegistrar instead.
+  //
+  // AVOID ADDING THESE. These are low-level observer notifications that are
+  // called for every pref change. This can lead to inefficiency, and the lack
+  // of a "registrar" model makes it easy to forget to undregister. It is
+  // really designed for integrating other notification systems, not for normal
+  // observation.
+  void AddPrefObserverAllPrefs(PrefObserver* obs);
+  void RemovePrefObserverAllPrefs(PrefObserver* obs);
+
+ protected:
+  // The PrefNotifier handles registering and notifying preference observers.
+  // It is created and owned by this PrefService. Subclasses may access it for
+  // unit testing.
+  const std::unique_ptr<PrefNotifierImpl> pref_notifier_;
+
+  // The PrefValueStore provides prioritized preference values. It is owned by
+  // this PrefService. Subclasses may access it for unit testing.
+  std::unique_ptr<PrefValueStore> pref_value_store_;
+
+  // Pref Stores and profile that we passed to the PrefValueStore.
+  const scoped_refptr<PersistentPrefStore> user_pref_store_;
+
+  // Callback to call when a read error occurs. Always invoked on the sequence
+  // this PrefService was created own.
+  const base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
+      read_error_callback_;
+
+ private:
+  // Hash map expected to be fastest here since it minimises expensive
+  // string comparisons. Order is unimportant, and deletions are rare.
+  // Confirmed on Android where this speeded Chrome startup by roughly 50ms
+  // vs. std::map, and by roughly 180ms vs. std::set of Preference pointers.
+  typedef std::unordered_map<std::string, Preference> PreferenceMap;
+
+  // Give access to ReportUserPrefChanged() and GetMutableUserPref().
+  friend class subtle::ScopedUserPrefUpdateBase;
+  friend class PrefServiceTest_WriteablePrefStoreFlags_Test;
+  friend class prefs::ScopedDictionaryPrefUpdate;
+
+  // Registration of pref change observers must be done using the
+  // PrefChangeRegistrar, which is declared as a friend here to grant it
+  // access to the otherwise protected members Add/RemovePrefObserver.
+  // PrefMember registers for preferences changes notification directly to
+  // avoid the storage overhead of the registrar, so its base class must be
+  // declared as a friend, too.
+  friend class PrefChangeRegistrar;
+  friend class subtle::PrefMemberBase;
+
+  // These are protected so they can only be accessed by the friend
+  // classes listed above.
+  //
+  // If the pref at the given path changes, we call the observer's
+  // OnPreferenceChanged method. Note that observers should not call
+  // these methods directly but rather use a PrefChangeRegistrar to
+  // make sure the observer gets cleaned up properly.
+  //
+  // Virtual for testing.
+  virtual void AddPrefObserver(const std::string& path, PrefObserver* obs);
+  virtual void RemovePrefObserver(const std::string& path, PrefObserver* obs);
+
+  // Sends notification of a changed preference. This needs to be called by
+  // a ScopedUserPrefUpdate or ScopedDictionaryPrefUpdate if a DictionaryValue
+  // or ListValue is changed.
+  void ReportUserPrefChanged(const std::string& key);
+  void ReportUserPrefChanged(
+      const std::string& key,
+      std::set<std::vector<std::string>> path_components);
+
+  // Sets the value for this pref path in the user pref store and informs the
+  // PrefNotifier of the change.
+  void SetUserPrefValue(const std::string& path,
+                        std::unique_ptr<base::Value> new_value);
+
+  // Load preferences from storage, attempting to diagnose and handle errors.
+  // This should only be called from the constructor.
+  void InitFromStorage(bool async);
+
+  // Used to set the value of dictionary or list values in the user pref store.
+  // This will create a dictionary or list if one does not exist in the user
+  // pref store. This method returns NULL only if you're requesting an
+  // unregistered pref or a non-dict/non-list pref.
+  // |type| may only be Values::Type::DICTIONARY or Values::Type::LIST and
+  // |path| must point to a registered preference of type |type|.
+  // Ownership of the returned value remains at the user pref store.
+  base::Value* GetMutableUserPref(const std::string& path,
+                                  base::Value::Type type);
+
+  // GetPreferenceValue is the equivalent of FindPreference(path)->GetValue(),
+  // it has been added for performance. It is faster because it does
+  // not need to find or create a Preference object to get the
+  // value (GetValue() calls back though the preference service to
+  // actually get the value.).
+  const base::Value* GetPreferenceValue(const std::string& path) const;
+  const base::Value* GetPreferenceValueChecked(const std::string& path) const;
+
+  const scoped_refptr<PrefRegistry> pref_registry_;
+
+  // Local cache of registered Preference objects. The pref_registry_
+  // is authoritative with respect to what the types and default values
+  // of registered preferences are.
+  mutable PreferenceMap prefs_map_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(PrefService);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_SERVICE_H_
diff --git a/src/updater_components/prefs/pref_service_factory.cc b/src/updater_components/prefs/pref_service_factory.cc
new file mode 100644
index 0000000..cc8e52d
--- /dev/null
+++ b/src/updater_components/prefs/pref_service_factory.cc
@@ -0,0 +1,51 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/prefs/pref_service_factory.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/sequenced_task_runner.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/json_pref_store.h"
+#include "components/prefs/pref_filter.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_value_store.h"
+
+PrefServiceFactory::PrefServiceFactory()
+    : read_error_callback_(base::DoNothing()), async_(false) {}
+
+PrefServiceFactory::~PrefServiceFactory() {}
+
+void PrefServiceFactory::SetUserPrefsFile(
+    const base::FilePath& prefs_file,
+    base::SequencedTaskRunner* task_runner) {
+  user_prefs_ =
+      base::MakeRefCounted<JsonPrefStore>(prefs_file, nullptr, task_runner);
+}
+
+std::unique_ptr<PrefService> PrefServiceFactory::Create(
+    scoped_refptr<PrefRegistry> pref_registry,
+    std::unique_ptr<PrefValueStore::Delegate> delegate) {
+  auto pref_notifier = std::make_unique<PrefNotifierImpl>();
+  auto pref_value_store = std::make_unique<PrefValueStore>(
+      managed_prefs_.get(), supervised_user_prefs_.get(),
+      extension_prefs_.get(), command_line_prefs_.get(), user_prefs_.get(),
+      recommended_prefs_.get(), pref_registry->defaults().get(),
+      pref_notifier.get(), std::move(delegate));
+  return std::make_unique<PrefService>(
+      std::move(pref_notifier), std::move(pref_value_store), user_prefs_.get(),
+      std::move(pref_registry), read_error_callback_, async_);
+}
+
+void PrefServiceFactory::ChangePrefValueStore(
+    PrefService* pref_service,
+    std::unique_ptr<PrefValueStore::Delegate> delegate) {
+  pref_service->ChangePrefValueStore(
+      managed_prefs_.get(), supervised_user_prefs_.get(),
+      extension_prefs_.get(), recommended_prefs_.get(), std::move(delegate));
+}
diff --git a/src/updater_components/prefs/pref_service_factory.h b/src/updater_components/prefs/pref_service_factory.h
new file mode 100644
index 0000000..4e7d922
--- /dev/null
+++ b/src/updater_components/prefs/pref_service_factory.h
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_
+#define COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_store.h"
+#include "components/prefs/pref_value_store.h"
+#include "components/prefs/prefs_export.h"
+
+class PrefService;
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+}
+
+// A class that allows convenient building of PrefService.
+class COMPONENTS_PREFS_EXPORT PrefServiceFactory {
+ public:
+  PrefServiceFactory();
+  virtual ~PrefServiceFactory();
+
+  // Functions for setting the various parameters of the PrefService to build.
+  void set_managed_prefs(scoped_refptr<PrefStore> prefs) {
+    managed_prefs_.swap(prefs);
+  }
+
+  void set_supervised_user_prefs(scoped_refptr<PrefStore> prefs) {
+    supervised_user_prefs_.swap(prefs);
+  }
+
+  void set_extension_prefs(scoped_refptr<PrefStore> prefs) {
+    extension_prefs_.swap(prefs);
+  }
+
+  void set_command_line_prefs(scoped_refptr<PrefStore> prefs) {
+    command_line_prefs_.swap(prefs);
+  }
+
+  void set_user_prefs(scoped_refptr<PersistentPrefStore> prefs) {
+    user_prefs_.swap(prefs);
+  }
+
+  void set_recommended_prefs(scoped_refptr<PrefStore> prefs) {
+    recommended_prefs_.swap(prefs);
+  }
+
+  // Sets up error callback for the PrefService.  A do-nothing default is
+  // provided if this is not called. This callback is always invoked (async or
+  // not) on the sequence on which Create is invoked.
+  void set_read_error_callback(
+      base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
+          read_error_callback) {
+    read_error_callback_ = std::move(read_error_callback);
+  }
+
+  // Specifies to use an actual file-backed user pref store.
+  void SetUserPrefsFile(const base::FilePath& prefs_file,
+                        base::SequencedTaskRunner* task_runner);
+
+  void set_async(bool async) {
+    async_ = async;
+  }
+
+  // Creates a PrefService object initialized with the parameters from
+  // this factory.
+  std::unique_ptr<PrefService> Create(
+      scoped_refptr<PrefRegistry> pref_registry,
+      std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr);
+
+  // Add pref stores from this object to the |pref_service|.
+  void ChangePrefValueStore(
+      PrefService* pref_service,
+      std::unique_ptr<PrefValueStore::Delegate> delegate = nullptr);
+
+ protected:
+  scoped_refptr<PrefStore> managed_prefs_;
+  scoped_refptr<PrefStore> supervised_user_prefs_;
+  scoped_refptr<PrefStore> extension_prefs_;
+  scoped_refptr<PrefStore> command_line_prefs_;
+  scoped_refptr<PersistentPrefStore> user_prefs_;
+  scoped_refptr<PrefStore> recommended_prefs_;
+
+  base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
+      read_error_callback_;
+
+  // Defaults to false.
+  bool async_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PrefServiceFactory);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_
diff --git a/src/updater_components/prefs/pref_service_unittest.cc b/src/updater_components/prefs/pref_service_unittest.cc
new file mode 100644
index 0000000..3bdcf35
--- /dev/null
+++ b/src/updater_components/prefs/pref_service_unittest.cc
@@ -0,0 +1,624 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/bind_helpers.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/prefs/json_pref_store.h"
+#include "components/prefs/mock_pref_change_callback.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service_factory.h"
+#include "components/prefs/pref_value_store.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/prefs/testing_pref_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace {
+
+const char kPrefName[] = "pref.name";
+const char kManagedPref[] = "managed_pref";
+const char kRecommendedPref[] = "recommended_pref";
+const char kSupervisedPref[] = "supervised_pref";
+
+}  // namespace
+
+TEST(PrefServiceTest, NoObserverFire) {
+  TestingPrefServiceSimple prefs;
+
+  const char pref_name[] = "homepage";
+  prefs.registry()->RegisterStringPref(pref_name, std::string());
+
+  const char new_pref_value[] = "http://www.google.com/";
+  MockPrefChangeCallback obs(&prefs);
+  PrefChangeRegistrar registrar;
+  registrar.Init(&prefs);
+  registrar.Add(pref_name, obs.GetCallback());
+
+  // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged.
+  const base::Value expected_value(new_pref_value);
+  obs.Expect(pref_name, &expected_value);
+  prefs.SetString(pref_name, new_pref_value);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Setting the pref to the same value should not set the pref value a second
+  // time.
+  EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0);
+  prefs.SetString(pref_name, new_pref_value);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Clearing the pref should cause the pref to fire.
+  const base::Value expected_default_value((std::string()));
+  obs.Expect(pref_name, &expected_default_value);
+  prefs.ClearPref(pref_name);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Clearing the pref again should not cause the pref to fire.
+  EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0);
+  prefs.ClearPref(pref_name);
+  Mock::VerifyAndClearExpectations(&obs);
+}
+
+TEST(PrefServiceTest, HasPrefPath) {
+  TestingPrefServiceSimple prefs;
+
+  const char path[] = "fake.path";
+
+  // Shouldn't initially have a path.
+  EXPECT_FALSE(prefs.HasPrefPath(path));
+
+  // Register the path. This doesn't set a value, so the path still shouldn't
+  // exist.
+  prefs.registry()->RegisterStringPref(path, std::string());
+  EXPECT_FALSE(prefs.HasPrefPath(path));
+
+  // Set a value and make sure we have a path.
+  prefs.SetString(path, "blah");
+  EXPECT_TRUE(prefs.HasPrefPath(path));
+}
+
+TEST(PrefServiceTest, Observers) {
+  const char pref_name[] = "homepage";
+
+  TestingPrefServiceSimple prefs;
+  prefs.SetUserPref(pref_name,
+                    std::make_unique<base::Value>("http://www.cnn.com"));
+  prefs.registry()->RegisterStringPref(pref_name, std::string());
+
+  const char new_pref_value[] = "http://www.google.com/";
+  const base::Value expected_new_pref_value(new_pref_value);
+  MockPrefChangeCallback obs(&prefs);
+  PrefChangeRegistrar registrar;
+  registrar.Init(&prefs);
+  registrar.Add(pref_name, obs.GetCallback());
+
+  PrefChangeRegistrar registrar_two;
+  registrar_two.Init(&prefs);
+
+  // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged.
+  obs.Expect(pref_name, &expected_new_pref_value);
+  prefs.SetString(pref_name, new_pref_value);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Now try adding a second pref observer.
+  const char new_pref_value2[] = "http://www.youtube.com/";
+  const base::Value expected_new_pref_value2(new_pref_value2);
+  MockPrefChangeCallback obs2(&prefs);
+  obs.Expect(pref_name, &expected_new_pref_value2);
+  obs2.Expect(pref_name, &expected_new_pref_value2);
+  registrar_two.Add(pref_name, obs2.GetCallback());
+  // This should fire the checks in obs and obs2.
+  prefs.SetString(pref_name, new_pref_value2);
+  Mock::VerifyAndClearExpectations(&obs);
+  Mock::VerifyAndClearExpectations(&obs2);
+
+  // Set a recommended value.
+  const base::Value recommended_pref_value("http://www.gmail.com/");
+  obs.Expect(pref_name, &expected_new_pref_value2);
+  obs2.Expect(pref_name, &expected_new_pref_value2);
+  // This should fire the checks in obs and obs2 but with an unchanged value
+  // as the recommended value is being overridden by the user-set value.
+  prefs.SetRecommendedPref(pref_name, recommended_pref_value.CreateDeepCopy());
+  Mock::VerifyAndClearExpectations(&obs);
+  Mock::VerifyAndClearExpectations(&obs2);
+
+  // Make sure obs2 still works after removing obs.
+  registrar.Remove(pref_name);
+  EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0);
+  obs2.Expect(pref_name, &expected_new_pref_value);
+  // This should only fire the observer in obs2.
+  prefs.SetString(pref_name, new_pref_value);
+  Mock::VerifyAndClearExpectations(&obs);
+  Mock::VerifyAndClearExpectations(&obs2);
+}
+
+// Make sure that if a preference changes type, so the wrong type is stored in
+// the user pref file, it uses the correct fallback value instead.
+TEST(PrefServiceTest, GetValueChangedType) {
+  const int kTestValue = 10;
+  TestingPrefServiceSimple prefs;
+  prefs.registry()->RegisterIntegerPref(kPrefName, kTestValue);
+
+  // Check falling back to a recommended value.
+  prefs.SetUserPref(kPrefName, std::make_unique<base::Value>("not an integer"));
+  const PrefService::Preference* pref = prefs.FindPreference(kPrefName);
+  ASSERT_TRUE(pref);
+  const base::Value* value = pref->GetValue();
+  ASSERT_TRUE(value);
+  EXPECT_EQ(base::Value::Type::INTEGER, value->type());
+  int actual_int_value = -1;
+  EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+  EXPECT_EQ(kTestValue, actual_int_value);
+}
+
+TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
+  const int kDefaultValue = 5;
+  const int kUserValue = 10;
+  const int kRecommendedValue = 15;
+  TestingPrefServiceSimple prefs;
+  prefs.registry()->RegisterIntegerPref(kPrefName, kDefaultValue);
+
+  // Create pref with a default value only.
+  const PrefService::Preference* pref = prefs.FindPreference(kPrefName);
+  ASSERT_TRUE(pref);
+
+  // Check that GetValue() returns the default value.
+  const base::Value* value = pref->GetValue();
+  ASSERT_TRUE(value);
+  EXPECT_EQ(base::Value::Type::INTEGER, value->type());
+  int actual_int_value = -1;
+  EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+  EXPECT_EQ(kDefaultValue, actual_int_value);
+
+  // Check that GetRecommendedValue() returns no value.
+  value = pref->GetRecommendedValue();
+  ASSERT_FALSE(value);
+
+  // Set a user-set value.
+  prefs.SetUserPref(kPrefName, std::make_unique<base::Value>(kUserValue));
+
+  // Check that GetValue() returns the user-set value.
+  value = pref->GetValue();
+  ASSERT_TRUE(value);
+  EXPECT_EQ(base::Value::Type::INTEGER, value->type());
+  actual_int_value = -1;
+  EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+  EXPECT_EQ(kUserValue, actual_int_value);
+
+  // Check that GetRecommendedValue() returns no value.
+  value = pref->GetRecommendedValue();
+  ASSERT_FALSE(value);
+
+  // Set a recommended value.
+  prefs.SetRecommendedPref(kPrefName,
+                           std::make_unique<base::Value>(kRecommendedValue));
+
+  // Check that GetValue() returns the user-set value.
+  value = pref->GetValue();
+  ASSERT_TRUE(value);
+  EXPECT_EQ(base::Value::Type::INTEGER, value->type());
+  actual_int_value = -1;
+  EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+  EXPECT_EQ(kUserValue, actual_int_value);
+
+  // Check that GetRecommendedValue() returns the recommended value.
+  value = pref->GetRecommendedValue();
+  ASSERT_TRUE(value);
+  EXPECT_EQ(base::Value::Type::INTEGER, value->type());
+  actual_int_value = -1;
+  EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+  EXPECT_EQ(kRecommendedValue, actual_int_value);
+
+  // Remove the user-set value.
+  prefs.RemoveUserPref(kPrefName);
+
+  // Check that GetValue() returns the recommended value.
+  value = pref->GetValue();
+  ASSERT_TRUE(value);
+  EXPECT_EQ(base::Value::Type::INTEGER, value->type());
+  actual_int_value = -1;
+  EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+  EXPECT_EQ(kRecommendedValue, actual_int_value);
+
+  // Check that GetRecommendedValue() returns the recommended value.
+  value = pref->GetRecommendedValue();
+  ASSERT_TRUE(value);
+  EXPECT_EQ(base::Value::Type::INTEGER, value->type());
+  actual_int_value = -1;
+  EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+  EXPECT_EQ(kRecommendedValue, actual_int_value);
+}
+
+TEST(PrefServiceTest, SetTimeValue_RegularTime) {
+  TestingPrefServiceSimple prefs;
+
+  // Register a null time as the default.
+  prefs.registry()->RegisterTimePref(kPrefName, base::Time());
+  EXPECT_TRUE(prefs.GetTime(kPrefName).is_null());
+
+  // Set a time and make sure that we can read it without any loss of precision.
+  const base::Time time = base::Time::Now();
+  prefs.SetTime(kPrefName, time);
+  EXPECT_EQ(time, prefs.GetTime(kPrefName));
+}
+
+TEST(PrefServiceTest, SetTimeValue_NullTime) {
+  TestingPrefServiceSimple prefs;
+
+  // Register a non-null time as the default.
+  const base::Time default_time = base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromMicroseconds(12345));
+  prefs.registry()->RegisterTimePref(kPrefName, default_time);
+  EXPECT_FALSE(prefs.GetTime(kPrefName).is_null());
+
+  // Set a null time and make sure that it remains null upon deserialization.
+  prefs.SetTime(kPrefName, base::Time());
+  EXPECT_TRUE(prefs.GetTime(kPrefName).is_null());
+}
+
+TEST(PrefServiceTest, SetTimeDeltaValue_RegularTimeDelta) {
+  TestingPrefServiceSimple prefs;
+
+  // Register a zero time delta as the default.
+  prefs.registry()->RegisterTimeDeltaPref(kPrefName, base::TimeDelta());
+  EXPECT_TRUE(prefs.GetTimeDelta(kPrefName).is_zero());
+
+  // Set a time delta and make sure that we can read it without any loss of
+  // precision.
+  const base::TimeDelta delta = base::Time::Now() - base::Time();
+  prefs.SetTimeDelta(kPrefName, delta);
+  EXPECT_EQ(delta, prefs.GetTimeDelta(kPrefName));
+}
+
+TEST(PrefServiceTest, SetTimeDeltaValue_ZeroTimeDelta) {
+  TestingPrefServiceSimple prefs;
+
+  // Register a non-zero time delta as the default.
+  const base::TimeDelta default_delta =
+      base::TimeDelta::FromMicroseconds(12345);
+  prefs.registry()->RegisterTimeDeltaPref(kPrefName, default_delta);
+  EXPECT_FALSE(prefs.GetTimeDelta(kPrefName).is_zero());
+
+  // Set a zero time delta and make sure that it remains zero upon
+  // deserialization.
+  prefs.SetTimeDelta(kPrefName, base::TimeDelta());
+  EXPECT_TRUE(prefs.GetTimeDelta(kPrefName).is_zero());
+}
+
+// A PrefStore which just stores the last write flags that were used to write
+// values to it.
+class WriteFlagChecker : public TestingPrefStore {
+ public:
+  WriteFlagChecker() {}
+
+  void ReportValueChanged(const std::string& key, uint32_t flags) override {
+    SetLastWriteFlags(flags);
+  }
+
+  void SetValue(const std::string& key,
+                std::unique_ptr<base::Value> value,
+                uint32_t flags) override {
+    SetLastWriteFlags(flags);
+  }
+
+  void SetValueSilently(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) override {
+    SetLastWriteFlags(flags);
+  }
+
+  void RemoveValue(const std::string& key, uint32_t flags) override {
+    SetLastWriteFlags(flags);
+  }
+
+  uint32_t GetLastFlagsAndClear() {
+    CHECK(last_write_flags_set_);
+    uint32_t result = last_write_flags_;
+    last_write_flags_set_ = false;
+    last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS;
+    return result;
+  }
+
+  bool last_write_flags_set() { return last_write_flags_set_; }
+
+ private:
+  ~WriteFlagChecker() override {}
+
+  void SetLastWriteFlags(uint32_t flags) {
+    CHECK(!last_write_flags_set_);
+    last_write_flags_set_ = true;
+    last_write_flags_ = flags;
+  }
+
+  bool last_write_flags_set_ = false;
+  uint32_t last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS;
+};
+
+TEST(PrefServiceTest, WriteablePrefStoreFlags) {
+  scoped_refptr<WriteFlagChecker> flag_checker(new WriteFlagChecker);
+  scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple);
+  PrefServiceFactory factory;
+  factory.set_user_prefs(flag_checker);
+  std::unique_ptr<PrefService> prefs(factory.Create(registry.get()));
+
+  // The first 8 bits of write flags are reserved for subclasses. Create a
+  // custom flag in this range
+  uint32_t kCustomRegistrationFlag = 1 << 2;
+
+  // A map of the registration flags that will be tested and the write flags
+  // they are expected to convert to.
+  struct RegistrationToWriteFlags {
+    const char* pref_name;
+    uint32_t registration_flags;
+    uint32_t write_flags;
+  };
+  const RegistrationToWriteFlags kRegistrationToWriteFlags[] = {
+      {"none",
+       PrefRegistry::NO_REGISTRATION_FLAGS,
+       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS},
+      {"lossy",
+       PrefRegistry::LOSSY_PREF,
+       WriteablePrefStore::LOSSY_PREF_WRITE_FLAG},
+      {"custom",
+       kCustomRegistrationFlag,
+       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS},
+      {"lossyandcustom",
+       PrefRegistry::LOSSY_PREF | kCustomRegistrationFlag,
+       WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}};
+
+  for (size_t i = 0; i < base::size(kRegistrationToWriteFlags); ++i) {
+    RegistrationToWriteFlags entry = kRegistrationToWriteFlags[i];
+    registry->RegisterDictionaryPref(entry.pref_name,
+                                     entry.registration_flags);
+
+    SCOPED_TRACE("Currently testing pref with name: " +
+                 std::string(entry.pref_name));
+
+    prefs->GetMutableUserPref(entry.pref_name, base::Value::Type::DICTIONARY);
+    EXPECT_TRUE(flag_checker->last_write_flags_set());
+    EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+
+    prefs->ReportUserPrefChanged(entry.pref_name);
+    EXPECT_TRUE(flag_checker->last_write_flags_set());
+    EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+
+    prefs->ClearPref(entry.pref_name);
+    EXPECT_TRUE(flag_checker->last_write_flags_set());
+    EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+
+    prefs->SetUserPrefValue(entry.pref_name,
+                            std::make_unique<base::DictionaryValue>());
+    EXPECT_TRUE(flag_checker->last_write_flags_set());
+    EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+  }
+}
+
+class PrefServiceSetValueTest : public testing::Test {
+ protected:
+  static const char kName[];
+  static const char kValue[];
+
+  PrefServiceSetValueTest() : observer_(&prefs_) {}
+
+  TestingPrefServiceSimple prefs_;
+  MockPrefChangeCallback observer_;
+};
+
+const char PrefServiceSetValueTest::kName[] = "name";
+const char PrefServiceSetValueTest::kValue[] = "value";
+
+TEST_F(PrefServiceSetValueTest, SetStringValue) {
+  const char default_string[] = "default";
+  const base::Value default_value(default_string);
+  prefs_.registry()->RegisterStringPref(kName, default_string);
+
+  PrefChangeRegistrar registrar;
+  registrar.Init(&prefs_);
+  registrar.Add(kName, observer_.GetCallback());
+
+  // Changing the controlling store from default to user triggers notification.
+  observer_.Expect(kName, &default_value);
+  prefs_.Set(kName, default_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+  prefs_.Set(kName, default_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  base::Value new_value(kValue);
+  observer_.Expect(kName, &new_value);
+  prefs_.Set(kName, new_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+}
+
+TEST_F(PrefServiceSetValueTest, SetDictionaryValue) {
+  prefs_.registry()->RegisterDictionaryPref(kName);
+  PrefChangeRegistrar registrar;
+  registrar.Init(&prefs_);
+  registrar.Add(kName, observer_.GetCallback());
+
+  EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+  prefs_.RemoveUserPref(kName);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  base::DictionaryValue new_value;
+  new_value.SetString(kName, kValue);
+  observer_.Expect(kName, &new_value);
+  prefs_.Set(kName, new_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+  prefs_.Set(kName, new_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  base::DictionaryValue empty;
+  observer_.Expect(kName, &empty);
+  prefs_.Set(kName, empty);
+  Mock::VerifyAndClearExpectations(&observer_);
+}
+
+TEST_F(PrefServiceSetValueTest, SetListValue) {
+  prefs_.registry()->RegisterListPref(kName);
+  PrefChangeRegistrar registrar;
+  registrar.Init(&prefs_);
+  registrar.Add(kName, observer_.GetCallback());
+
+  EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+  prefs_.RemoveUserPref(kName);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  base::ListValue new_value;
+  new_value.AppendString(kValue);
+  observer_.Expect(kName, &new_value);
+  prefs_.Set(kName, new_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+  prefs_.Set(kName, new_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  base::ListValue empty;
+  observer_.Expect(kName, &empty);
+  prefs_.Set(kName, empty);
+  Mock::VerifyAndClearExpectations(&observer_);
+}
+
+class PrefValueStoreChangeTest : public testing::Test {
+ protected:
+  PrefValueStoreChangeTest()
+      : user_pref_store_(base::MakeRefCounted<TestingPrefStore>()),
+        pref_registry_(base::MakeRefCounted<PrefRegistrySimple>()) {}
+
+  ~PrefValueStoreChangeTest() override = default;
+
+  void SetUp() override {
+    auto pref_notifier = std::make_unique<PrefNotifierImpl>();
+    auto pref_value_store = std::make_unique<PrefValueStore>(
+        nullptr /* managed_prefs */, nullptr /* supervised_user_prefs */,
+        nullptr /* extension_prefs */, new TestingPrefStore(),
+        user_pref_store_.get(), nullptr /* recommended_prefs */,
+        pref_registry_->defaults().get(), pref_notifier.get());
+    pref_service_ = std::make_unique<PrefService>(
+        std::move(pref_notifier), std::move(pref_value_store), user_pref_store_,
+        pref_registry_, base::DoNothing(), false);
+    pref_registry_->RegisterIntegerPref(kManagedPref, 1);
+    pref_registry_->RegisterIntegerPref(kRecommendedPref, 2);
+    pref_registry_->RegisterIntegerPref(kSupervisedPref, 3);
+  }
+
+  std::unique_ptr<PrefService> pref_service_;
+  scoped_refptr<TestingPrefStore> user_pref_store_;
+  scoped_refptr<PrefRegistrySimple> pref_registry_;
+};
+
+// Check that value from the new PrefValueStore will be correctly retrieved.
+TEST_F(PrefValueStoreChangeTest, ChangePrefValueStore) {
+  const PrefService::Preference* preference =
+      pref_service_->FindPreference(kManagedPref);
+  EXPECT_TRUE(preference->IsDefaultValue());
+  EXPECT_EQ(base::Value(1), *(preference->GetValue()));
+  const PrefService::Preference* supervised =
+      pref_service_->FindPreference(kSupervisedPref);
+  EXPECT_TRUE(supervised->IsDefaultValue());
+  EXPECT_EQ(base::Value(3), *(supervised->GetValue()));
+  const PrefService::Preference* recommended =
+      pref_service_->FindPreference(kRecommendedPref);
+  EXPECT_TRUE(recommended->IsDefaultValue());
+  EXPECT_EQ(base::Value(2), *(recommended->GetValue()));
+
+  user_pref_store_->SetInteger(kManagedPref, 10);
+  EXPECT_TRUE(preference->IsUserControlled());
+  ASSERT_EQ(base::Value(10), *(preference->GetValue()));
+
+  scoped_refptr<TestingPrefStore> managed_pref_store =
+      base::MakeRefCounted<TestingPrefStore>();
+  pref_service_->ChangePrefValueStore(
+      managed_pref_store.get(), nullptr /* supervised_user_prefs */,
+      nullptr /* extension_prefs */, nullptr /* recommended_prefs */);
+  EXPECT_TRUE(preference->IsUserControlled());
+  ASSERT_EQ(base::Value(10), *(preference->GetValue()));
+
+  // Test setting a managed pref after overriding the managed PrefStore.
+  managed_pref_store->SetInteger(kManagedPref, 20);
+  EXPECT_TRUE(preference->IsManaged());
+  ASSERT_EQ(base::Value(20), *(preference->GetValue()));
+
+  // Test overriding the supervised and recommended PrefStore with already set
+  // prefs.
+  scoped_refptr<TestingPrefStore> supervised_pref_store =
+      base::MakeRefCounted<TestingPrefStore>();
+  scoped_refptr<TestingPrefStore> recommened_pref_store =
+      base::MakeRefCounted<TestingPrefStore>();
+  supervised_pref_store->SetInteger(kManagedPref, 30);
+  supervised_pref_store->SetInteger(kSupervisedPref, 31);
+  recommened_pref_store->SetInteger(kManagedPref, 40);
+  recommened_pref_store->SetInteger(kRecommendedPref, 41);
+  pref_service_->ChangePrefValueStore(
+      nullptr /* managed_prefs */, supervised_pref_store.get(),
+      nullptr /* extension_prefs */, recommened_pref_store.get());
+  EXPECT_TRUE(preference->IsManaged());
+  ASSERT_EQ(base::Value(20), *(preference->GetValue()));
+  EXPECT_TRUE(supervised->IsManagedByCustodian());
+  EXPECT_EQ(base::Value(31), *(supervised->GetValue()));
+  EXPECT_TRUE(recommended->IsRecommended());
+  EXPECT_EQ(base::Value(41), *(recommended->GetValue()));
+}
+
+// Tests that PrefChangeRegistrar works after PrefValueStore is changed.
+TEST_F(PrefValueStoreChangeTest, PrefChangeRegistrar) {
+  MockPrefChangeCallback obs(pref_service_.get());
+  PrefChangeRegistrar registrar;
+  registrar.Init(pref_service_.get());
+  registrar.Add(kManagedPref, obs.GetCallback());
+  registrar.Add(kSupervisedPref, obs.GetCallback());
+  registrar.Add(kRecommendedPref, obs.GetCallback());
+
+  base::Value expected_value(10);
+  obs.Expect(kManagedPref, &expected_value);
+  user_pref_store_->SetInteger(kManagedPref, 10);
+  Mock::VerifyAndClearExpectations(&obs);
+  expected_value = base::Value(11);
+  obs.Expect(kRecommendedPref, &expected_value);
+  user_pref_store_->SetInteger(kRecommendedPref, 11);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Test overriding the managed and supervised PrefStore with already set
+  // prefs.
+  scoped_refptr<TestingPrefStore> managed_pref_store =
+      base::MakeRefCounted<TestingPrefStore>();
+  scoped_refptr<TestingPrefStore> supervised_pref_store =
+      base::MakeRefCounted<TestingPrefStore>();
+  // Update |kManagedPref| before changing the PrefValueStore, the
+  // PrefChangeRegistrar should get notified on |kManagedPref| as its value
+  // changes.
+  managed_pref_store->SetInteger(kManagedPref, 20);
+  // Due to store precedence, the value of |kRecommendedPref| will not be
+  // changed so PrefChangeRegistrar will not be notified.
+  managed_pref_store->SetInteger(kRecommendedPref, 11);
+  supervised_pref_store->SetInteger(kManagedPref, 30);
+  supervised_pref_store->SetInteger(kRecommendedPref, 21);
+  expected_value = base::Value(20);
+  obs.Expect(kManagedPref, &expected_value);
+  pref_service_->ChangePrefValueStore(
+      managed_pref_store.get(), supervised_pref_store.get(),
+      nullptr /* extension_prefs */, nullptr /* recommended_prefs */);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Update a pref value after PrefValueStore change, it should also work.
+  expected_value = base::Value(31);
+  obs.Expect(kSupervisedPref, &expected_value);
+  supervised_pref_store->SetInteger(kSupervisedPref, 31);
+  Mock::VerifyAndClearExpectations(&obs);
+}
diff --git a/src/updater_components/prefs/pref_store.cc b/src/updater_components/prefs/pref_store.cc
new file mode 100644
index 0000000..2ae0afc
--- /dev/null
+++ b/src/updater_components/prefs/pref_store.cc
@@ -0,0 +1,13 @@
+// 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 "components/prefs/pref_store.h"
+
+bool PrefStore::HasObservers() const {
+  return false;
+}
+
+bool PrefStore::IsInitializationComplete() const {
+  return true;
+}
diff --git a/src/updater_components/prefs/pref_store.h b/src/updater_components/prefs/pref_store.h
new file mode 100644
index 0000000..5f5f092
--- /dev/null
+++ b/src/updater_components/prefs/pref_store.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_PREF_STORE_H_
+#define COMPONENTS_PREFS_PREF_STORE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/prefs_export.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+}
+
+// This is an abstract interface for reading and writing from/to a persistent
+// preference store, used by PrefService. An implementation using a JSON file
+// can be found in JsonPrefStore, while an implementation without any backing
+// store for testing can be found in TestingPrefStore. Furthermore, there is
+// CommandLinePrefStore, which bridges command line options to preferences and
+// ConfigurationPolicyPrefStore, which is used for hooking up configuration
+// policy with the preference subsystem.
+class COMPONENTS_PREFS_EXPORT PrefStore : public base::RefCounted<PrefStore> {
+ public:
+  // Observer interface for monitoring PrefStore.
+  class COMPONENTS_PREFS_EXPORT Observer {
+   public:
+    // Called when the value for the given |key| in the store changes.
+    virtual void OnPrefValueChanged(const std::string& key) = 0;
+    // Notification about the PrefStore being fully initialized.
+    virtual void OnInitializationCompleted(bool succeeded) = 0;
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  PrefStore() {}
+
+  // Add and remove observers.
+  virtual void AddObserver(Observer* observer) {}
+  virtual void RemoveObserver(Observer* observer) {}
+  virtual bool HasObservers() const;
+
+  // Whether the store has completed all asynchronous initialization.
+  virtual bool IsInitializationComplete() const;
+
+  // Get the value for a given preference |key| and stores it in |*result|.
+  // |*result| is only modified if the return value is true and if |result|
+  // is not NULL. Ownership of the |*result| value remains with the PrefStore.
+  virtual bool GetValue(const std::string& key,
+                        const base::Value** result) const = 0;
+
+  // Get all the values. Never returns a null pointer.
+  virtual std::unique_ptr<base::DictionaryValue> GetValues() const = 0;
+
+ protected:
+  friend class base::RefCounted<PrefStore>;
+  virtual ~PrefStore() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_STORE_H_
diff --git a/src/updater_components/prefs/pref_store_observer_mock.cc b/src/updater_components/prefs/pref_store_observer_mock.cc
new file mode 100644
index 0000000..a03c858
--- /dev/null
+++ b/src/updater_components/prefs/pref_store_observer_mock.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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 "components/prefs/pref_store_observer_mock.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+PrefStoreObserverMock::PrefStoreObserverMock()
+    : initialized(false), initialization_success(false) {}
+
+PrefStoreObserverMock::~PrefStoreObserverMock() {}
+
+void PrefStoreObserverMock::VerifyAndResetChangedKey(
+    const std::string& expected) {
+  EXPECT_EQ(1u, changed_keys.size());
+  if (changed_keys.size() >= 1)
+    EXPECT_EQ(expected, changed_keys.front());
+  changed_keys.clear();
+}
+
+void PrefStoreObserverMock::OnPrefValueChanged(const std::string& key) {
+  changed_keys.push_back(key);
+}
+
+void PrefStoreObserverMock::OnInitializationCompleted(bool success) {
+  initialized = true;
+  initialization_success = success;
+}
diff --git a/src/updater_components/prefs/pref_store_observer_mock.h b/src/updater_components/prefs/pref_store_observer_mock.h
new file mode 100644
index 0000000..01bd6a6
--- /dev/null
+++ b/src/updater_components/prefs/pref_store_observer_mock.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_
+#define COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "components/prefs/pref_store.h"
+
+// A mock implementation of PrefStore::Observer.
+class PrefStoreObserverMock : public PrefStore::Observer {
+ public:
+  PrefStoreObserverMock();
+  ~PrefStoreObserverMock() override;
+
+  void VerifyAndResetChangedKey(const std::string& expected);
+
+  // PrefStore::Observer implementation
+  void OnPrefValueChanged(const std::string& key) override;
+  void OnInitializationCompleted(bool success) override;
+
+  std::vector<std::string> changed_keys;
+  bool initialized;
+  bool initialization_success;  // Only valid if |initialized|.
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_
diff --git a/src/updater_components/prefs/pref_value_map.cc b/src/updater_components/prefs/pref_value_map.cc
new file mode 100644
index 0000000..b932eef
--- /dev/null
+++ b/src/updater_components/prefs/pref_value_map.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2011 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 "components/prefs/pref_value_map.h"
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/values.h"
+
+PrefValueMap::PrefValueMap() {}
+
+PrefValueMap::~PrefValueMap() {}
+
+bool PrefValueMap::GetValue(const std::string& key,
+                            const base::Value** value) const {
+  auto it = prefs_.find(key);
+  if (it == prefs_.end())
+    return false;
+
+  if (value)
+    *value = &it->second;
+
+  return true;
+}
+
+bool PrefValueMap::GetValue(const std::string& key, base::Value** value) {
+  auto it = prefs_.find(key);
+  if (it == prefs_.end())
+    return false;
+
+  if (value)
+    *value = &it->second;
+
+  return true;
+}
+
+bool PrefValueMap::SetValue(const std::string& key, base::Value value) {
+  base::Value& existing_value = prefs_[key];
+  if (value == existing_value)
+    return false;
+
+  existing_value = std::move(value);
+  return true;
+}
+
+bool PrefValueMap::RemoveValue(const std::string& key) {
+  return prefs_.erase(key) != 0;
+}
+
+void PrefValueMap::Clear() {
+  prefs_.clear();
+}
+
+void PrefValueMap::Swap(PrefValueMap* other) {
+  prefs_.swap(other->prefs_);
+}
+
+PrefValueMap::iterator PrefValueMap::begin() {
+  return prefs_.begin();
+}
+
+PrefValueMap::iterator PrefValueMap::end() {
+  return prefs_.end();
+}
+
+PrefValueMap::const_iterator PrefValueMap::begin() const {
+  return prefs_.begin();
+}
+
+PrefValueMap::const_iterator PrefValueMap::end() const {
+  return prefs_.end();
+}
+
+bool PrefValueMap::empty() const {
+  return prefs_.empty();
+}
+
+bool PrefValueMap::GetBoolean(const std::string& key,
+                              bool* value) const {
+  const base::Value* stored_value = nullptr;
+  return GetValue(key, &stored_value) && stored_value->GetAsBoolean(value);
+}
+
+void PrefValueMap::SetBoolean(const std::string& key, bool value) {
+  SetValue(key, base::Value(value));
+}
+
+bool PrefValueMap::GetString(const std::string& key,
+                             std::string* value) const {
+  const base::Value* stored_value = nullptr;
+  return GetValue(key, &stored_value) && stored_value->GetAsString(value);
+}
+
+void PrefValueMap::SetString(const std::string& key,
+                             const std::string& value) {
+  SetValue(key, base::Value(value));
+}
+
+bool PrefValueMap::GetInteger(const std::string& key, int* value) const {
+  const base::Value* stored_value = nullptr;
+  return GetValue(key, &stored_value) && stored_value->GetAsInteger(value);
+}
+
+void PrefValueMap::SetInteger(const std::string& key, const int value) {
+  SetValue(key, base::Value(value));
+}
+
+void PrefValueMap::SetDouble(const std::string& key, const double value) {
+  SetValue(key, base::Value(value));
+}
+
+void PrefValueMap::GetDifferingKeys(
+    const PrefValueMap* other,
+    std::vector<std::string>* differing_keys) const {
+  differing_keys->clear();
+
+  // Put everything into ordered maps.
+  std::map<std::string, const base::Value*> this_prefs;
+  std::map<std::string, const base::Value*> other_prefs;
+  for (const auto& pair : prefs_)
+    this_prefs.emplace(pair.first, &pair.second);
+  for (const auto& pair : other->prefs_)
+    other_prefs.emplace(pair.first, &pair.second);
+
+  // Walk over the maps in lockstep, adding everything that is different.
+  auto this_pref = this_prefs.begin();
+  auto other_pref = other_prefs.begin();
+  while (this_pref != this_prefs.end() && other_pref != other_prefs.end()) {
+    const int diff = this_pref->first.compare(other_pref->first);
+    if (diff == 0) {
+      if (!this_pref->second->Equals(other_pref->second))
+        differing_keys->push_back(this_pref->first);
+      ++this_pref;
+      ++other_pref;
+    } else if (diff < 0) {
+      differing_keys->push_back(this_pref->first);
+      ++this_pref;
+    } else if (diff > 0) {
+      differing_keys->push_back(other_pref->first);
+      ++other_pref;
+    }
+  }
+
+  // Add the remaining entries.
+  for ( ; this_pref != this_prefs.end(); ++this_pref)
+    differing_keys->push_back(this_pref->first);
+  for ( ; other_pref != other_prefs.end(); ++other_pref)
+    differing_keys->push_back(other_pref->first);
+}
+
+std::unique_ptr<base::DictionaryValue> PrefValueMap::AsDictionaryValue() const {
+  auto dictionary = std::make_unique<base::DictionaryValue>();
+  for (const auto& value : prefs_)
+    dictionary->Set(value.first, value.second.CreateDeepCopy());
+
+  return dictionary;
+}
diff --git a/src/updater_components/prefs/pref_value_map.h b/src/updater_components/prefs/pref_value_map.h
new file mode 100644
index 0000000..6a9b4b6
--- /dev/null
+++ b/src/updater_components/prefs/pref_value_map.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_VALUE_MAP_H_
+#define COMPONENTS_PREFS_PREF_VALUE_MAP_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/prefs/prefs_export.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+}
+
+// A generic string to value map used by the PrefStore implementations.
+class COMPONENTS_PREFS_EXPORT PrefValueMap {
+ public:
+  using Map = std::map<std::string, base::Value>;
+  using iterator = Map::iterator;
+  using const_iterator = Map::const_iterator;
+
+  PrefValueMap();
+  virtual ~PrefValueMap();
+
+  // Gets the value for |key| and stores it in |value|. Ownership remains with
+  // the map. Returns true if a value is present. If not, |value| is not
+  // touched.
+  bool GetValue(const std::string& key, const base::Value** value) const;
+  bool GetValue(const std::string& key, base::Value** value);
+
+  // Sets a new |value| for |key|. Returns true if the value changed.
+  bool SetValue(const std::string& key, base::Value value);
+
+  // Removes the value for |key| from the map. Returns true if a value was
+  // removed.
+  bool RemoveValue(const std::string& key);
+
+  // Clears the map.
+  void Clear();
+
+  // Swaps the contents of two maps.
+  void Swap(PrefValueMap* other);
+
+  iterator begin();
+  iterator end();
+  const_iterator begin() const;
+  const_iterator end() const;
+  bool empty() const;
+
+  // Gets a boolean value for |key| and stores it in |value|. Returns true if
+  // the value was found and of the proper type.
+  bool GetBoolean(const std::string& key, bool* value) const;
+
+  // Sets the value for |key| to the boolean |value|.
+  void SetBoolean(const std::string& key, bool value);
+
+  // Gets a string value for |key| and stores it in |value|. Returns true if
+  // the value was found and of the proper type.
+  bool GetString(const std::string& key, std::string* value) const;
+
+  // Sets the value for |key| to the string |value|.
+  void SetString(const std::string& key, const std::string& value);
+
+  // Gets an int value for |key| and stores it in |value|. Returns true if
+  // the value was found and of the proper type.
+  bool GetInteger(const std::string& key, int* value) const;
+
+  // Sets the value for |key| to the int |value|.
+  void SetInteger(const std::string& key, const int value);
+
+  // Sets the value for |key| to the double |value|.
+  void SetDouble(const std::string& key, const double value);
+
+  // Compares this value map against |other| and stores all key names that have
+  // different values in |differing_keys|. This includes keys that are present
+  // only in one of the maps.
+  void GetDifferingKeys(const PrefValueMap* other,
+                        std::vector<std::string>* differing_keys) const;
+
+  // Copies the map into a dictionary value.
+  std::unique_ptr<base::DictionaryValue> AsDictionaryValue() const;
+
+ private:
+  Map prefs_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefValueMap);
+};
+
+#endif  // COMPONENTS_PREFS_PREF_VALUE_MAP_H_
diff --git a/src/updater_components/prefs/pref_value_map_unittest.cc b/src/updater_components/prefs/pref_value_map_unittest.cc
new file mode 100644
index 0000000..7d2f05c
--- /dev/null
+++ b/src/updater_components/prefs/pref_value_map_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2011 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 "components/prefs/pref_value_map.h"
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(PrefValueMapTest, SetValue) {
+  PrefValueMap map;
+  const Value* result = nullptr;
+  EXPECT_FALSE(map.GetValue("key", &result));
+  EXPECT_FALSE(result);
+
+  EXPECT_TRUE(map.SetValue("key", Value("test")));
+  EXPECT_FALSE(map.SetValue("key", Value("test")));
+  EXPECT_TRUE(map.SetValue("key", Value("hi mom!")));
+
+  EXPECT_TRUE(map.GetValue("key", &result));
+  EXPECT_TRUE(Value("hi mom!").Equals(result));
+}
+
+TEST(PrefValueMapTest, GetAndSetIntegerValue) {
+  PrefValueMap map;
+  ASSERT_TRUE(map.SetValue("key", Value(5)));
+
+  int int_value = 0;
+  EXPECT_TRUE(map.GetInteger("key", &int_value));
+  EXPECT_EQ(5, int_value);
+
+  map.SetInteger("key", -14);
+  EXPECT_TRUE(map.GetInteger("key", &int_value));
+  EXPECT_EQ(-14, int_value);
+}
+
+TEST(PrefValueMapTest, SetDoubleValue) {
+  PrefValueMap map;
+  ASSERT_TRUE(map.SetValue("key", Value(5.5)));
+
+  const Value* result = nullptr;
+  ASSERT_TRUE(map.GetValue("key", &result));
+  double double_value = 0.;
+  EXPECT_TRUE(result->GetAsDouble(&double_value));
+  EXPECT_DOUBLE_EQ(5.5, double_value);
+}
+
+TEST(PrefValueMapTest, RemoveValue) {
+  PrefValueMap map;
+  EXPECT_FALSE(map.RemoveValue("key"));
+
+  EXPECT_TRUE(map.SetValue("key", Value("test")));
+  EXPECT_TRUE(map.GetValue("key", nullptr));
+
+  EXPECT_TRUE(map.RemoveValue("key"));
+  EXPECT_FALSE(map.GetValue("key", nullptr));
+
+  EXPECT_FALSE(map.RemoveValue("key"));
+}
+
+TEST(PrefValueMapTest, Clear) {
+  PrefValueMap map;
+  EXPECT_TRUE(map.SetValue("key", Value("test")));
+  EXPECT_TRUE(map.GetValue("key", nullptr));
+
+  map.Clear();
+
+  EXPECT_FALSE(map.GetValue("key", nullptr));
+}
+
+TEST(PrefValueMapTest, GetDifferingKeys) {
+  PrefValueMap reference;
+  EXPECT_TRUE(reference.SetValue("b", Value("test")));
+  EXPECT_TRUE(reference.SetValue("c", Value("test")));
+  EXPECT_TRUE(reference.SetValue("e", Value("test")));
+
+  PrefValueMap check;
+  std::vector<std::string> differing_paths;
+  std::vector<std::string> expected_differing_paths;
+
+  reference.GetDifferingKeys(&check, &differing_paths);
+  expected_differing_paths.push_back("b");
+  expected_differing_paths.push_back("c");
+  expected_differing_paths.push_back("e");
+  EXPECT_EQ(expected_differing_paths, differing_paths);
+
+  EXPECT_TRUE(check.SetValue("a", Value("test")));
+  EXPECT_TRUE(check.SetValue("c", Value("test")));
+  EXPECT_TRUE(check.SetValue("d", Value("test")));
+
+  reference.GetDifferingKeys(&check, &differing_paths);
+  expected_differing_paths.clear();
+  expected_differing_paths.push_back("a");
+  expected_differing_paths.push_back("b");
+  expected_differing_paths.push_back("d");
+  expected_differing_paths.push_back("e");
+  EXPECT_EQ(expected_differing_paths, differing_paths);
+}
+
+TEST(PrefValueMapTest, SwapTwoMaps) {
+  PrefValueMap first_map;
+  EXPECT_TRUE(first_map.SetValue("a", Value("test")));
+  EXPECT_TRUE(first_map.SetValue("b", Value("test")));
+  EXPECT_TRUE(first_map.SetValue("c", Value("test")));
+
+  PrefValueMap second_map;
+  EXPECT_TRUE(second_map.SetValue("d", Value("test")));
+  EXPECT_TRUE(second_map.SetValue("e", Value("test")));
+  EXPECT_TRUE(second_map.SetValue("f", Value("test")));
+
+  first_map.Swap(&second_map);
+
+  EXPECT_TRUE(first_map.GetValue("d", nullptr));
+  EXPECT_TRUE(first_map.GetValue("e", nullptr));
+  EXPECT_TRUE(first_map.GetValue("f", nullptr));
+
+  EXPECT_TRUE(second_map.GetValue("a", nullptr));
+  EXPECT_TRUE(second_map.GetValue("b", nullptr));
+  EXPECT_TRUE(second_map.GetValue("c", nullptr));
+}
+
+}  // namespace
+}  // namespace base
diff --git a/src/updater_components/prefs/pref_value_store.cc b/src/updater_components/prefs/pref_value_store.cc
new file mode 100644
index 0000000..a7aecdd
--- /dev/null
+++ b/src/updater_components/prefs/pref_value_store.cc
@@ -0,0 +1,312 @@
+// 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 "components/prefs/pref_value_store.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/pref_observer.h"
+
+PrefValueStore::PrefStoreKeeper::PrefStoreKeeper()
+    : pref_value_store_(nullptr), type_(PrefValueStore::INVALID_STORE) {}
+
+PrefValueStore::PrefStoreKeeper::~PrefStoreKeeper() {
+  if (pref_store_) {
+    pref_store_->RemoveObserver(this);
+    pref_store_ = nullptr;
+  }
+  pref_value_store_ = nullptr;
+}
+
+void PrefValueStore::PrefStoreKeeper::Initialize(
+    PrefValueStore* store,
+    PrefStore* pref_store,
+    PrefValueStore::PrefStoreType type) {
+  if (pref_store_) {
+    pref_store_->RemoveObserver(this);
+    DCHECK(!pref_store_->HasObservers());
+  }
+  type_ = type;
+  pref_value_store_ = store;
+  pref_store_ = pref_store;
+  if (pref_store_)
+    pref_store_->AddObserver(this);
+}
+
+void PrefValueStore::PrefStoreKeeper::OnPrefValueChanged(
+    const std::string& key) {
+  pref_value_store_->OnPrefValueChanged(type_, key);
+}
+
+void PrefValueStore::PrefStoreKeeper::OnInitializationCompleted(
+    bool succeeded) {
+  pref_value_store_->OnInitializationCompleted(type_, succeeded);
+}
+
+PrefValueStore::PrefValueStore(PrefStore* managed_prefs,
+                               PrefStore* supervised_user_prefs,
+                               PrefStore* extension_prefs,
+                               PrefStore* command_line_prefs,
+                               PrefStore* user_prefs,
+                               PrefStore* recommended_prefs,
+                               PrefStore* default_prefs,
+                               PrefNotifier* pref_notifier,
+                               std::unique_ptr<Delegate> delegate)
+    : pref_notifier_(pref_notifier),
+      initialization_failed_(false),
+      delegate_(std::move(delegate)) {
+  InitPrefStore(MANAGED_STORE, managed_prefs);
+  InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs);
+  InitPrefStore(EXTENSION_STORE, extension_prefs);
+  InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
+  InitPrefStore(USER_STORE, user_prefs);
+  InitPrefStore(RECOMMENDED_STORE, recommended_prefs);
+  InitPrefStore(DEFAULT_STORE, default_prefs);
+
+  CheckInitializationCompleted();
+  if (delegate_) {
+    delegate_->Init(managed_prefs, supervised_user_prefs, extension_prefs,
+                    command_line_prefs, user_prefs, recommended_prefs,
+                    default_prefs, pref_notifier);
+  }
+}
+
+PrefValueStore::~PrefValueStore() {}
+
+std::unique_ptr<PrefValueStore> PrefValueStore::CloneAndSpecialize(
+    PrefStore* managed_prefs,
+    PrefStore* supervised_user_prefs,
+    PrefStore* extension_prefs,
+    PrefStore* command_line_prefs,
+    PrefStore* user_prefs,
+    PrefStore* recommended_prefs,
+    PrefStore* default_prefs,
+    PrefNotifier* pref_notifier,
+    std::unique_ptr<Delegate> delegate) {
+  DCHECK(pref_notifier);
+  if (!managed_prefs)
+    managed_prefs = GetPrefStore(MANAGED_STORE);
+  if (!supervised_user_prefs)
+    supervised_user_prefs = GetPrefStore(SUPERVISED_USER_STORE);
+  if (!extension_prefs)
+    extension_prefs = GetPrefStore(EXTENSION_STORE);
+  if (!command_line_prefs)
+    command_line_prefs = GetPrefStore(COMMAND_LINE_STORE);
+  if (!user_prefs)
+    user_prefs = GetPrefStore(USER_STORE);
+  if (!recommended_prefs)
+    recommended_prefs = GetPrefStore(RECOMMENDED_STORE);
+  if (!default_prefs)
+    default_prefs = GetPrefStore(DEFAULT_STORE);
+
+  return std::make_unique<PrefValueStore>(
+      managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs,
+      user_prefs, recommended_prefs, default_prefs, pref_notifier,
+      std::move(delegate));
+}
+
+void PrefValueStore::set_callback(const PrefChangedCallback& callback) {
+  pref_changed_callback_ = callback;
+}
+
+bool PrefValueStore::GetValue(const std::string& name,
+                              base::Value::Type type,
+                              const base::Value** out_value) const {
+  // Check the |PrefStore|s in order of their priority from highest to lowest,
+  // looking for the first preference value with the given |name| and |type|.
+  for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
+    if (GetValueFromStoreWithType(name, type, static_cast<PrefStoreType>(i),
+                                  out_value))
+      return true;
+  }
+  return false;
+}
+
+bool PrefValueStore::GetRecommendedValue(const std::string& name,
+                                         base::Value::Type type,
+                                         const base::Value** out_value) const {
+  return GetValueFromStoreWithType(name, type, RECOMMENDED_STORE, out_value);
+}
+
+void PrefValueStore::NotifyPrefChanged(
+    const std::string& path,
+    PrefValueStore::PrefStoreType new_store) {
+  DCHECK(new_store != INVALID_STORE);
+  // A notification is sent when the pref value in any store changes. If this
+  // store is currently being overridden by a higher-priority store, the
+  // effective value of the pref will not have changed.
+  pref_notifier_->OnPreferenceChanged(path);
+  if (!pref_changed_callback_.is_null())
+    pref_changed_callback_.Run(path);
+}
+
+bool PrefValueStore::PrefValueInManagedStore(const std::string& name) const {
+  return PrefValueInStore(name, MANAGED_STORE);
+}
+
+bool PrefValueStore::PrefValueInSupervisedStore(const std::string& name) const {
+  return PrefValueInStore(name, SUPERVISED_USER_STORE);
+}
+
+bool PrefValueStore::PrefValueInExtensionStore(const std::string& name) const {
+  return PrefValueInStore(name, EXTENSION_STORE);
+}
+
+bool PrefValueStore::PrefValueInUserStore(const std::string& name) const {
+  return PrefValueInStore(name, USER_STORE);
+}
+
+bool PrefValueStore::PrefValueFromExtensionStore(
+    const std::string& name) const {
+  return ControllingPrefStoreForPref(name) == EXTENSION_STORE;
+}
+
+bool PrefValueStore::PrefValueFromUserStore(const std::string& name) const {
+  return ControllingPrefStoreForPref(name) == USER_STORE;
+}
+
+bool PrefValueStore::PrefValueFromRecommendedStore(
+    const std::string& name) const {
+  return ControllingPrefStoreForPref(name) == RECOMMENDED_STORE;
+}
+
+bool PrefValueStore::PrefValueFromDefaultStore(const std::string& name) const {
+  return ControllingPrefStoreForPref(name) == DEFAULT_STORE;
+}
+
+bool PrefValueStore::PrefValueUserModifiable(const std::string& name) const {
+  PrefStoreType effective_store = ControllingPrefStoreForPref(name);
+  return effective_store >= USER_STORE ||
+         effective_store == INVALID_STORE;
+}
+
+bool PrefValueStore::PrefValueExtensionModifiable(
+    const std::string& name) const {
+  PrefStoreType effective_store = ControllingPrefStoreForPref(name);
+  return effective_store >= EXTENSION_STORE ||
+         effective_store == INVALID_STORE;
+}
+
+void PrefValueStore::UpdateCommandLinePrefStore(PrefStore* command_line_prefs) {
+  InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
+  if (delegate_)
+    delegate_->UpdateCommandLinePrefStore(command_line_prefs);
+}
+
+bool PrefValueStore::IsInitializationComplete() const {
+  for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
+    const PrefStore* pref_store = GetPrefStore(static_cast<PrefStoreType>(i));
+    if (pref_store && !pref_store->IsInitializationComplete())
+      return false;
+  }
+  return true;
+}
+
+bool PrefValueStore::HasPrefStore(PrefStoreType type) const {
+  return GetPrefStore(type);
+}
+
+bool PrefValueStore::PrefValueInStore(
+    const std::string& name,
+    PrefValueStore::PrefStoreType store) const {
+  // Declare a temp Value* and call GetValueFromStore,
+  // ignoring the output value.
+  const base::Value* tmp_value = nullptr;
+  return GetValueFromStore(name, store, &tmp_value);
+}
+
+bool PrefValueStore::PrefValueInStoreRange(
+    const std::string& name,
+    PrefValueStore::PrefStoreType first_checked_store,
+    PrefValueStore::PrefStoreType last_checked_store) const {
+  if (first_checked_store > last_checked_store) {
+    NOTREACHED();
+    return false;
+  }
+
+  for (size_t i = first_checked_store;
+       i <= static_cast<size_t>(last_checked_store); ++i) {
+    if (PrefValueInStore(name, static_cast<PrefStoreType>(i)))
+      return true;
+  }
+  return false;
+}
+
+PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref(
+    const std::string& name) const {
+  for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
+    if (PrefValueInStore(name, static_cast<PrefStoreType>(i)))
+      return static_cast<PrefStoreType>(i);
+  }
+  return INVALID_STORE;
+}
+
+bool PrefValueStore::GetValueFromStore(const std::string& name,
+                                       PrefValueStore::PrefStoreType store_type,
+                                       const base::Value** out_value) const {
+  // Only return true if we find a value and it is the correct type, so stale
+  // values with the incorrect type will be ignored.
+  const PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(store_type));
+  if (store && store->GetValue(name, out_value))
+    return true;
+
+  // No valid value found for the given preference name: set the return value
+  // to false.
+  *out_value = nullptr;
+  return false;
+}
+
+bool PrefValueStore::GetValueFromStoreWithType(
+    const std::string& name,
+    base::Value::Type type,
+    PrefStoreType store,
+    const base::Value** out_value) const {
+  if (GetValueFromStore(name, store, out_value)) {
+    if ((*out_value)->type() == type)
+      return true;
+
+    LOG(WARNING) << "Expected type for " << name << " is " << type
+                 << " but got " << (*out_value)->type() << " in store "
+                 << store;
+  }
+
+  *out_value = nullptr;
+  return false;
+}
+
+void PrefValueStore::OnPrefValueChanged(PrefValueStore::PrefStoreType type,
+                                        const std::string& key) {
+  NotifyPrefChanged(key, type);
+}
+
+void PrefValueStore::OnInitializationCompleted(
+    PrefValueStore::PrefStoreType type, bool succeeded) {
+  if (initialization_failed_)
+    return;
+  if (!succeeded) {
+    initialization_failed_ = true;
+    pref_notifier_->OnInitializationCompleted(false);
+    return;
+  }
+  CheckInitializationCompleted();
+}
+
+void PrefValueStore::InitPrefStore(PrefValueStore::PrefStoreType type,
+                                   PrefStore* pref_store) {
+  pref_stores_[type].Initialize(this, pref_store, type);
+}
+
+void PrefValueStore::CheckInitializationCompleted() {
+  if (initialization_failed_)
+    return;
+  for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
+    scoped_refptr<PrefStore> store =
+        GetPrefStore(static_cast<PrefStoreType>(i));
+    if (store.get() && !store->IsInitializationComplete())
+      return;
+  }
+  pref_notifier_->OnInitializationCompleted(true);
+}
diff --git a/src/updater_components/prefs/pref_value_store.h b/src/updater_components/prefs/pref_value_store.h
new file mode 100644
index 0000000..70a92bc
--- /dev/null
+++ b/src/updater_components/prefs/pref_value_store.h
@@ -0,0 +1,318 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_PREF_VALUE_STORE_H_
+#define COMPONENTS_PREFS_PREF_VALUE_STORE_H_
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "components/prefs/pref_store.h"
+#include "components/prefs/prefs_export.h"
+
+class PersistentPrefStore;
+class PrefNotifier;
+class PrefRegistry;
+class PrefStore;
+
+// The PrefValueStore manages various sources of values for Preferences
+// (e.g., configuration policies, extensions, and user settings). It returns
+// the value of a Preference from the source with the highest priority, and
+// allows setting user-defined values for preferences that are not managed.
+//
+// Unless otherwise explicitly noted, all of the methods of this class must
+// be called on the UI thread.
+class COMPONENTS_PREFS_EXPORT PrefValueStore {
+ public:
+  typedef base::Callback<void(const std::string&)> PrefChangedCallback;
+
+  // Delegate used to observe certain events in the |PrefValueStore|'s lifetime.
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    // Called by the PrefValueStore constructor with the PrefStores passed to
+    // it.
+    virtual void Init(PrefStore* managed_prefs,
+                      PrefStore* supervised_user_prefs,
+                      PrefStore* extension_prefs,
+                      PrefStore* command_line_prefs,
+                      PrefStore* user_prefs,
+                      PrefStore* recommended_prefs,
+                      PrefStore* default_prefs,
+                      PrefNotifier* pref_notifier) = 0;
+
+    virtual void InitIncognitoUserPrefs(
+        scoped_refptr<PersistentPrefStore> incognito_user_prefs_overlay,
+        scoped_refptr<PersistentPrefStore> incognito_user_prefs_underlay,
+        const std::vector<const char*>& overlay_pref_names) = 0;
+
+    virtual void InitPrefRegistry(PrefRegistry* pref_registry) = 0;
+
+    // Called whenever PrefValueStore::UpdateCommandLinePrefStore is called,
+    // with the same argument.
+    virtual void UpdateCommandLinePrefStore(PrefStore* command_line_prefs) = 0;
+  };
+
+  // PrefStores must be listed here in order from highest to lowest priority.
+  //   MANAGED contains all managed preference values that are provided by
+  //      mandatory policies (e.g. Windows Group Policy or cloud policy).
+  //   SUPERVISED_USER contains preferences that are valid for supervised users.
+  //   EXTENSION contains preference values set by extensions.
+  //   COMMAND_LINE contains preference values set by command-line switches.
+  //   USER contains all user-set preference values.
+  //   RECOMMENDED contains all preferences that are provided by recommended
+  //      policies.
+  //   DEFAULT contains all application default preference values.
+  enum PrefStoreType {
+    // INVALID_STORE is not associated with an actual PrefStore but used as
+    // an invalid marker, e.g. as a return value.
+    INVALID_STORE = -1,
+    MANAGED_STORE = 0,
+    SUPERVISED_USER_STORE,
+    EXTENSION_STORE,
+    COMMAND_LINE_STORE,
+    USER_STORE,
+    RECOMMENDED_STORE,
+    DEFAULT_STORE,
+    PREF_STORE_TYPE_MAX = DEFAULT_STORE
+  };
+
+  // In decreasing order of precedence:
+  //   |managed_prefs| contains all preferences from mandatory policies.
+  //   |supervised_user_prefs| contains all preferences from supervised user
+  //        settings, i.e. settings configured for a supervised user by their
+  //        custodian.
+  //   |extension_prefs| contains preference values set by extensions.
+  //   |command_line_prefs| contains preference values set by command-line
+  //        switches.
+  //   |user_prefs| contains all user-set preference values.
+  //   |recommended_prefs| contains all preferences from recommended policies.
+  //   |default_prefs| contains application-default preference values. It must
+  //        be non-null if any preferences are to be registered.
+  //
+  // |pref_notifier| facilitates broadcasting preference change notifications
+  // to the world.
+  PrefValueStore(PrefStore* managed_prefs,
+                 PrefStore* supervised_user_prefs,
+                 PrefStore* extension_prefs,
+                 PrefStore* command_line_prefs,
+                 PrefStore* user_prefs,
+                 PrefStore* recommended_prefs,
+                 PrefStore* default_prefs,
+                 PrefNotifier* pref_notifier,
+                 std::unique_ptr<Delegate> delegate = nullptr);
+  virtual ~PrefValueStore();
+
+  // Creates a clone of this PrefValueStore with PrefStores overwritten
+  // by the parameters passed, if unequal NULL.
+  //
+  // The new PrefValueStore is passed the |delegate| in its constructor.
+  std::unique_ptr<PrefValueStore> CloneAndSpecialize(
+      PrefStore* managed_prefs,
+      PrefStore* supervised_user_prefs,
+      PrefStore* extension_prefs,
+      PrefStore* command_line_prefs,
+      PrefStore* user_prefs,
+      PrefStore* recommended_prefs,
+      PrefStore* default_prefs,
+      PrefNotifier* pref_notifier,
+      std::unique_ptr<Delegate> delegate = nullptr);
+
+  // A PrefValueStore can have exactly one callback that is directly
+  // notified of preferences changing in the store. This does not
+  // filter through the PrefNotifier mechanism, which may not forward
+  // certain changes (e.g. unregistered prefs).
+  void set_callback(const PrefChangedCallback& callback);
+
+  // Gets the value for the given preference name that has the specified value
+  // type. Values stored in a PrefStore that have the matching |name| but
+  // a non-matching |type| are silently skipped. Returns true if a valid value
+  // was found in any of the available PrefStores. Most callers should use
+  // Preference::GetValue() instead of calling this method directly.
+  bool GetValue(const std::string& name,
+                base::Value::Type type,
+                const base::Value** out_value) const;
+
+  // Gets the recommended value for the given preference name that has the
+  // specified value type. A value stored in the recommended PrefStore that has
+  // the matching |name| but a non-matching |type| is silently ignored. Returns
+  // true if a valid value was found. Most callers should use
+  // Preference::GetRecommendedValue() instead of calling this method directly.
+  bool GetRecommendedValue(const std::string& name,
+                           base::Value::Type type,
+                           const base::Value** out_value) const;
+
+  // These methods return true if a preference with the given name is in the
+  // indicated pref store, even if that value is currently being overridden by
+  // a higher-priority source.
+  bool PrefValueInManagedStore(const std::string& name) const;
+  bool PrefValueInSupervisedStore(const std::string& name) const;
+  bool PrefValueInExtensionStore(const std::string& name) const;
+  bool PrefValueInUserStore(const std::string& name) const;
+
+  // These methods return true if a preference with the given name is actually
+  // being controlled by the indicated pref store and not being overridden by
+  // a higher-priority source.
+  bool PrefValueFromExtensionStore(const std::string& name) const;
+  bool PrefValueFromUserStore(const std::string& name) const;
+  bool PrefValueFromRecommendedStore(const std::string& name) const;
+  bool PrefValueFromDefaultStore(const std::string& name) const;
+
+  // Check whether a Preference value is modifiable by the user, i.e. whether
+  // there is no higher-priority source controlling it.
+  bool PrefValueUserModifiable(const std::string& name) const;
+
+  // Check whether a Preference value is modifiable by an extension, i.e.
+  // whether there is no higher-priority source controlling it.
+  bool PrefValueExtensionModifiable(const std::string& name) const;
+
+  // Update the command line PrefStore with |command_line_prefs|.
+  void UpdateCommandLinePrefStore(PrefStore* command_line_prefs);
+
+  bool IsInitializationComplete() const;
+
+  // Check whether a particular type of PrefStore exists.
+  bool HasPrefStore(PrefStoreType type) const;
+
+ private:
+  // Keeps a PrefStore reference on behalf of the PrefValueStore and monitors
+  // the PrefStore for changes, forwarding notifications to PrefValueStore. This
+  // indirection is here for the sake of disambiguating notifications from the
+  // individual PrefStores.
+  class PrefStoreKeeper : public PrefStore::Observer {
+   public:
+    PrefStoreKeeper();
+    ~PrefStoreKeeper() override;
+
+    // Takes ownership of |pref_store|.
+    void Initialize(PrefValueStore* store,
+                    PrefStore* pref_store,
+                    PrefStoreType type);
+
+    PrefStore* store() { return pref_store_.get(); }
+    const PrefStore* store() const { return pref_store_.get(); }
+
+   private:
+    // PrefStore::Observer implementation.
+    void OnPrefValueChanged(const std::string& key) override;
+    void OnInitializationCompleted(bool succeeded) override;
+
+    // PrefValueStore this keeper is part of.
+    PrefValueStore* pref_value_store_;
+
+    // The PrefStore managed by this keeper.
+    scoped_refptr<PrefStore> pref_store_;
+
+    // Type of the pref store.
+    PrefStoreType type_;
+
+    DISALLOW_COPY_AND_ASSIGN(PrefStoreKeeper);
+  };
+
+  typedef std::map<std::string, base::Value::Type> PrefTypeMap;
+
+  // Returns true if the preference with the given name has a value in the
+  // given PrefStoreType, of the same value type as the preference was
+  // registered with.
+  bool PrefValueInStore(const std::string& name, PrefStoreType store) const;
+
+  // Returns true if a preference has an explicit value in any of the
+  // stores in the range specified by |first_checked_store| and
+  // |last_checked_store|, even if that value is currently being
+  // overridden by a higher-priority store.
+  bool PrefValueInStoreRange(const std::string& name,
+                             PrefStoreType first_checked_store,
+                             PrefStoreType last_checked_store) const;
+
+  // Returns the pref store type identifying the source that controls the
+  // Preference identified by |name|. If none of the sources has a value,
+  // INVALID_STORE is returned. In practice, the default PrefStore
+  // should always have a value for any registered preferencem, so INVALID_STORE
+  // indicates an error.
+  PrefStoreType ControllingPrefStoreForPref(const std::string& name) const;
+
+  // Get a value from the specified |store|.
+  bool GetValueFromStore(const std::string& name,
+                         PrefStoreType store,
+                         const base::Value** out_value) const;
+
+  // Get a value from the specified |store| if its |type| matches.
+  bool GetValueFromStoreWithType(const std::string& name,
+                                 base::Value::Type type,
+                                 PrefStoreType store,
+                                 const base::Value** out_value) const;
+
+  // Called upon changes in individual pref stores in order to determine whether
+  // the user-visible pref value has changed. Triggers the change notification
+  // if the effective value of the preference has changed, or if the store
+  // controlling the pref has changed.
+  void NotifyPrefChanged(const std::string& path, PrefStoreType new_store);
+
+  // Called from the PrefStoreKeeper implementation when a pref value for |key|
+  // changed in the pref store for |type|.
+  void OnPrefValueChanged(PrefStoreType type, const std::string& key);
+
+  // Handle the event that the store for |type| has completed initialization.
+  void OnInitializationCompleted(PrefStoreType type, bool succeeded);
+
+  // Initializes a pref store keeper. Sets up a PrefStoreKeeper that will take
+  // ownership of the passed |pref_store|.
+  void InitPrefStore(PrefStoreType type, PrefStore* pref_store);
+
+  // Checks whether initialization is completed and tells the notifier if that
+  // is the case.
+  void CheckInitializationCompleted();
+
+  // Get the PrefStore pointer for the given type. May return NULL if there is
+  // no PrefStore for that type.
+  PrefStore* GetPrefStore(PrefStoreType type) {
+    return pref_stores_[type].store();
+  }
+  const PrefStore* GetPrefStore(PrefStoreType type) const {
+    return pref_stores_[type].store();
+  }
+
+  // Keeps the PrefStore references in order of precedence.
+  PrefStoreKeeper pref_stores_[PREF_STORE_TYPE_MAX + 1];
+
+  PrefChangedCallback pref_changed_callback_;
+
+  // Used for generating notifications. This is a weak reference,
+  // since the notifier is owned by the corresponding PrefService.
+  PrefNotifier* pref_notifier_;
+
+  // A mapping of preference names to their registered types.
+  PrefTypeMap pref_types_;
+
+  // True if not all of the PrefStores were initialized successfully.
+  bool initialization_failed_;
+
+  // Might be null.
+  std::unique_ptr<Delegate> delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefValueStore);
+};
+
+namespace std {
+
+template <>
+struct hash<PrefValueStore::PrefStoreType> {
+  size_t operator()(PrefValueStore::PrefStoreType type) const {
+    return std::hash<
+        std::underlying_type<PrefValueStore::PrefStoreType>::type>()(type);
+  }
+};
+
+}  // namespace std
+
+#endif  // COMPONENTS_PREFS_PREF_VALUE_STORE_H_
diff --git a/src/updater_components/prefs/pref_value_store_unittest.cc b/src/updater_components/prefs/pref_value_store_unittest.cc
new file mode 100644
index 0000000..bbbeed8
--- /dev/null
+++ b/src/updater_components/prefs/pref_value_store_unittest.cc
@@ -0,0 +1,671 @@
+// 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 "components/prefs/pref_value_store.h"
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/testing_pref_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::_;
+
+namespace {
+
+// Allows to capture pref notifications through gmock.
+class MockPrefNotifier : public PrefNotifier {
+ public:
+  MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
+  MOCK_METHOD1(OnInitializationCompleted, void(bool));
+};
+
+// Allows to capture sync model associator interaction.
+class MockPrefModelAssociator {
+ public:
+  MOCK_METHOD1(ProcessPrefChange, void(const std::string&));
+};
+
+}  // namespace
+
+// Names of the preferences used in this test.
+namespace prefs {
+const char kManagedPref[] = "this.pref.managed";
+const char kSupervisedUserPref[] = "this.pref.supervised_user";
+const char kCommandLinePref[] = "this.pref.command_line";
+const char kExtensionPref[] = "this.pref.extension";
+const char kUserPref[] = "this.pref.user";
+const char kRecommendedPref[] = "this.pref.recommended";
+const char kDefaultPref[] = "this.pref.default";
+const char kMissingPref[] = "this.pref.does_not_exist";
+}
+
+// Potentially expected values of all preferences used in this test program.
+namespace managed_pref {
+const char kManagedValue[] = "managed:managed";
+}
+
+namespace supervised_user_pref {
+const char kManagedValue[] = "supervised_user:managed";
+const char kSupervisedUserValue[] = "supervised_user:supervised_user";
+}
+
+namespace extension_pref {
+const char kManagedValue[] = "extension:managed";
+const char kSupervisedUserValue[] = "extension:supervised_user";
+const char kExtensionValue[] = "extension:extension";
+}
+
+namespace command_line_pref {
+const char kManagedValue[] = "command_line:managed";
+const char kSupervisedUserValue[] = "command_line:supervised_user";
+const char kExtensionValue[] = "command_line:extension";
+const char kCommandLineValue[] = "command_line:command_line";
+}
+
+namespace user_pref {
+const char kManagedValue[] = "user:managed";
+const char kSupervisedUserValue[] = "supervised_user:supervised_user";
+const char kExtensionValue[] = "user:extension";
+const char kCommandLineValue[] = "user:command_line";
+const char kUserValue[] = "user:user";
+}
+
+namespace recommended_pref {
+const char kManagedValue[] = "recommended:managed";
+const char kSupervisedUserValue[] = "recommended:supervised_user";
+const char kExtensionValue[] = "recommended:extension";
+const char kCommandLineValue[] = "recommended:command_line";
+const char kUserValue[] = "recommended:user";
+const char kRecommendedValue[] = "recommended:recommended";
+}
+
+namespace default_pref {
+const char kManagedValue[] = "default:managed";
+const char kSupervisedUserValue[] = "default:supervised_user";
+const char kExtensionValue[] = "default:extension";
+const char kCommandLineValue[] = "default:command_line";
+const char kUserValue[] = "default:user";
+const char kRecommendedValue[] = "default:recommended";
+const char kDefaultValue[] = "default:default";
+}
+
+class PrefValueStoreTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    // Create TestingPrefStores.
+    CreateManagedPrefs();
+    CreateSupervisedUserPrefs();
+    CreateExtensionPrefs();
+    CreateCommandLinePrefs();
+    CreateUserPrefs();
+    CreateRecommendedPrefs();
+    CreateDefaultPrefs();
+    sync_associator_.reset(new MockPrefModelAssociator());
+
+    // Create a fresh PrefValueStore.
+    pref_value_store_.reset(
+        new PrefValueStore(managed_pref_store_.get(),
+                           supervised_user_pref_store_.get(),
+                           extension_pref_store_.get(),
+                           command_line_pref_store_.get(),
+                           user_pref_store_.get(),
+                           recommended_pref_store_.get(),
+                           default_pref_store_.get(),
+                           &pref_notifier_));
+
+    pref_value_store_->set_callback(
+        base::Bind(&MockPrefModelAssociator::ProcessPrefChange,
+                   base::Unretained(sync_associator_.get())));
+  }
+
+  void CreateManagedPrefs() {
+    managed_pref_store_ = new TestingPrefStore;
+    managed_pref_store_->SetString(
+        prefs::kManagedPref,
+        managed_pref::kManagedValue);
+  }
+
+  void CreateSupervisedUserPrefs() {
+    supervised_user_pref_store_ = new TestingPrefStore;
+    supervised_user_pref_store_->SetString(
+        prefs::kManagedPref,
+        supervised_user_pref::kManagedValue);
+    supervised_user_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        supervised_user_pref::kSupervisedUserValue);
+  }
+
+  void CreateExtensionPrefs() {
+    extension_pref_store_ = new TestingPrefStore;
+    extension_pref_store_->SetString(
+        prefs::kManagedPref,
+        extension_pref::kManagedValue);
+    extension_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        extension_pref::kSupervisedUserValue);
+    extension_pref_store_->SetString(
+        prefs::kExtensionPref,
+        extension_pref::kExtensionValue);
+  }
+
+  void CreateCommandLinePrefs() {
+    command_line_pref_store_ = new TestingPrefStore;
+    command_line_pref_store_->SetString(
+        prefs::kManagedPref,
+        command_line_pref::kManagedValue);
+    command_line_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        command_line_pref::kSupervisedUserValue);
+    command_line_pref_store_->SetString(
+        prefs::kExtensionPref,
+        command_line_pref::kExtensionValue);
+    command_line_pref_store_->SetString(
+        prefs::kCommandLinePref,
+        command_line_pref::kCommandLineValue);
+  }
+
+  void CreateUserPrefs() {
+    user_pref_store_ = new TestingPrefStore;
+    user_pref_store_->SetString(
+        prefs::kManagedPref,
+        user_pref::kManagedValue);
+    user_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        user_pref::kSupervisedUserValue);
+    user_pref_store_->SetString(
+        prefs::kCommandLinePref,
+        user_pref::kCommandLineValue);
+    user_pref_store_->SetString(
+        prefs::kExtensionPref,
+        user_pref::kExtensionValue);
+    user_pref_store_->SetString(
+        prefs::kUserPref,
+        user_pref::kUserValue);
+  }
+
+  void CreateRecommendedPrefs() {
+    recommended_pref_store_ = new TestingPrefStore;
+    recommended_pref_store_->SetString(
+        prefs::kManagedPref,
+        recommended_pref::kManagedValue);
+    recommended_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        recommended_pref::kSupervisedUserValue);
+    recommended_pref_store_->SetString(
+        prefs::kCommandLinePref,
+        recommended_pref::kCommandLineValue);
+    recommended_pref_store_->SetString(
+        prefs::kExtensionPref,
+        recommended_pref::kExtensionValue);
+    recommended_pref_store_->SetString(
+        prefs::kUserPref,
+        recommended_pref::kUserValue);
+    recommended_pref_store_->SetString(
+        prefs::kRecommendedPref,
+        recommended_pref::kRecommendedValue);
+  }
+
+  void CreateDefaultPrefs() {
+    default_pref_store_ = new TestingPrefStore;
+    default_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        default_pref::kSupervisedUserValue);
+    default_pref_store_->SetString(
+        prefs::kManagedPref,
+        default_pref::kManagedValue);
+    default_pref_store_->SetString(
+        prefs::kCommandLinePref,
+        default_pref::kCommandLineValue);
+    default_pref_store_->SetString(
+        prefs::kExtensionPref,
+        default_pref::kExtensionValue);
+    default_pref_store_->SetString(
+        prefs::kUserPref,
+        default_pref::kUserValue);
+    default_pref_store_->SetString(
+        prefs::kRecommendedPref,
+        default_pref::kRecommendedValue);
+    default_pref_store_->SetString(
+        prefs::kDefaultPref,
+        default_pref::kDefaultValue);
+  }
+
+  void ExpectValueChangeNotifications(const std::string& name) {
+    EXPECT_CALL(pref_notifier_, OnPreferenceChanged(name));
+    EXPECT_CALL(*sync_associator_, ProcessPrefChange(name));
+  }
+
+  void CheckAndClearValueChangeNotifications() {
+    Mock::VerifyAndClearExpectations(&pref_notifier_);
+    Mock::VerifyAndClearExpectations(sync_associator_.get());
+  }
+
+  MockPrefNotifier pref_notifier_;
+  std::unique_ptr<MockPrefModelAssociator> sync_associator_;
+  std::unique_ptr<PrefValueStore> pref_value_store_;
+
+  scoped_refptr<TestingPrefStore> managed_pref_store_;
+  scoped_refptr<TestingPrefStore> supervised_user_pref_store_;
+  scoped_refptr<TestingPrefStore> extension_pref_store_;
+  scoped_refptr<TestingPrefStore> command_line_pref_store_;
+  scoped_refptr<TestingPrefStore> user_pref_store_;
+  scoped_refptr<TestingPrefStore> recommended_pref_store_;
+  scoped_refptr<TestingPrefStore> default_pref_store_;
+};
+
+TEST_F(PrefValueStoreTest, GetValue) {
+  const base::Value* value;
+
+  // The following tests read a value from the PrefService. The preferences are
+  // set in a way such that all lower-priority stores have a value and we can
+  // test whether overrides work correctly.
+
+  // Test getting a managed value.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kManagedPref,
+                                          base::Value::Type::STRING, &value));
+  std::string actual_str_value;
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(managed_pref::kManagedValue, actual_str_value);
+
+  // Test getting a supervised user value.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kSupervisedUserPref,
+                                          base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(supervised_user_pref::kSupervisedUserValue, actual_str_value);
+
+  // Test getting an extension value.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kExtensionPref,
+                                          base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(extension_pref::kExtensionValue, actual_str_value);
+
+  // Test getting a command-line value.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kCommandLinePref,
+                                          base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(command_line_pref::kCommandLineValue, actual_str_value);
+
+  // Test getting a user-set value.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kUserPref,
+                                          base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(user_pref::kUserValue, actual_str_value);
+
+  // Test getting a user set value overwriting a recommended value.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kRecommendedPref,
+                                          base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kRecommendedValue,
+            actual_str_value);
+
+  // Test getting a default value.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kDefaultPref,
+                                          base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(default_pref::kDefaultValue, actual_str_value);
+
+  // Test getting a preference value that the |PrefValueStore|
+  // does not contain.
+  base::Value tmp_dummy_value(true);
+  value = &tmp_dummy_value;
+  ASSERT_FALSE(pref_value_store_->GetValue(prefs::kMissingPref,
+                                           base::Value::Type::STRING, &value));
+  ASSERT_FALSE(value);
+}
+
+TEST_F(PrefValueStoreTest, GetRecommendedValue) {
+  const base::Value* value;
+
+  // The following tests read a value from the PrefService. The preferences are
+  // set in a way such that all lower-priority stores have a value and we can
+  // test whether overrides do not clutter the recommended value.
+
+  // Test getting recommended value when a managed value is present.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+      prefs::kManagedPref,
+      base::Value::Type::STRING, &value));
+  std::string actual_str_value;
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kManagedValue, actual_str_value);
+
+  // Test getting recommended value when a supervised user value is present.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+      prefs::kSupervisedUserPref,
+      base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kSupervisedUserValue, actual_str_value);
+
+  // Test getting recommended value when an extension value is present.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+      prefs::kExtensionPref,
+      base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kExtensionValue, actual_str_value);
+
+  // Test getting recommended value when a command-line value is present.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+      prefs::kCommandLinePref,
+      base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kCommandLineValue, actual_str_value);
+
+  // Test getting recommended value when a user-set value is present.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+      prefs::kUserPref,
+      base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kUserValue, actual_str_value);
+
+  // Test getting recommended value when no higher-priority value is present.
+  value = nullptr;
+  ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+      prefs::kRecommendedPref,
+      base::Value::Type::STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kRecommendedValue,
+            actual_str_value);
+
+  // Test getting recommended value when no recommended value is present.
+  base::Value tmp_dummy_value(true);
+  value = &tmp_dummy_value;
+  ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
+      prefs::kDefaultPref,
+      base::Value::Type::STRING, &value));
+  ASSERT_FALSE(value);
+
+  // Test getting a preference value that the |PrefValueStore|
+  // does not contain.
+  value = &tmp_dummy_value;
+  ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
+      prefs::kMissingPref,
+      base::Value::Type::STRING, &value));
+  ASSERT_FALSE(value);
+}
+
+TEST_F(PrefValueStoreTest, PrefChanges) {
+  // Check pref controlled by highest-priority store.
+  ExpectValueChangeNotifications(prefs::kManagedPref);
+  managed_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kManagedPref);
+  supervised_user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kManagedPref);
+  extension_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kManagedPref);
+  command_line_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kManagedPref);
+  user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kManagedPref);
+  recommended_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kManagedPref);
+  default_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  // Check pref controlled by user store.
+  ExpectValueChangeNotifications(prefs::kUserPref);
+  managed_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kUserPref);
+  extension_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kUserPref);
+  command_line_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kUserPref);
+  user_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kUserPref);
+  recommended_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kUserPref);
+  default_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+  CheckAndClearValueChangeNotifications();
+
+  // Check pref controlled by default-pref store.
+  ExpectValueChangeNotifications(prefs::kDefaultPref);
+  managed_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kDefaultPref);
+  extension_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kDefaultPref);
+  command_line_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kDefaultPref);
+  user_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kDefaultPref);
+  recommended_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kDefaultPref);
+  default_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+  CheckAndClearValueChangeNotifications();
+}
+
+TEST_F(PrefValueStoreTest, OnInitializationCompleted) {
+  EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(0);
+  managed_pref_store_->SetInitializationCompleted();
+  supervised_user_pref_store_->SetInitializationCompleted();
+  extension_pref_store_->SetInitializationCompleted();
+  command_line_pref_store_->SetInitializationCompleted();
+  recommended_pref_store_->SetInitializationCompleted();
+  default_pref_store_->SetInitializationCompleted();
+  Mock::VerifyAndClearExpectations(&pref_notifier_);
+
+  // The notification should only be triggered after the last store is done.
+  EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(1);
+  user_pref_store_->SetInitializationCompleted();
+  Mock::VerifyAndClearExpectations(&pref_notifier_);
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInManagedStore) {
+  EXPECT_TRUE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kExtensionPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kCommandLinePref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kRecommendedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kDefaultPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInExtensionStore) {
+  EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kManagedPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kExtensionPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kCommandLinePref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kRecommendedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kDefaultPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInUserStore) {
+  EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+      prefs::kManagedPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+      prefs::kExtensionPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+      prefs::kCommandLinePref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+      prefs::kUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(
+      prefs::kRecommendedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(
+      prefs::kDefaultPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromExtensionStore) {
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kExtensionPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kCommandLinePref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kRecommendedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kDefaultPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromUserStore) {
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kExtensionPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kCommandLinePref));
+  EXPECT_TRUE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kRecommendedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kDefaultPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromRecommendedStore) {
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kExtensionPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kCommandLinePref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kRecommendedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kDefaultPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromDefaultStore) {
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kExtensionPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kCommandLinePref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kRecommendedPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kDefaultPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueUserModifiable) {
+  EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kExtensionPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kCommandLinePref));
+  EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kRecommendedPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kDefaultPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueExtensionModifiable) {
+  EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kSupervisedUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kExtensionPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kCommandLinePref));
+  EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kRecommendedPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kDefaultPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kMissingPref));
+}
diff --git a/src/updater_components/prefs/prefs_export.h b/src/updater_components/prefs/prefs_export.h
new file mode 100644
index 0000000..e047ad9
--- /dev/null
+++ b/src/updater_components/prefs/prefs_export.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_
+#define COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(COMPONENTS_PREFS_IMPLEMENTATION)
+#define COMPONENTS_PREFS_EXPORT __declspec(dllexport)
+#else
+#define COMPONENTS_PREFS_EXPORT __declspec(dllimport)
+#endif  // defined(COMPONENTS_PREFS_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(COMPONENTS_PREFS_IMPLEMENTATION)
+#define COMPONENTS_PREFS_EXPORT __attribute__((visibility("default")))
+#else
+#define COMPONENTS_PREFS_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define COMPONENTS_PREFS_EXPORT
+#endif
+
+#endif  // COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_
diff --git a/src/updater_components/prefs/scoped_user_pref_update.cc b/src/updater_components/prefs/scoped_user_pref_update.cc
new file mode 100644
index 0000000..27d56af
--- /dev/null
+++ b/src/updater_components/prefs/scoped_user_pref_update.cc
@@ -0,0 +1,44 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/prefs/scoped_user_pref_update.h"
+
+#include "base/logging.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/pref_service.h"
+
+namespace subtle {
+
+ScopedUserPrefUpdateBase::ScopedUserPrefUpdateBase(PrefService* service,
+                                                   const std::string& path)
+    : service_(service), path_(path), value_(nullptr) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(service_->sequence_checker_);
+}
+
+ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  Notify();
+}
+
+base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!value_)
+    value_ = service_->GetMutableUserPref(path_, type);
+
+  // |value_| might be downcast to base::DictionaryValue or base::ListValue,
+  // side-stepping CHECKs built into base::Value. Thus we need to be certain
+  // that the type matches.
+  if (value_)
+    CHECK_EQ(value_->type(), type);
+  return value_;
+}
+
+void ScopedUserPrefUpdateBase::Notify() {
+  if (value_) {
+    service_->ReportUserPrefChanged(path_);
+    value_ = nullptr;
+  }
+}
+
+}  // namespace subtle
diff --git a/src/updater_components/prefs/scoped_user_pref_update.h b/src/updater_components/prefs/scoped_user_pref_update.h
new file mode 100644
index 0000000..7de6bfb
--- /dev/null
+++ b/src/updater_components/prefs/scoped_user_pref_update.h
@@ -0,0 +1,110 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A helper class that assists preferences in firing notifications when lists
+// or dictionaries are changed.
+
+#ifndef COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
+#define COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "base/values.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/prefs_export.h"
+
+class PrefService;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace subtle {
+
+// Base class for ScopedUserPrefUpdateTemplate that contains the parts
+// that do not depend on ScopedUserPrefUpdateTemplate's template parameter.
+//
+// We need this base class mostly for making it a friend of PrefService
+// and getting access to PrefService::GetMutableUserPref and
+// PrefService::ReportUserPrefChanged.
+class COMPONENTS_PREFS_EXPORT ScopedUserPrefUpdateBase {
+ protected:
+  ScopedUserPrefUpdateBase(PrefService* service, const std::string& path);
+
+  // Calls Notify().
+  ~ScopedUserPrefUpdateBase();
+
+  // Sets |value_| to |service_|->GetMutableUserPref and returns it.
+  base::Value* GetValueOfType(base::Value::Type type);
+
+ private:
+  // If |value_| is not null, triggers a notification of PrefObservers and
+  // resets |value_|.
+  void Notify();
+
+  // Weak pointer.
+  PrefService* service_;
+  // Path of the preference being updated.
+  std::string path_;
+  // Cache of value from user pref store (set between Get() and Notify() calls).
+  base::Value* value_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdateBase);
+};
+
+}  // namespace subtle
+
+// Class to support modifications to DictionaryValues and ListValues while
+// guaranteeing that PrefObservers are notified of changed values.
+//
+// This class may only be used on the UI thread as it requires access to the
+// PrefService.
+template <typename T, base::Value::Type type_enum_value>
+class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase {
+ public:
+  ScopedUserPrefUpdate(PrefService* service, const std::string& path)
+      : ScopedUserPrefUpdateBase(service, path) {}
+
+  // Triggers an update notification if Get() was called.
+  virtual ~ScopedUserPrefUpdate() {}
+
+  // Returns a mutable |T| instance that
+  // - is already in the user pref store, or
+  // - is (silently) created and written to the user pref store if none existed
+  //   before.
+  //
+  // Calling Get() implies that an update notification is necessary at
+  // destruction time.
+  //
+  // The ownership of the return value remains with the user pref store.
+  // Virtual so it can be overriden in subclasses that transform the value
+  // before returning it (for example to return a subelement of a dictionary).
+  virtual T* Get() {
+    return static_cast<T*>(GetValueOfType(type_enum_value));
+  }
+
+  T& operator*() {
+    return *Get();
+  }
+
+  T* operator->() {
+    return Get();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdate);
+};
+
+typedef ScopedUserPrefUpdate<base::DictionaryValue,
+                             base::Value::Type::DICTIONARY>
+    DictionaryPrefUpdate;
+typedef ScopedUserPrefUpdate<base::ListValue, base::Value::Type::LIST>
+    ListPrefUpdate;
+
+#endif  // COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
diff --git a/src/updater_components/prefs/scoped_user_pref_update_unittest.cc b/src/updater_components/prefs/scoped_user_pref_update_unittest.cc
new file mode 100644
index 0000000..fa1dc71
--- /dev/null
+++ b/src/updater_components/prefs/scoped_user_pref_update_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/prefs/mock_pref_change_callback.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+class ScopedUserPrefUpdateTest : public testing::Test {
+ public:
+  ScopedUserPrefUpdateTest() : observer_(&prefs_) {}
+  ~ScopedUserPrefUpdateTest() override {}
+
+ protected:
+  void SetUp() override {
+    prefs_.registry()->RegisterDictionaryPref(kPref);
+    registrar_.Init(&prefs_);
+    registrar_.Add(kPref, observer_.GetCallback());
+  }
+
+  static const char kPref[];
+  static const char kKey[];
+  static const char kValue[];
+
+  TestingPrefServiceSimple prefs_;
+  MockPrefChangeCallback observer_;
+  PrefChangeRegistrar registrar_;
+};
+
+const char ScopedUserPrefUpdateTest::kPref[] = "name";
+const char ScopedUserPrefUpdateTest::kKey[] = "key";
+const char ScopedUserPrefUpdateTest::kValue[] = "value";
+
+TEST_F(ScopedUserPrefUpdateTest, RegularUse) {
+  // Dictionary that will be expected to be set at the end.
+  base::DictionaryValue expected_dictionary;
+  expected_dictionary.SetString(kKey, kValue);
+
+  {
+    EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+    DictionaryPrefUpdate update(&prefs_, kPref);
+    base::DictionaryValue* value = update.Get();
+    ASSERT_TRUE(value);
+    value->SetString(kKey, kValue);
+
+    // The dictionary was created for us but the creation should have happened
+    // silently without notifications.
+    Mock::VerifyAndClearExpectations(&observer_);
+
+    // Modifications happen online and are instantly visible, though.
+    const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+    ASSERT_TRUE(current_value);
+    EXPECT_TRUE(expected_dictionary.Equals(current_value));
+
+    // Now we are leaving the scope of the update so we should be notified.
+    observer_.Expect(kPref, &expected_dictionary);
+  }
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+  ASSERT_TRUE(current_value);
+  EXPECT_TRUE(expected_dictionary.Equals(current_value));
+}
+
+TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) {
+  const base::DictionaryValue* old_value = prefs_.GetDictionary(kPref);
+  EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+  {
+    DictionaryPrefUpdate update(&prefs_, kPref);
+  }
+  const base::DictionaryValue* new_value = prefs_.GetDictionary(kPref);
+  EXPECT_EQ(old_value, new_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+}
+
+TEST_F(ScopedUserPrefUpdateTest, UpdatingListPrefWithDefaults) {
+  base::Value::ListStorage defaults;
+  defaults.emplace_back("firstvalue");
+  defaults.emplace_back("secondvalue");
+
+  std::string pref_name = "mypref";
+  prefs_.registry()->RegisterListPref(pref_name,
+                                      base::Value(std::move(defaults)));
+  EXPECT_EQ(2u, prefs_.GetList(pref_name)->GetList().size());
+
+  ListPrefUpdate update(&prefs_, pref_name);
+  update->AppendString("thirdvalue");
+  EXPECT_EQ(3u, prefs_.GetList(pref_name)->GetList().size());
+}
+
+TEST_F(ScopedUserPrefUpdateTest, UpdatingDictionaryPrefWithDefaults) {
+  base::Value defaults(base::Value::Type::DICTIONARY);
+  defaults.SetKey("firstkey", base::Value("value"));
+  defaults.SetKey("secondkey", base::Value("value"));
+
+  std::string pref_name = "mypref";
+  prefs_.registry()->RegisterDictionaryPref(pref_name, std::move(defaults));
+  EXPECT_EQ(2u, prefs_.GetDictionary(pref_name)->size());
+
+  DictionaryPrefUpdate update(&prefs_, pref_name);
+  update->SetKey("thirdkey", base::Value("value"));
+  EXPECT_EQ(3u, prefs_.GetDictionary(pref_name)->size());
+}
diff --git a/src/updater_components/prefs/testing_pref_service.cc b/src/updater_components/prefs/testing_pref_service.cc
new file mode 100644
index 0000000..aa6582d
--- /dev/null
+++ b/src/updater_components/prefs/testing_pref_service.cc
@@ -0,0 +1,59 @@
+// 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 "components/prefs/testing_pref_service.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_value_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+template <>
+TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
+    TestingPrefStore* managed_prefs,
+    TestingPrefStore* extension_prefs,
+    TestingPrefStore* user_prefs,
+    TestingPrefStore* recommended_prefs,
+    PrefRegistry* pref_registry,
+    PrefNotifierImpl* pref_notifier)
+    : PrefService(
+          std::unique_ptr<PrefNotifierImpl>(pref_notifier),
+          std::make_unique<PrefValueStore>(managed_prefs,
+                                           nullptr,
+                                           extension_prefs,
+                                           nullptr,
+                                           user_prefs,
+                                           recommended_prefs,
+                                           pref_registry->defaults().get(),
+                                           pref_notifier),
+          user_prefs,
+          pref_registry,
+          base::Bind(&TestingPrefServiceBase<PrefService,
+                                             PrefRegistry>::HandleReadError),
+          false),
+      managed_prefs_(managed_prefs),
+      extension_prefs_(extension_prefs),
+      user_prefs_(user_prefs),
+      recommended_prefs_(recommended_prefs) {}
+
+TestingPrefServiceSimple::TestingPrefServiceSimple()
+    : TestingPrefServiceBase<PrefService, PrefRegistry>(
+          new TestingPrefStore(),
+          new TestingPrefStore(),
+          new TestingPrefStore(),
+          new TestingPrefStore(),
+          new PrefRegistrySimple(),
+          new PrefNotifierImpl()) {}
+
+TestingPrefServiceSimple::~TestingPrefServiceSimple() {
+}
+
+PrefRegistrySimple* TestingPrefServiceSimple::registry() {
+  return static_cast<PrefRegistrySimple*>(DeprecatedGetPrefRegistry());
+}
diff --git a/src/updater_components/prefs/testing_pref_service.h b/src/updater_components/prefs/testing_pref_service.h
new file mode 100644
index 0000000..f10e0cb
--- /dev/null
+++ b/src/updater_components/prefs/testing_pref_service.h
@@ -0,0 +1,233 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_
+#define COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_store.h"
+
+class PrefNotifierImpl;
+class PrefRegistrySimple;
+class TestingPrefStore;
+
+// A PrefService subclass for testing. It operates totally in memory and
+// provides additional API for manipulating preferences at the different levels
+// (managed, extension, user) conveniently.
+//
+// Use this via its specializations, e.g. TestingPrefServiceSimple.
+template <class SuperPrefService, class ConstructionPrefRegistry>
+class TestingPrefServiceBase : public SuperPrefService {
+ public:
+  virtual ~TestingPrefServiceBase();
+
+  // Reads the value of a preference from the managed layer. Returns NULL if the
+  // preference is not defined at the managed layer.
+  const base::Value* GetManagedPref(const std::string& path) const;
+
+  // Sets a preference on the managed layer and fires observers if the
+  // preference changed.
+  void SetManagedPref(const std::string& path,
+                      std::unique_ptr<base::Value> value);
+
+  // Clears the preference on the managed layer and fire observers if the
+  // preference has been defined previously.
+  void RemoveManagedPref(const std::string& path);
+
+  // Similar to the above, but for extension preferences.
+  // Does not really know about extensions and their order of installation.
+  // Useful in tests that only check that a preference is overridden by an
+  // extension.
+  const base::Value* GetExtensionPref(const std::string& path) const;
+  void SetExtensionPref(const std::string& path,
+                        std::unique_ptr<base::Value> value);
+  void RemoveExtensionPref(const std::string& path);
+
+  // Similar to the above, but for user preferences.
+  const base::Value* GetUserPref(const std::string& path) const;
+  void SetUserPref(const std::string& path, std::unique_ptr<base::Value> value);
+  void RemoveUserPref(const std::string& path);
+
+  // Similar to the above, but for recommended policy preferences.
+  const base::Value* GetRecommendedPref(const std::string& path) const;
+  void SetRecommendedPref(const std::string& path,
+                          std::unique_ptr<base::Value> value);
+  void RemoveRecommendedPref(const std::string& path);
+
+  // Do-nothing implementation for TestingPrefService.
+  static void HandleReadError(PersistentPrefStore::PrefReadError error) {}
+
+ protected:
+  TestingPrefServiceBase(TestingPrefStore* managed_prefs,
+                         TestingPrefStore* extension_prefs,
+                         TestingPrefStore* user_prefs,
+                         TestingPrefStore* recommended_prefs,
+                         ConstructionPrefRegistry* pref_registry,
+                         PrefNotifierImpl* pref_notifier);
+
+ private:
+  // Reads the value of the preference indicated by |path| from |pref_store|.
+  // Returns NULL if the preference was not found.
+  const base::Value* GetPref(TestingPrefStore* pref_store,
+                             const std::string& path) const;
+
+  // Sets the value for |path| in |pref_store|.
+  void SetPref(TestingPrefStore* pref_store,
+               const std::string& path,
+               std::unique_ptr<base::Value> value);
+
+  // Removes the preference identified by |path| from |pref_store|.
+  void RemovePref(TestingPrefStore* pref_store, const std::string& path);
+
+  // Pointers to the pref stores our value store uses.
+  scoped_refptr<TestingPrefStore> managed_prefs_;
+  scoped_refptr<TestingPrefStore> extension_prefs_;
+  scoped_refptr<TestingPrefStore> user_prefs_;
+  scoped_refptr<TestingPrefStore> recommended_prefs_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceBase);
+};
+
+// Test version of PrefService.
+class TestingPrefServiceSimple
+    : public TestingPrefServiceBase<PrefService, PrefRegistry> {
+ public:
+  TestingPrefServiceSimple();
+  ~TestingPrefServiceSimple() override;
+
+  // This is provided as a convenience for registering preferences on
+  // an existing TestingPrefServiceSimple instance. On a production
+  // PrefService you would do all registrations before constructing
+  // it, passing it a PrefRegistry via its constructor (or via
+  // e.g. PrefServiceFactory).
+  PrefRegistrySimple* registry();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceSimple);
+};
+
+template<>
+TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
+    TestingPrefStore* managed_prefs,
+    TestingPrefStore* extension_prefs,
+    TestingPrefStore* user_prefs,
+    TestingPrefStore* recommended_prefs,
+    PrefRegistry* pref_registry,
+    PrefNotifierImpl* pref_notifier);
+
+template<class SuperPrefService, class ConstructionPrefRegistry>
+TestingPrefServiceBase<
+    SuperPrefService, ConstructionPrefRegistry>::~TestingPrefServiceBase() {
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value* TestingPrefServiceBase<
+    SuperPrefService,
+    ConstructionPrefRegistry>::GetManagedPref(const std::string& path) const {
+  return GetPref(managed_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    SetManagedPref(const std::string& path,
+                   std::unique_ptr<base::Value> value) {
+  SetPref(managed_prefs_.get(), path, std::move(value));
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    RemoveManagedPref(const std::string& path) {
+  RemovePref(managed_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value* TestingPrefServiceBase<
+    SuperPrefService,
+    ConstructionPrefRegistry>::GetExtensionPref(const std::string& path) const {
+  return GetPref(extension_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    SetExtensionPref(const std::string& path,
+                     std::unique_ptr<base::Value> value) {
+  SetPref(extension_prefs_.get(), path, std::move(value));
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    RemoveExtensionPref(const std::string& path) {
+  RemovePref(extension_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value*
+TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetUserPref(
+    const std::string& path) const {
+  return GetPref(user_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    SetUserPref(const std::string& path, std::unique_ptr<base::Value> value) {
+  SetPref(user_prefs_.get(), path, std::move(value));
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    RemoveUserPref(const std::string& path) {
+  RemovePref(user_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value*
+TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    GetRecommendedPref(const std::string& path) const {
+  return GetPref(recommended_prefs_, path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    SetRecommendedPref(const std::string& path,
+                       std::unique_ptr<base::Value> value) {
+  SetPref(recommended_prefs_.get(), path, std::move(value));
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    RemoveRecommendedPref(const std::string& path) {
+  RemovePref(recommended_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value*
+TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetPref(
+    TestingPrefStore* pref_store,
+    const std::string& path) const {
+  const base::Value* res;
+  return pref_store->GetValue(path, &res) ? res : NULL;
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    SetPref(TestingPrefStore* pref_store,
+            const std::string& path,
+            std::unique_ptr<base::Value> value) {
+  pref_store->SetValue(path, std::move(value),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+    RemovePref(TestingPrefStore* pref_store, const std::string& path) {
+  pref_store->RemoveValue(path, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+}
+
+#endif  // COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_
diff --git a/src/updater_components/prefs/testing_pref_store.cc b/src/updater_components/prefs/testing_pref_store.cc
new file mode 100644
index 0000000..4380407
--- /dev/null
+++ b/src/updater_components/prefs/testing_pref_store.cc
@@ -0,0 +1,216 @@
+// 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 "components/prefs/testing_pref_store.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/json/json_writer.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TestingPrefStore::TestingPrefStore()
+    : read_only_(true),
+      read_success_(true),
+      read_error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
+      block_async_read_(false),
+      pending_async_read_(false),
+      init_complete_(false),
+      committed_(true) {}
+
+bool TestingPrefStore::GetValue(const std::string& key,
+                                const base::Value** value) const {
+  return prefs_.GetValue(key, value);
+}
+
+std::unique_ptr<base::DictionaryValue> TestingPrefStore::GetValues() const {
+  return prefs_.AsDictionaryValue();
+}
+
+bool TestingPrefStore::GetMutableValue(const std::string& key,
+                                       base::Value** value) {
+  return prefs_.GetValue(key, value);
+}
+
+void TestingPrefStore::AddObserver(PrefStore::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void TestingPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool TestingPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
+}
+
+bool TestingPrefStore::IsInitializationComplete() const {
+  return init_complete_;
+}
+
+void TestingPrefStore::SetValue(const std::string& key,
+                                std::unique_ptr<base::Value> value,
+                                uint32_t flags) {
+  DCHECK(value);
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) {
+    committed_ = false;
+    NotifyPrefValueChanged(key);
+  }
+}
+
+void TestingPrefStore::SetValueSilently(const std::string& key,
+                                        std::unique_ptr<base::Value> value,
+                                        uint32_t flags) {
+  DCHECK(value);
+  CheckPrefIsSerializable(key, *value);
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value))))
+    committed_ = false;
+}
+
+void TestingPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+  if (prefs_.RemoveValue(key)) {
+    committed_ = false;
+    NotifyPrefValueChanged(key);
+  }
+}
+
+bool TestingPrefStore::ReadOnly() const {
+  return read_only_;
+}
+
+PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const {
+  return read_error_;
+}
+
+PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() {
+  NotifyInitializationCompleted();
+  return read_error_;
+}
+
+void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
+  DCHECK(!pending_async_read_);
+  error_delegate_.reset(error_delegate);
+  if (block_async_read_)
+    pending_async_read_ = true;
+  else
+    NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::CommitPendingWrite(
+    base::OnceClosure reply_callback,
+    base::OnceClosure synchronous_done_callback) {
+  committed_ = true;
+  PersistentPrefStore::CommitPendingWrite(std::move(reply_callback),
+                                          std::move(synchronous_done_callback));
+}
+
+void TestingPrefStore::SchedulePendingLossyWrites() {}
+
+void TestingPrefStore::SetInitializationCompleted() {
+  NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) {
+  for (Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+}
+
+void TestingPrefStore::NotifyInitializationCompleted() {
+  DCHECK(!init_complete_);
+  init_complete_ = true;
+  if (read_success_ && read_error_ != PREF_READ_ERROR_NONE && error_delegate_)
+    error_delegate_->OnError(read_error_);
+  for (Observer& observer : observers_)
+    observer.OnInitializationCompleted(read_success_);
+}
+
+void TestingPrefStore::ReportValueChanged(const std::string& key,
+                                          uint32_t flags) {
+  const base::Value* value = nullptr;
+  if (prefs_.GetValue(key, &value))
+    CheckPrefIsSerializable(key, *value);
+
+  for (Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+}
+
+void TestingPrefStore::SetString(const std::string& key,
+                                 const std::string& value) {
+  SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void TestingPrefStore::SetInteger(const std::string& key, int value) {
+  SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void TestingPrefStore::SetBoolean(const std::string& key, bool value) {
+  SetValue(key, std::make_unique<base::Value>(value), DEFAULT_PREF_WRITE_FLAGS);
+}
+
+bool TestingPrefStore::GetString(const std::string& key,
+                                 std::string* value) const {
+  const base::Value* stored_value;
+  if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+    return false;
+
+  return stored_value->GetAsString(value);
+}
+
+bool TestingPrefStore::GetInteger(const std::string& key, int* value) const {
+  const base::Value* stored_value;
+  if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+    return false;
+
+  return stored_value->GetAsInteger(value);
+}
+
+bool TestingPrefStore::GetBoolean(const std::string& key, bool* value) const {
+  const base::Value* stored_value;
+  if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+    return false;
+
+  return stored_value->GetAsBoolean(value);
+}
+
+void TestingPrefStore::SetBlockAsyncRead(bool block_async_read) {
+  DCHECK(!init_complete_);
+  block_async_read_ = block_async_read;
+  if (pending_async_read_ && !block_async_read_)
+    NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::ClearMutableValues() {
+  NOTIMPLEMENTED();
+}
+
+void TestingPrefStore::OnStoreDeletionFromDisk() {}
+
+void TestingPrefStore::set_read_only(bool read_only) {
+  read_only_ = read_only;
+}
+
+void TestingPrefStore::set_read_success(bool read_success) {
+  DCHECK(!init_complete_);
+  read_success_ = read_success;
+}
+
+void TestingPrefStore::set_read_error(
+    PersistentPrefStore::PrefReadError read_error) {
+  DCHECK(!init_complete_);
+  read_error_ = read_error;
+}
+
+TestingPrefStore::~TestingPrefStore() {
+  for (auto& pref : prefs_)
+    CheckPrefIsSerializable(pref.first, pref.second);
+}
+
+void TestingPrefStore::CheckPrefIsSerializable(const std::string& key,
+                                               const base::Value& value) {
+  std::string json;
+  EXPECT_TRUE(base::JSONWriter::Write(value, &json))
+      << "Pref \"" << key << "\" is not serializable as JSON.";
+}
diff --git a/src/updater_components/prefs/testing_pref_store.h b/src/updater_components/prefs/testing_pref_store.h
new file mode 100644
index 0000000..499b094
--- /dev/null
+++ b/src/updater_components/prefs/testing_pref_store.h
@@ -0,0 +1,122 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_TESTING_PREF_STORE_H_
+#define COMPONENTS_PREFS_TESTING_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_value_map.h"
+
+// |TestingPrefStore| is a preference store implementation that allows tests to
+// explicitly manipulate the contents of the store, triggering notifications
+// where appropriate.
+class TestingPrefStore : public PersistentPrefStore {
+ public:
+  TestingPrefStore();
+
+  // Overriden from PrefStore.
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+  bool IsInitializationComplete() const override;
+
+  // PersistentPrefStore overrides:
+  bool GetMutableValue(const std::string& key, base::Value** result) override;
+  void ReportValueChanged(const std::string& key, uint32_t flags) override;
+  void SetValue(const std::string& key,
+                std::unique_ptr<base::Value> value,
+                uint32_t flags) override;
+  void SetValueSilently(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) override;
+  void RemoveValue(const std::string& key, uint32_t flags) override;
+  bool ReadOnly() const override;
+  PrefReadError GetReadError() const override;
+  PersistentPrefStore::PrefReadError ReadPrefs() override;
+  void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
+  void CommitPendingWrite(base::OnceClosure reply_callback,
+                          base::OnceClosure synchronous_done_callback) override;
+  void SchedulePendingLossyWrites() override;
+
+  // Marks the store as having completed initialization.
+  void SetInitializationCompleted();
+
+  // Used for tests to trigger notifications explicitly.
+  void NotifyPrefValueChanged(const std::string& key);
+  void NotifyInitializationCompleted();
+
+  // Some convenience getters/setters.
+  void SetString(const std::string& key, const std::string& value);
+  void SetInteger(const std::string& key, int value);
+  void SetBoolean(const std::string& key, bool value);
+
+  bool GetString(const std::string& key, std::string* value) const;
+  bool GetInteger(const std::string& key, int* value) const;
+  bool GetBoolean(const std::string& key, bool* value) const;
+
+  // Determines whether ReadPrefsAsync completes immediately. Defaults to false
+  // (non-blocking). To block, invoke this with true (blocking) before the call
+  // to ReadPrefsAsync. To unblock, invoke again with false (non-blocking) after
+  // the call to ReadPrefsAsync.
+  void SetBlockAsyncRead(bool block_async_read);
+
+  void ClearMutableValues() override;
+  void OnStoreDeletionFromDisk() override;
+
+  // Getter and Setter methods for setting and getting the state of the
+  // |TestingPrefStore|.
+  virtual void set_read_only(bool read_only);
+  void set_read_success(bool read_success);
+  void set_read_error(PersistentPrefStore::PrefReadError read_error);
+  bool committed() { return committed_; }
+
+ protected:
+  ~TestingPrefStore() override;
+
+ private:
+  void CheckPrefIsSerializable(const std::string& key,
+                               const base::Value& value);
+
+  // Stores the preference values.
+  PrefValueMap prefs_;
+
+  // Flag that indicates if the PrefStore is read-only
+  bool read_only_;
+
+  // The result to pass to PrefStore::Observer::OnInitializationCompleted
+  bool read_success_;
+
+  // The result to return from ReadPrefs or ReadPrefsAsync.
+  PersistentPrefStore::PrefReadError read_error_;
+
+  // Whether a call to ReadPrefsAsync should block.
+  bool block_async_read_;
+
+  // Whether there is a pending call to ReadPrefsAsync.
+  bool pending_async_read_;
+
+  // Whether initialization has been completed.
+  bool init_complete_;
+
+  // Whether the store contents have been committed to disk since the last
+  // mutation.
+  bool committed_;
+
+  std::unique_ptr<ReadErrorDelegate> error_delegate_;
+  base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestingPrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_TESTING_PREF_STORE_H_
diff --git a/src/updater_components/prefs/value_map_pref_store.cc b/src/updater_components/prefs/value_map_pref_store.cc
new file mode 100644
index 0000000..79c2017
--- /dev/null
+++ b/src/updater_components/prefs/value_map_pref_store.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/prefs/value_map_pref_store.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/stl_util.h"
+#include "base/values.h"
+
+ValueMapPrefStore::ValueMapPrefStore() {}
+
+bool ValueMapPrefStore::GetValue(const std::string& key,
+                                 const base::Value** value) const {
+  return prefs_.GetValue(key, value);
+}
+
+std::unique_ptr<base::DictionaryValue> ValueMapPrefStore::GetValues() const {
+  return prefs_.AsDictionaryValue();
+}
+
+void ValueMapPrefStore::AddObserver(PrefStore::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void ValueMapPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool ValueMapPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
+}
+
+void ValueMapPrefStore::SetValue(const std::string& key,
+                                 std::unique_ptr<base::Value> value,
+                                 uint32_t flags) {
+  DCHECK(value);
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) {
+    for (Observer& observer : observers_)
+      observer.OnPrefValueChanged(key);
+  }
+}
+
+void ValueMapPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+  if (prefs_.RemoveValue(key)) {
+    for (Observer& observer : observers_)
+      observer.OnPrefValueChanged(key);
+  }
+}
+
+bool ValueMapPrefStore::GetMutableValue(const std::string& key,
+                                        base::Value** value) {
+  return prefs_.GetValue(key, value);
+}
+
+void ValueMapPrefStore::ReportValueChanged(const std::string& key,
+                                           uint32_t flags) {
+  for (Observer& observer : observers_)
+    observer.OnPrefValueChanged(key);
+}
+
+void ValueMapPrefStore::SetValueSilently(const std::string& key,
+                                         std::unique_ptr<base::Value> value,
+                                         uint32_t flags) {
+  DCHECK(value);
+  prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)));
+}
+
+ValueMapPrefStore::~ValueMapPrefStore() {}
+
+void ValueMapPrefStore::NotifyInitializationCompleted() {
+  for (Observer& observer : observers_)
+    observer.OnInitializationCompleted(true);
+}
diff --git a/src/updater_components/prefs/value_map_pref_store.h b/src/updater_components/prefs/value_map_pref_store.h
new file mode 100644
index 0000000..f0de0cd
--- /dev/null
+++ b/src/updater_components/prefs/value_map_pref_store.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_
+#define COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/prefs/pref_value_map.h"
+#include "components/prefs/prefs_export.h"
+#include "components/prefs/writeable_pref_store.h"
+
+// A basic PrefStore implementation that uses a simple name-value map for
+// storing the preference values.
+class COMPONENTS_PREFS_EXPORT ValueMapPrefStore : public WriteablePrefStore {
+ public:
+  ValueMapPrefStore();
+
+  // PrefStore overrides:
+  bool GetValue(const std::string& key,
+                const base::Value** value) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+
+  // WriteablePrefStore overrides:
+  void SetValue(const std::string& key,
+                std::unique_ptr<base::Value> value,
+                uint32_t flags) override;
+  void RemoveValue(const std::string& key, uint32_t flags) override;
+  bool GetMutableValue(const std::string& key, base::Value** value) override;
+  void ReportValueChanged(const std::string& key, uint32_t flags) override;
+  void SetValueSilently(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) override;
+
+ protected:
+  ~ValueMapPrefStore() override;
+
+  // Notify observers about the initialization completed event.
+  void NotifyInitializationCompleted();
+
+ private:
+  PrefValueMap prefs_;
+
+  base::ObserverList<PrefStore::Observer, true>::Unchecked observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(ValueMapPrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_
diff --git a/src/updater_components/prefs/writeable_pref_store.cc b/src/updater_components/prefs/writeable_pref_store.cc
new file mode 100644
index 0000000..d51985e
--- /dev/null
+++ b/src/updater_components/prefs/writeable_pref_store.cc
@@ -0,0 +1,14 @@
+// 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 "components/prefs/writeable_pref_store.h"
+
+void WriteablePrefStore::ReportSubValuesChanged(
+    const std::string& key,
+    std::set<std::vector<std::string>> path_components,
+    uint32_t flags) {
+  // Default implementation. Subclasses may use |path_components| to improve
+  // performance.
+  ReportValueChanged(key, flags);
+}
diff --git a/src/updater_components/prefs/writeable_pref_store.h b/src/updater_components/prefs/writeable_pref_store.h
new file mode 100644
index 0000000..9a69c7c
--- /dev/null
+++ b/src/updater_components/prefs/writeable_pref_store.h
@@ -0,0 +1,86 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_
+#define COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/prefs/pref_store.h"
+
+namespace base {
+class Value;
+}
+
+// A pref store that can be written to as well as read from.
+class COMPONENTS_PREFS_EXPORT WriteablePrefStore : public PrefStore {
+ public:
+  // PrefWriteFlags can be used to change the way a pref will be written to
+  // storage.
+  enum PrefWriteFlags : uint32_t {
+    // No flags are specified.
+    DEFAULT_PREF_WRITE_FLAGS = 0,
+
+    // This marks the pref as "lossy". There is no strict time guarantee on when
+    // a lossy pref will be persisted to permanent storage when it is modified.
+    LOSSY_PREF_WRITE_FLAG = 1 << 1
+  };
+
+  WriteablePrefStore() {}
+
+  // Sets a |value| for |key| in the store. |value| must be non-NULL. |flags| is
+  // a bitmask of PrefWriteFlags.
+  virtual void SetValue(const std::string& key,
+                        std::unique_ptr<base::Value> value,
+                        uint32_t flags) = 0;
+
+  // Removes the value for |key|.
+  virtual void RemoveValue(const std::string& key, uint32_t flags) = 0;
+
+  // Equivalent to PrefStore::GetValue but returns a mutable value.
+  virtual bool GetMutableValue(const std::string& key,
+                               base::Value** result) = 0;
+
+  // Triggers a value changed notification. This function or
+  // ReportSubValuesChanged needs to be called if one retrieves a list or
+  // dictionary with GetMutableValue and change its value. SetValue takes care
+  // of notifications itself. Note that ReportValueChanged will trigger
+  // notifications even if nothing has changed.  |flags| is a bitmask of
+  // PrefWriteFlags.
+  virtual void ReportValueChanged(const std::string& key, uint32_t flags) = 0;
+
+  // Triggers a value changed notification for |path_components| in the |key|
+  // pref. This function or ReportValueChanged needs to be called if one
+  // retrieves a list or dictionary with GetMutableValue and change its value.
+  // SetValue takes care of notifications itself. Note that
+  // ReportSubValuesChanged will trigger notifications even if nothing has
+  // changed. |flags| is a bitmask of PrefWriteFlags.
+  virtual void ReportSubValuesChanged(
+      const std::string& key,
+      std::set<std::vector<std::string>> path_components,
+      uint32_t flags);
+
+  // Same as SetValue, but doesn't generate notifications. This is used by
+  // PrefService::GetMutableUserPref() in order to put empty entries
+  // into the user pref store. Using SetValue is not an option since existing
+  // tests rely on the number of notifications generated. |flags| is a bitmask
+  // of PrefWriteFlags.
+  virtual void SetValueSilently(const std::string& key,
+                                std::unique_ptr<base::Value> value,
+                                uint32_t flags) = 0;
+
+ protected:
+  ~WriteablePrefStore() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WriteablePrefStore);
+};
+
+#endif  // COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_
diff --git a/src/updater_components/update_client/BUILD.gn b/src/updater_components/update_client/BUILD.gn
new file mode 100644
index 0000000..de3d06b
--- /dev/null
+++ b/src/updater_components/update_client/BUILD.gn
@@ -0,0 +1,243 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//net/features.gni")
+
+source_set("network_impl") {
+  sources = [
+    "net/network_chromium.h",
+    "net/network_impl.cc",
+    "net/network_impl.h",
+  ]
+
+  deps = [
+    ":update_client",
+    "//base",
+    "//net",
+    "//services/network/public/cpp:cpp",
+    "//url",
+  ]
+}
+
+source_set("unzip_impl") {
+  sources = [
+    "unzip/unzip_impl.cc",
+    "unzip/unzip_impl.h",
+  ]
+  deps = [
+    ":update_client",
+    "//components/services/unzip/public/cpp",
+  ]
+}
+
+source_set("patch_impl") {
+  sources = [
+    "patch/patch_impl.cc",
+    "patch/patch_impl.h",
+  ]
+  deps = [
+    ":update_client",
+    "//components/services/patch/public/cpp",
+    "//components/services/patch/public/mojom",
+    "//mojo/public/cpp/bindings",
+  ]
+}
+
+group("common_impl") {
+  public_deps = [
+    ":network_impl",
+    ":patch_impl",
+    ":unzip_impl",
+  ]
+}
+
+static_library("update_client") {
+  sources = [
+    "action_runner.cc",
+    "action_runner.h",
+    "action_runner_win.cc",
+    "activity_data_service.h",
+    "background_downloader_win.cc",
+    "background_downloader_win.h",
+    "command_line_config_policy.cc",
+    "command_line_config_policy.h",
+    "component.cc",
+    "component.h",
+    "component_patcher.cc",
+    "component_patcher.h",
+    "component_patcher_operation.cc",
+    "component_patcher_operation.h",
+    "component_unpacker.cc",
+    "component_unpacker.h",
+    "configurator.h",
+    "crx_downloader.cc",
+    "crx_downloader.h",
+    "crx_update_item.h",
+    "network.cc",
+    "network.h",
+    "patcher.h",
+    "persisted_data.cc",
+    "persisted_data.h",
+    "ping_manager.cc",
+    "ping_manager.h",
+    "protocol_definition.cc",
+    "protocol_definition.h",
+    "protocol_handler.cc",
+    "protocol_handler.h",
+    "protocol_parser.cc",
+    "protocol_parser.h",
+    "protocol_parser_json.cc",
+    "protocol_parser_json.h",
+    "protocol_serializer.cc",
+    "protocol_serializer.h",
+    "protocol_serializer_json.cc",
+    "protocol_serializer_json.h",
+    "request_sender.cc",
+    "request_sender.h",
+    "task.h",
+    "task_send_uninstall_ping.cc",
+    "task_send_uninstall_ping.h",
+    "task_traits.h",
+    "task_update.cc",
+    "task_update.h",
+    "unzipper.h",
+    "update_checker.cc",
+    "update_checker.h",
+    "update_client.cc",
+    "update_client.h",
+    "update_client_errors.h",
+    "update_client_internal.h",
+    "update_engine.cc",
+    "update_engine.h",
+    "update_query_params.cc",
+    "update_query_params.h",
+    "update_query_params_delegate.cc",
+    "update_query_params_delegate.h",
+    "updater_state.cc",
+    "updater_state.h",
+    "updater_state_mac.mm",
+    "updater_state_win.cc",
+    "url_fetcher_downloader.cc",
+    "url_fetcher_downloader.h",
+    "utils.cc",
+    "utils.h",
+  ]
+
+  deps = [
+    "//base",
+    "//build:branding_buildflags",
+    "//components/client_update_protocol",
+    "//components/crx_file",
+    "//components/prefs",
+    "//components/version_info:version_info",
+    "//courgette:courgette_lib",
+    "//crypto",
+    "//url",
+  ]
+}
+
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "net/url_loader_post_interceptor.cc",
+    "net/url_loader_post_interceptor.h",
+    "test_configurator.cc",
+    "test_configurator.h",
+    "test_installer.cc",
+    "test_installer.h",
+  ]
+
+  public_deps = [
+    ":update_client",
+  ]
+
+  deps = [
+    ":network_impl",
+    ":patch_impl",
+    ":unzip_impl",
+    "//base",
+    "//components/prefs",
+    "//components/services/patch:in_process",
+    "//components/services/unzip:in_process",
+    "//mojo/public/cpp/bindings",
+    "//net:test_support",
+    "//services/network:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//url",
+  ]
+}
+
+bundle_data("unit_tests_bundle_data") {
+  visibility = [ ":unit_tests" ]
+  testonly = true
+  sources = [
+    "//components/test/data/update_client/binary_bsdiff_patch.bin",
+    "//components/test/data/update_client/binary_courgette_patch.bin",
+    "//components/test/data/update_client/binary_input.bin",
+    "//components/test/data/update_client/binary_output.bin",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx",
+    "//components/test/data/update_client/jebgalgnebhfojomionfpkfelancnnkf.crx",
+    "//components/test/data/update_client/runaction_test_win.crx3",
+    "//components/test/data/update_client/updatecheck_reply_1.json",
+    "//components/test/data/update_client/updatecheck_reply_4.json",
+    "//components/test/data/update_client/updatecheck_reply_noupdate.json",
+    "//components/test/data/update_client/updatecheck_reply_parse_error.json",
+    "//components/test/data/update_client/updatecheck_reply_unknownapp.json",
+  ]
+  outputs = [
+    "{{bundle_resources_dir}}/" +
+        "{{source_root_relative_dir}}/{{source_file_part}}",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "component_patcher_unittest.cc",
+    "component_patcher_unittest.h",
+    "component_unpacker_unittest.cc",
+    "persisted_data_unittest.cc",
+    "ping_manager_unittest.cc",
+    "protocol_parser_json_unittest.cc",
+    "protocol_serializer_json_unittest.cc",
+    "protocol_serializer_unittest.cc",
+    "request_sender_unittest.cc",
+    "update_checker_unittest.cc",
+    "update_client_unittest.cc",
+    "update_query_params_unittest.cc",
+    "updater_state_unittest.cc",
+    "utils_unittest.cc",
+  ]
+
+  if (!disable_file_support) {
+    sources += [ "crx_downloader_unittest.cc" ]
+  }
+
+  deps = [
+    ":network_impl",
+    ":patch_impl",
+    ":test_support",
+    ":unit_tests_bundle_data",
+    ":unzip_impl",
+    ":update_client",
+    "//base",
+    "//build:branding_buildflags",
+    "//components/crx_file",
+    "//components/prefs",
+    "//components/prefs:test_support",
+    "//components/services/patch:in_process",
+    "//components/version_info:version_info",
+    "//courgette:courgette_lib",
+    "//net:test_support",
+    "//services/network:test_support",
+    "//services/network/public/cpp:cpp",
+    "//services/network/public/cpp:cpp_base",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/re2",
+  ]
+}
diff --git a/src/updater_components/update_client/action_runner.cc b/src/updater_components/update_client/action_runner.cc
new file mode 100644
index 0000000..e738bdc
--- /dev/null
+++ b/src/updater_components/update_client/action_runner.cc
@@ -0,0 +1,108 @@
+// 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 "components/update_client/action_runner.h"
+
+#include <iterator>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/update_client/component.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/task_traits.h"
+#include "components/update_client/unzipper.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_engine.h"
+
+namespace update_client {
+
+ActionRunner::ActionRunner(const Component& component)
+    : is_per_user_install_(component.config()->IsPerUserInstall()),
+      component_(component),
+      main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+
+ActionRunner::~ActionRunner() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void ActionRunner::Run(Callback run_complete) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  run_complete_ = std::move(run_complete);
+
+  base::CreateSequencedTaskRunner(kTaskTraits)
+      ->PostTask(
+          FROM_HERE,
+          base::BindOnce(&ActionRunner::RunOnTaskRunner, base::Unretained(this),
+                         component_.config()->GetUnzipperFactory()->Create(),
+                         component_.config()->GetPatcherFactory()->Create()));
+}
+
+void ActionRunner::RunOnTaskRunner(std::unique_ptr<Unzipper> unzip,
+                                   scoped_refptr<Patcher> patch) {
+  const auto installer = component_.crx_component()->installer;
+
+  base::FilePath crx_path;
+  installer->GetInstalledFile(component_.action_run(), &crx_path);
+
+  if (!is_per_user_install_) {
+    RunRecoveryCRXElevated(std::move(crx_path));
+    return;
+  }
+
+  const auto config = component_.config();
+  auto unpacker = base::MakeRefCounted<ComponentUnpacker>(
+      config->GetRunActionKeyHash(), crx_path, installer, std::move(unzip),
+      std::move(patch), component_.crx_component()->crx_format_requirement);
+  unpacker->Unpack(
+      base::BindOnce(&ActionRunner::UnpackComplete, base::Unretained(this)));
+}
+
+void ActionRunner::UnpackComplete(const ComponentUnpacker::Result& result) {
+  if (result.error != UnpackerError::kNone) {
+    DCHECK(!base::DirectoryExists(result.unpack_path));
+
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(run_complete_), false,
+                       static_cast<int>(result.error), result.extended_error));
+    return;
+  }
+
+  unpack_path_ = result.unpack_path;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ActionRunner::RunCommand, base::Unretained(this),
+                     MakeCommandLine(result.unpack_path)));
+}
+
+#if !defined(OS_WIN)
+
+void ActionRunner::RunRecoveryCRXElevated(const base::FilePath& crx_path) {
+  NOTREACHED();
+}
+
+void ActionRunner::RunCommand(const base::CommandLine& cmdline) {
+  base::DeleteFile(unpack_path_, true);
+  main_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(std::move(run_complete_), false, -1, 0));
+}
+
+base::CommandLine ActionRunner::MakeCommandLine(
+    const base::FilePath& unpack_path) const {
+  return base::CommandLine(base::CommandLine::NO_PROGRAM);
+}
+
+#endif  // OS_WIN
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/action_runner.h b/src/updater_components/update_client/action_runner.h
new file mode 100644
index 0000000..ee41890
--- /dev/null
+++ b/src/updater_components/update_client/action_runner.h
@@ -0,0 +1,75 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_
+#define COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "build/build_config.h"
+#include "components/update_client/component_unpacker.h"
+
+namespace base {
+class CommandLine;
+class Process;
+class SingleThreadTaskRunner;
+}
+
+namespace update_client {
+
+class Component;
+
+class ActionRunner {
+ public:
+  using Callback =
+      base::OnceCallback<void(bool succeeded, int error_code, int extra_code1)>;
+
+  explicit ActionRunner(const Component& component);
+  ~ActionRunner();
+
+  void Run(Callback run_complete);
+
+ private:
+  void RunOnTaskRunner(std::unique_ptr<Unzipper> unzipper,
+                       scoped_refptr<Patcher> patcher);
+  void UnpackComplete(const ComponentUnpacker::Result& result);
+
+  void RunCommand(const base::CommandLine& cmdline);
+  void RunRecoveryCRXElevated(const base::FilePath& crx_path);
+
+  base::CommandLine MakeCommandLine(const base::FilePath& unpack_path) const;
+
+  void WaitForCommand(base::Process process);
+
+#if defined(OS_WIN)
+  void RunRecoveryCRXElevatedInSTA(const base::FilePath& crx_path);
+#endif
+
+  bool is_per_user_install_ = false;
+  const Component& component_;
+
+  // Used to post callbacks to the main thread.
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+  // Contains the unpack path for the component associated with the run action.
+  base::FilePath unpack_path_;
+
+  Callback run_complete_;
+
+  THREAD_CHECKER(thread_checker_);
+  DISALLOW_COPY_AND_ASSIGN(ActionRunner);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_ACTION_RUNNER_H_
diff --git a/src/updater_components/update_client/action_runner_win.cc b/src/updater_components/update_client/action_runner_win.cc
new file mode 100644
index 0000000..dca3059
--- /dev/null
+++ b/src/updater_components/update_client/action_runner_win.cc
@@ -0,0 +1,90 @@
+// 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 "components/update_client/action_runner.h"
+
+#include <tuple>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task/post_task.h"
+#include "components/update_client/component.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/task_traits.h"
+
+namespace {
+
+const base::FilePath::CharType kRecoveryFileName[] =
+    FILE_PATH_LITERAL("ChromeRecovery.exe");
+
+}  // namespace
+
+namespace update_client {
+
+void ActionRunner::RunCommand(const base::CommandLine& cmdline) {
+  base::LaunchOptions options;
+  options.start_hidden = true;
+  base::Process process = base::LaunchProcess(cmdline, options);
+
+  base::PostTask(FROM_HERE, kTaskTraitsRunCommand,
+                 base::BindOnce(&ActionRunner::WaitForCommand,
+                                base::Unretained(this), std::move(process)));
+}
+
+void ActionRunner::WaitForCommand(base::Process process) {
+  int exit_code = 0;
+  const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromSeconds(600);
+  const bool succeeded =
+      process.WaitForExitWithTimeout(kMaxWaitTime, &exit_code);
+  base::DeleteFile(unpack_path_, true);
+  main_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(run_complete_), succeeded, exit_code, 0));
+}
+
+base::CommandLine ActionRunner::MakeCommandLine(
+    const base::FilePath& unpack_path) const {
+  base::CommandLine command_line(unpack_path.Append(kRecoveryFileName));
+  if (!is_per_user_install_)
+    command_line.AppendSwitch("system");
+  command_line.AppendSwitchASCII(
+      "browser-version", component_.config()->GetBrowserVersion().GetString());
+  command_line.AppendSwitchASCII("sessionid", component_.session_id());
+  const auto app_guid = component_.config()->GetAppGuid();
+  if (!app_guid.empty())
+    command_line.AppendSwitchASCII("appguid", app_guid);
+  VLOG(1) << "run action: " << command_line.GetCommandLineString();
+  return command_line;
+}
+
+void ActionRunner::RunRecoveryCRXElevated(const base::FilePath& crx_path) {
+  base::CreateCOMSTATaskRunner(
+      kTaskTraitsRunCommand, base::SingleThreadTaskRunnerThreadMode::DEDICATED)
+      ->PostTask(FROM_HERE,
+                 base::BindOnce(&ActionRunner::RunRecoveryCRXElevatedInSTA,
+                                base::Unretained(this), crx_path));
+}
+
+void ActionRunner::RunRecoveryCRXElevatedInSTA(const base::FilePath& crx_path) {
+  bool succeeded = false;
+  int error_code = 0;
+  int extra_code = 0;
+  const auto config = component_.config();
+  std::tie(succeeded, error_code, extra_code) =
+      component_.config()->GetRecoveryCRXElevator().Run(
+          crx_path, config->GetAppGuid(),
+          config->GetBrowserVersion().GetString(), component_.session_id());
+  main_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(std::move(run_complete_), succeeded, error_code,
+                                extra_code));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/activity_data_service.h b/src/updater_components/update_client/activity_data_service.h
new file mode 100644
index 0000000..5d3744c
--- /dev/null
+++ b/src/updater_components/update_client/activity_data_service.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_ACTIVITY_DATA_SERVICE_H_
+#define COMPONENTS_UPDATE_CLIENT_ACTIVITY_DATA_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+
+namespace update_client {
+
+const int kDateFirstTime = -1;
+const int kDaysFirstTime = -1;
+const int kDateUnknown = -2;
+const int kDaysUnknown = -2;
+
+// This is an interface that injects certain update information (active, days
+// since ...) into the update engine of the update client.
+// GetDaysSinceLastActive and GetDaysSinceLastRollCall are used for backward
+// compatibility.
+class ActivityDataService {
+ public:
+  // Returns the current state of the active bit of the specified |id|.
+  virtual bool GetActiveBit(const std::string& id) const = 0;
+
+  // Clears the active bit of the specified |id|.
+  virtual void ClearActiveBit(const std::string& id) = 0;
+
+  // The following 2 functions return the number of days since last
+  // active/roll call.
+  virtual int GetDaysSinceLastActive(const std::string& id) const = 0;
+  virtual int GetDaysSinceLastRollCall(const std::string& id) const = 0;
+
+  virtual ~ActivityDataService() {}
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_ACTIVITY_DATA_SERVICE_H_
diff --git a/src/updater_components/update_client/background_downloader_win.cc b/src/updater_components/update_client/background_downloader_win.cc
new file mode 100644
index 0000000..5e16579
--- /dev/null
+++ b/src/updater_components/update_client/background_downloader_win.cc
@@ -0,0 +1,903 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/background_downloader_win.h"
+
+#include <objbase.h>
+#include <winerror.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <functional>
+#include <iomanip>
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/win/atl.h"
+#include "base/win/scoped_co_mem.h"
+#include "components/update_client/task_traits.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+#include "url/gurl.h"
+
+using Microsoft::WRL::ComPtr;
+using base::win::ScopedCoMem;
+
+// The class BackgroundDownloader in this module is an adapter between
+// the CrxDownloader interface and the BITS service interfaces.
+// The interface exposed on the CrxDownloader code runs on the main thread,
+// while the BITS specific code runs on a separate thread passed in by the
+// client. For every url to download, a BITS job is created, unless there is
+// already an existing job for that url, in which case, the downloader
+// connects to it. Once a job is associated with the url, the code looks for
+// changes in the BITS job state. The checks are triggered by a timer.
+// The BITS job contains just one file to download. There could only be one
+// download in progress at a time. If Chrome closes down before the download is
+// complete, the BITS job remains active and finishes in the background, without
+// any intervention. The job can be completed next time the code runs, if the
+// file is still needed, otherwise it will be cleaned up on a periodic basis.
+//
+// To list the BITS jobs for a user, use the |bitsadmin| tool. The command line
+// to do that is: "bitsadmin /list /verbose". Another useful command is
+// "bitsadmin /info" and provide the job id returned by the previous /list
+// command.
+//
+// Ignoring the suspend/resume issues since this code is not using them, the
+// job state machine implemented by BITS is something like this:
+//
+//  Suspended--->Queued--->Connecting---->Transferring--->Transferred
+//       |          ^         |                 |               |
+//       |          |         V                 V               | (complete)
+//       +----------|---------+-----------------+-----+         V
+//                  |         |                 |     |    Acknowledged
+//                  |         V                 V     |
+//                  |  Transient Error------->Error   |
+//                  |         |                 |     |(cancel)
+//                  |         +-------+---------+--->-+
+//                  |                 V               |
+//                  |   (resume)      |               |
+//                  +------<----------+               +---->Cancelled
+//
+// The job is created in the "suspended" state. Once |Resume| is called,
+// BITS queues up the job, then tries to connect, begins transferring the
+// job bytes, and moves the job to the "transferred state, after the job files
+// have been transferred. When calling |Complete| for a job, the job files are
+// made available to the caller, and the job is moved to the "acknowledged"
+// state.
+// At any point, the job can be cancelled, in which case, the job is moved
+// to the "cancelled state" and the job object is removed from the BITS queue.
+// Along the way, the job can encounter recoverable and non-recoverable errors.
+// BITS moves the job to "transient error" or "error", depending on which kind
+// of error has occured.
+// If  the job has reached the "transient error" state, BITS retries the
+// job after a certain programmable delay. If the job can't be completed in a
+// certain time interval, BITS stops retrying and errors the job out. This time
+// interval is also programmable.
+// If the job is in either of the error states, the job parameters can be
+// adjusted to handle the error, after which the job can be resumed, and the
+// whole cycle starts again.
+// Jobs that are not touched in 90 days (or a value set by group policy) are
+// automatically disposed off by BITS. This concludes the brief description of
+// a job lifetime, according to BITS.
+//
+// In addition to how BITS is managing the life time of the job, there are a
+// couple of special cases defined by the BackgroundDownloader.
+// First, if the job encounters any of the 5xx HTTP responses, the job is
+// not retried, in order to avoid DDOS-ing the servers.
+// Second, there is a simple mechanism to detect stuck jobs, and allow the rest
+// of the code to move on to trying other urls or trying other components.
+// Last, after completing a job, irrespective of the outcome, the jobs older
+// than a week are proactively cleaned up.
+
+namespace update_client {
+
+namespace {
+
+// All jobs created by this module have a specific description so they can
+// be found at run-time or by using system administration tools.
+const base::char16 kJobName[] = L"Chrome Component Updater";
+
+// How often the code looks for changes in the BITS job state.
+const int kJobPollingIntervalSec = 4;
+
+// How long BITS waits before retrying a job after the job encountered
+// a transient error. If this value is not set, the BITS default is 10 minutes.
+const int kMinimumRetryDelayMin = 1;
+
+// How long to wait for stuck jobs. Stuck jobs could be queued for too long,
+// have trouble connecting, could be suspended for any reason, or they have
+// encountered some transient error.
+const int kJobStuckTimeoutMin = 15;
+
+// How long BITS waits before giving up on a job that could not be completed
+// since the job has encountered its first transient error. If this value is
+// not set, the BITS default is 14 days.
+const int kSetNoProgressTimeoutDays = 1;
+
+// How often the jobs which were started but not completed for any reason
+// are cleaned up. Reasons for jobs to be left behind include browser restarts,
+// system restarts, etc. Also, the check to purge stale jobs only happens
+// at most once a day. If the job clean up code is not running, the BITS
+// default policy is to cancel jobs after 90 days of inactivity.
+const int kPurgeStaleJobsAfterDays = 3;
+const int kPurgeStaleJobsIntervalBetweenChecksDays = 1;
+
+// Number of maximum BITS jobs this downloader can create and queue up.
+const int kMaxQueuedJobs = 10;
+
+// Retrieves the singleton instance of GIT for this process.
+HRESULT GetGit(ComPtr<IGlobalInterfaceTable>* git) {
+  return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr,
+                            CLSCTX_INPROC_SERVER,
+                            IID_PPV_ARGS(git->GetAddressOf()));
+}
+
+// Retrieves an interface pointer from the process GIT for a given |cookie|.
+HRESULT GetInterfaceFromGit(const ComPtr<IGlobalInterfaceTable>& git,
+                            DWORD cookie,
+                            REFIID riid,
+                            void** ppv) {
+  return git->GetInterfaceFromGlobal(cookie, riid, ppv);
+}
+
+// Registers an interface pointer in GIT and returns its corresponding |cookie|.
+template <typename T>
+HRESULT RegisterInterfaceInGit(const ComPtr<IGlobalInterfaceTable>& git,
+                               const ComPtr<T>& p,
+                               DWORD* cookie) {
+  return git->RegisterInterfaceInGlobal(p.Get(), __uuidof(T), cookie);
+}
+
+// Returns the status code from a given BITS error.
+int GetHttpStatusFromBitsError(HRESULT error) {
+  // BITS errors are defined in bitsmsg.h. Although not documented, it is
+  // clear that all errors corresponding to http status code have the high
+  // word equal to 0x8019 and the low word equal to the http status code.
+  const int kHttpStatusFirst = 100;  // Continue.
+  const int kHttpStatusLast = 505;   // Version not supported.
+  bool is_valid = HIWORD(error) == 0x8019 &&
+                  LOWORD(error) >= kHttpStatusFirst &&
+                  LOWORD(error) <= kHttpStatusLast;
+  return is_valid ? LOWORD(error) : 0;
+}
+
+// Returns the files in a BITS job.
+HRESULT GetFilesInJob(const ComPtr<IBackgroundCopyJob>& job,
+                      std::vector<ComPtr<IBackgroundCopyFile>>* files) {
+  ComPtr<IEnumBackgroundCopyFiles> enum_files;
+  HRESULT hr = job->EnumFiles(enum_files.GetAddressOf());
+  if (FAILED(hr))
+    return hr;
+
+  ULONG num_files = 0;
+  hr = enum_files->GetCount(&num_files);
+  if (FAILED(hr))
+    return hr;
+
+  for (ULONG i = 0; i != num_files; ++i) {
+    ComPtr<IBackgroundCopyFile> file;
+    if (enum_files->Next(1, file.GetAddressOf(), nullptr) == S_OK && file.Get())
+      files->push_back(file);
+  }
+
+  return S_OK;
+}
+
+// Returns the file name, the url, and some per-file progress information.
+// The function out parameters can be NULL if that data is not requested.
+HRESULT GetJobFileProperties(const ComPtr<IBackgroundCopyFile>& file,
+                             base::string16* local_name,
+                             base::string16* remote_name,
+                             BG_FILE_PROGRESS* progress) {
+  if (!file)
+    return E_FAIL;
+
+  HRESULT hr = S_OK;
+
+  if (local_name) {
+    ScopedCoMem<base::char16> name;
+    hr = file->GetLocalName(&name);
+    if (FAILED(hr))
+      return hr;
+    local_name->assign(name);
+  }
+
+  if (remote_name) {
+    ScopedCoMem<base::char16> name;
+    hr = file->GetRemoteName(&name);
+    if (FAILED(hr))
+      return hr;
+    remote_name->assign(name);
+  }
+
+  if (progress) {
+    BG_FILE_PROGRESS bg_file_progress = {};
+    hr = file->GetProgress(&bg_file_progress);
+    if (FAILED(hr))
+      return hr;
+    *progress = bg_file_progress;
+  }
+
+  return hr;
+}
+
+// Returns the number of bytes downloaded and bytes to download for all files
+// in the job. If the values are not known or if an error has occurred,
+// a value of -1 is reported.
+HRESULT GetJobByteCount(const ComPtr<IBackgroundCopyJob>& job,
+                        int64_t* downloaded_bytes,
+                        int64_t* total_bytes) {
+  *downloaded_bytes = -1;
+  *total_bytes = -1;
+
+  if (!job)
+    return E_FAIL;
+
+  BG_JOB_PROGRESS job_progress = {};
+  HRESULT hr = job->GetProgress(&job_progress);
+  if (FAILED(hr))
+    return hr;
+
+  const uint64_t kMaxNumBytes =
+      static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
+  if (job_progress.BytesTransferred <= kMaxNumBytes)
+    *downloaded_bytes = job_progress.BytesTransferred;
+
+  if (job_progress.BytesTotal <= kMaxNumBytes &&
+      job_progress.BytesTotal != BG_SIZE_UNKNOWN)
+    *total_bytes = job_progress.BytesTotal;
+
+  return S_OK;
+}
+
+HRESULT GetJobDisplayName(const ComPtr<IBackgroundCopyJob>& job,
+                          base::string16* name) {
+  ScopedCoMem<base::char16> local_name;
+  const HRESULT hr = job->GetDisplayName(&local_name);
+  if (FAILED(hr))
+    return hr;
+  *name = local_name.get();
+  return S_OK;
+}
+
+// Returns the job error code in |error_code| if the job is in the transient
+// or the final error state. Otherwise, the job error is not available and
+// the function fails.
+HRESULT GetJobError(const ComPtr<IBackgroundCopyJob>& job,
+                    HRESULT* error_code_out) {
+  *error_code_out = S_OK;
+  ComPtr<IBackgroundCopyError> copy_error;
+  HRESULT hr = job->GetError(copy_error.GetAddressOf());
+  if (FAILED(hr))
+    return hr;
+
+  BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE;
+  HRESULT error_code = S_OK;
+  hr = copy_error->GetError(&error_context, &error_code);
+  if (FAILED(hr))
+    return hr;
+
+  *error_code_out = FAILED(error_code) ? error_code : E_FAIL;
+  return S_OK;
+}
+
+// Finds the component updater jobs matching the given predicate.
+// Returns S_OK if the function has found at least one job, returns S_FALSE if
+// no job was found, and it returns an error otherwise.
+template <class Predicate>
+HRESULT FindBitsJobIf(Predicate pred,
+                      const ComPtr<IBackgroundCopyManager>& bits_manager,
+                      std::vector<ComPtr<IBackgroundCopyJob>>* jobs) {
+  ComPtr<IEnumBackgroundCopyJobs> enum_jobs;
+  HRESULT hr = bits_manager->EnumJobs(0, enum_jobs.GetAddressOf());
+  if (FAILED(hr))
+    return hr;
+
+  ULONG job_count = 0;
+  hr = enum_jobs->GetCount(&job_count);
+  if (FAILED(hr))
+    return hr;
+
+  // Iterate over jobs, run the predicate, and select the job only if
+  // the job description matches the component updater jobs.
+  for (ULONG i = 0; i != job_count; ++i) {
+    ComPtr<IBackgroundCopyJob> current_job;
+    if (enum_jobs->Next(1, current_job.GetAddressOf(), nullptr) == S_OK &&
+        pred(current_job)) {
+      base::string16 job_name;
+      hr = GetJobDisplayName(current_job, &job_name);
+      if (job_name.compare(kJobName) == 0)
+        jobs->push_back(current_job);
+    }
+  }
+
+  return jobs->empty() ? S_FALSE : S_OK;
+}
+
+bool JobCreationOlderThanDaysPredicate(ComPtr<IBackgroundCopyJob> job,
+                                       int num_days) {
+  BG_JOB_TIMES times = {};
+  HRESULT hr = job->GetTimes(&times);
+  if (FAILED(hr))
+    return false;
+
+  const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days));
+  const base::Time creation_time(base::Time::FromFileTime(times.CreationTime));
+
+  return creation_time + time_delta < base::Time::Now();
+}
+
+bool JobFileUrlEqualPredicate(ComPtr<IBackgroundCopyJob> job, const GURL& url) {
+  std::vector<ComPtr<IBackgroundCopyFile>> files;
+  HRESULT hr = GetFilesInJob(job, &files);
+  if (FAILED(hr))
+    return false;
+
+  for (size_t i = 0; i != files.size(); ++i) {
+    ScopedCoMem<base::char16> remote_name;
+    if (SUCCEEDED(files[i]->GetRemoteName(&remote_name)) &&
+        url == GURL(base::StringPiece16(remote_name)))
+      return true;
+  }
+
+  return false;
+}
+
+// Creates an instance of the BITS manager.
+HRESULT CreateBitsManager(ComPtr<IBackgroundCopyManager>* bits_manager) {
+  ComPtr<IBackgroundCopyManager> local_bits_manager;
+  HRESULT hr =
+      ::CoCreateInstance(__uuidof(BackgroundCopyManager), nullptr, CLSCTX_ALL,
+                         IID_PPV_ARGS(&local_bits_manager));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  *bits_manager = local_bits_manager;
+  return S_OK;
+}
+
+void CleanupJob(const ComPtr<IBackgroundCopyJob>& job) {
+  if (!job)
+    return;
+
+  // Get the file paths associated with this job before canceling the job.
+  // Canceling the job removes it from the BITS queue right away. It appears
+  // that it is still possible to query for the properties of the job after
+  // the job has been canceled. It seems safer though to get the paths first.
+  std::vector<ComPtr<IBackgroundCopyFile>> files;
+  GetFilesInJob(job, &files);
+
+  std::vector<base::FilePath> paths;
+  for (const auto& file : files) {
+    base::string16 local_name;
+    HRESULT hr = GetJobFileProperties(file, &local_name, nullptr, nullptr);
+    if (SUCCEEDED(hr))
+      paths.push_back(base::FilePath(local_name));
+  }
+
+  job->Cancel();
+
+  for (const auto& path : paths)
+    DeleteFileAndEmptyParentDirectory(path);
+}
+
+}  // namespace
+
+BackgroundDownloader::BackgroundDownloader(
+    std::unique_ptr<CrxDownloader> successor)
+    : CrxDownloader(std::move(successor)),
+      com_task_runner_(
+          base::CreateCOMSTATaskRunner(kTaskTraitsBackgroundDownloader)),
+      git_cookie_bits_manager_(0),
+      git_cookie_job_(0) {}
+
+BackgroundDownloader::~BackgroundDownloader() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  timer_.reset();
+}
+
+void BackgroundDownloader::StartTimer() {
+  timer_.reset(new base::OneShotTimer);
+  timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(kJobPollingIntervalSec),
+                this, &BackgroundDownloader::OnTimer);
+}
+
+void BackgroundDownloader::OnTimer() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  com_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloading,
+                                base::Unretained(this)));
+}
+
+void BackgroundDownloader::DoStartDownload(const GURL& url) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  com_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BackgroundDownloader::BeginDownload,
+                                base::Unretained(this), url));
+}
+
+// Called one time when this class is asked to do a download.
+void BackgroundDownloader::BeginDownload(const GURL& url) {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+
+  download_start_time_ = base::TimeTicks::Now();
+  job_stuck_begin_time_ = download_start_time_;
+
+  HRESULT hr = BeginDownloadHelper(url);
+  if (FAILED(hr)) {
+    EndDownload(hr);
+    return;
+  }
+
+  VLOG(1) << "Starting BITS download for: " << url.spec();
+
+  ResetInterfacePointers();
+  main_task_runner()->PostTask(FROM_HERE,
+                               base::BindOnce(&BackgroundDownloader::StartTimer,
+                                              base::Unretained(this)));
+}
+
+// Creates or opens an existing BITS job to download the |url|, and handles
+// the marshalling of the interfaces in GIT.
+HRESULT BackgroundDownloader::BeginDownloadHelper(const GURL& url) {
+  ComPtr<IGlobalInterfaceTable> git;
+  HRESULT hr = GetGit(&git);
+  if (FAILED(hr))
+    return hr;
+
+  hr = CreateBitsManager(&bits_manager_);
+  if (FAILED(hr))
+    return hr;
+
+  hr = QueueBitsJob(url, &job_);
+  if (FAILED(hr))
+    return hr;
+
+  hr = RegisterInterfaceInGit(git, bits_manager_, &git_cookie_bits_manager_);
+  if (FAILED(hr))
+    return hr;
+
+  hr = RegisterInterfaceInGit(git, job_, &git_cookie_job_);
+  if (FAILED(hr))
+    return hr;
+
+  return S_OK;
+}
+
+// Called any time the timer fires.
+void BackgroundDownloader::OnDownloading() {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+
+  HRESULT hr = UpdateInterfacePointers();
+  if (FAILED(hr)) {
+    EndDownload(hr);
+    return;
+  }
+
+  BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
+  hr = job_->GetState(&job_state);
+  if (FAILED(hr)) {
+    EndDownload(hr);
+    return;
+  }
+
+  bool is_handled =  false;
+  switch (job_state) {
+    case BG_JOB_STATE_TRANSFERRED:
+      is_handled = OnStateTransferred();
+      break;
+
+    case BG_JOB_STATE_ERROR:
+      is_handled = OnStateError();
+      break;
+
+    case BG_JOB_STATE_CANCELLED:
+      is_handled = OnStateCancelled();
+      break;
+
+    case BG_JOB_STATE_ACKNOWLEDGED:
+      is_handled = OnStateAcknowledged();
+      break;
+
+    case BG_JOB_STATE_QUEUED:
+    // Fall through.
+    case BG_JOB_STATE_CONNECTING:
+    // Fall through.
+    case BG_JOB_STATE_SUSPENDED:
+      is_handled = OnStateQueued();
+      break;
+
+    case BG_JOB_STATE_TRANSIENT_ERROR:
+      is_handled = OnStateTransientError();
+      break;
+
+    case BG_JOB_STATE_TRANSFERRING:
+      is_handled = OnStateTransferring();
+      break;
+
+    default:
+      break;
+  }
+
+  if (is_handled)
+    return;
+
+  ResetInterfacePointers();
+  main_task_runner()->PostTask(FROM_HERE,
+                               base::BindOnce(&BackgroundDownloader::StartTimer,
+                                              base::Unretained(this)));
+}
+
+// Completes the BITS download, picks up the file path of the response, and
+// notifies the CrxDownloader. The function should be called only once.
+void BackgroundDownloader::EndDownload(HRESULT error) {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+
+  const base::TimeTicks download_end_time(base::TimeTicks::Now());
+  const base::TimeDelta download_time =
+      download_end_time >= download_start_time_
+          ? download_end_time - download_start_time_
+          : base::TimeDelta();
+
+  int64_t downloaded_bytes = -1;
+  int64_t total_bytes = -1;
+  GetJobByteCount(job_, &downloaded_bytes, &total_bytes);
+
+  if (FAILED(error))
+    CleanupJob(job_);
+
+  ClearGit();
+
+  // Consider the url handled if it has been successfully downloaded or a
+  // 5xx has been received.
+  const bool is_handled =
+      SUCCEEDED(error) || IsHttpServerError(GetHttpStatusFromBitsError(error));
+
+  const int error_to_report = SUCCEEDED(error) ? 0 : error;
+
+  DCHECK(static_cast<bool>(error_to_report) == !base::PathExists(response_));
+
+  DownloadMetrics download_metrics;
+  download_metrics.url = url();
+  download_metrics.downloader = DownloadMetrics::kBits;
+  download_metrics.error = error_to_report;
+  download_metrics.downloaded_bytes = downloaded_bytes;
+  download_metrics.total_bytes = total_bytes;
+  download_metrics.download_time_ms = download_time.InMilliseconds();
+
+  Result result;
+  result.error = error_to_report;
+  if (!result.error)
+    result.response = response_;
+  main_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadComplete,
+                                base::Unretained(this), is_handled, result,
+                                download_metrics));
+
+  // Once the task is posted to the the main thread, this object may be deleted
+  // by its owner. It is not safe to access members of this object on this task
+  // runner from now on.
+}
+
+// Called when the BITS job has been transferred successfully. Completes the
+// BITS job by removing it from the BITS queue and making the download
+// available to the caller.
+bool BackgroundDownloader::OnStateTransferred() {
+  EndDownload(CompleteJob());
+  return true;
+}
+
+// Called when the job has encountered an error and no further progress can
+// be made. Cancels this job and removes it from the BITS queue.
+bool BackgroundDownloader::OnStateError() {
+  HRESULT error_code = S_OK;
+  HRESULT hr = GetJobError(job_, &error_code);
+  if (FAILED(hr))
+    error_code = hr;
+
+  DCHECK(FAILED(error_code));
+  EndDownload(error_code);
+  return true;
+}
+
+// Called when the download was completed. This notification is not seen
+// in the current implementation but provided here as a defensive programming
+// measure.
+bool BackgroundDownloader::OnStateAcknowledged() {
+  EndDownload(E_UNEXPECTED);
+  return true;
+}
+
+// Called when the download was cancelled. Same as above.
+bool BackgroundDownloader::OnStateCancelled() {
+  EndDownload(E_UNEXPECTED);
+  return true;
+}
+
+// Called when the job has encountered a transient error, such as a
+// network disconnect, a server error, or some other recoverable error.
+bool BackgroundDownloader::OnStateTransientError() {
+  // If the job appears to be stuck, handle the transient error as if
+  // it were a final error. This causes the job to be cancelled and a specific
+  // error be returned, if the error was available.
+  if (IsStuck()) {
+    return OnStateError();
+  }
+
+  // Don't retry at all if the transient error was a 5xx.
+  HRESULT error_code = S_OK;
+  HRESULT hr = GetJobError(job_, &error_code);
+  if (SUCCEEDED(hr) &&
+      IsHttpServerError(GetHttpStatusFromBitsError(error_code))) {
+    return OnStateError();
+  }
+
+  return false;
+}
+
+bool BackgroundDownloader::OnStateQueued() {
+  if (!IsStuck())
+    return false;
+
+  // Terminate the download if the job has not made progress in a while.
+  EndDownload(E_ABORT);
+  return true;
+}
+
+bool BackgroundDownloader::OnStateTransferring() {
+  // Resets the baseline for detecting a stuck job since the job is transferring
+  // data and it is making progress.
+  job_stuck_begin_time_ = base::TimeTicks::Now();
+
+  main_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadProgress,
+                                base::Unretained(this)));
+  return false;
+}
+
+HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url,
+                                           ComPtr<IBackgroundCopyJob>* job) {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+
+  size_t num_jobs = std::numeric_limits<size_t>::max();
+  HRESULT hr = GetBackgroundDownloaderJobCount(&num_jobs);
+
+  // The metric records a large number if the job count can't be read.
+  UMA_HISTOGRAM_COUNTS_100("UpdateClient.BackgroundDownloaderJobs", num_jobs);
+
+  // Remove some old jobs from the BITS queue before creating new jobs.
+  CleanupStaleJobs();
+
+  if (FAILED(hr) || num_jobs >= kMaxQueuedJobs)
+    return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF,
+                        CrxDownloaderError::BITS_TOO_MANY_JOBS);
+
+  ComPtr<IBackgroundCopyJob> local_job;
+  hr = CreateOrOpenJob(url, &local_job);
+  if (FAILED(hr)) {
+    CleanupJob(local_job);
+    return hr;
+  }
+
+  const bool is_new_job = hr == S_OK;
+  UMA_HISTOGRAM_BOOLEAN("UpdateClient.BackgroundDownloaderNewJob", is_new_job);
+
+  hr = local_job->Resume();
+  if (FAILED(hr)) {
+    CleanupJob(local_job);
+    return hr;
+  }
+
+  *job = local_job;
+  return S_OK;
+}
+
+HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url,
+                                              ComPtr<IBackgroundCopyJob>* job) {
+  std::vector<ComPtr<IBackgroundCopyJob>> jobs;
+  HRESULT hr = FindBitsJobIf(
+      [&url](ComPtr<IBackgroundCopyJob> job) {
+        return JobFileUrlEqualPredicate(job, url);
+      },
+      bits_manager_, &jobs);
+  if (SUCCEEDED(hr) && !jobs.empty()) {
+    *job = jobs.front();
+    return S_FALSE;
+  }
+
+  ComPtr<IBackgroundCopyJob> local_job;
+
+  GUID guid = {0};
+  hr = bits_manager_->CreateJob(kJobName, BG_JOB_TYPE_DOWNLOAD, &guid,
+                                local_job.GetAddressOf());
+  if (FAILED(hr)) {
+    CleanupJob(local_job);
+    return hr;
+  }
+
+  hr = InitializeNewJob(local_job, url);
+  if (FAILED(hr)) {
+    CleanupJob(local_job);
+    return hr;
+  }
+
+  *job = local_job;
+  return S_OK;
+}
+
+HRESULT BackgroundDownloader::InitializeNewJob(
+    const ComPtr<IBackgroundCopyJob>& job,
+    const GURL& url) {
+  base::FilePath tempdir;
+  if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_BITS_"),
+                                    &tempdir))
+    return E_FAIL;
+
+  const base::string16 filename(base::SysUTF8ToWide(url.ExtractFileName()));
+  HRESULT hr = job->AddFile(base::SysUTF8ToWide(url.spec()).c_str(),
+                            tempdir.Append(filename).AsUTF16Unsafe().c_str());
+  if (FAILED(hr))
+    return hr;
+
+  hr = job->SetDescription(filename.c_str());
+  if (FAILED(hr))
+    return hr;
+
+  hr = job->SetPriority(BG_JOB_PRIORITY_NORMAL);
+  if (FAILED(hr))
+    return hr;
+
+  hr = job->SetMinimumRetryDelay(60 * kMinimumRetryDelayMin);
+  if (FAILED(hr))
+    return hr;
+
+  const int kSecondsDay = 60 * 60 * 24;
+  hr = job->SetNoProgressTimeout(kSecondsDay * kSetNoProgressTimeoutDays);
+  if (FAILED(hr))
+    return hr;
+
+  return S_OK;
+}
+
+bool BackgroundDownloader::IsStuck() {
+  const base::TimeDelta job_stuck_timeout(
+      base::TimeDelta::FromMinutes(kJobStuckTimeoutMin));
+  return job_stuck_begin_time_ + job_stuck_timeout < base::TimeTicks::Now();
+}
+
+HRESULT BackgroundDownloader::CompleteJob() {
+  HRESULT hr = job_->Complete();
+  if (FAILED(hr) && hr != BG_S_UNABLE_TO_DELETE_FILES)
+    return hr;
+
+  std::vector<ComPtr<IBackgroundCopyFile>> files;
+  hr = GetFilesInJob(job_, &files);
+  if (FAILED(hr))
+    return hr;
+
+  if (files.empty())
+    return E_UNEXPECTED;
+
+  base::string16 local_name;
+  BG_FILE_PROGRESS progress = {0};
+  hr = GetJobFileProperties(files.front(), &local_name, nullptr, &progress);
+  if (FAILED(hr))
+    return hr;
+
+  // Sanity check the post-conditions of a successful download, including
+  // the file and job invariants. The byte counts for a job and its file
+  // must match as a job only contains one file.
+  DCHECK(progress.Completed);
+  DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred);
+
+  response_ = base::FilePath(local_name);
+
+  return S_OK;
+}
+
+HRESULT BackgroundDownloader::UpdateInterfacePointers() {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+
+  bits_manager_ = nullptr;
+  job_ = nullptr;
+
+  ComPtr<IGlobalInterfaceTable> git;
+  HRESULT hr = GetGit(&git);
+  if (FAILED(hr))
+    return hr;
+
+  hr = GetInterfaceFromGit(git, git_cookie_bits_manager_,
+                           IID_PPV_ARGS(bits_manager_.GetAddressOf()));
+  if (FAILED(hr))
+    return hr;
+
+  hr = GetInterfaceFromGit(git, git_cookie_job_,
+                           IID_PPV_ARGS(job_.GetAddressOf()));
+  if (FAILED(hr))
+    return hr;
+
+  return S_OK;
+}
+
+void BackgroundDownloader::ResetInterfacePointers() {
+  job_ = nullptr;
+  bits_manager_ = nullptr;
+}
+
+HRESULT BackgroundDownloader::ClearGit() {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+
+  ResetInterfacePointers();
+
+  ComPtr<IGlobalInterfaceTable> git;
+  HRESULT hr = GetGit(&git);
+  if (FAILED(hr))
+    return hr;
+
+  const DWORD cookies[] = {
+      git_cookie_job_, git_cookie_bits_manager_
+  };
+
+  for (auto cookie : cookies) {
+    // TODO(sorin): check the result of the call, see crbug.com/644857.
+    git->RevokeInterfaceFromGlobal(cookie);
+  }
+
+  return S_OK;
+}
+
+HRESULT BackgroundDownloader::GetBackgroundDownloaderJobCount(
+    size_t* num_jobs) {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(bits_manager_);
+
+  std::vector<ComPtr<IBackgroundCopyJob>> jobs;
+  const HRESULT hr =
+      FindBitsJobIf([](const ComPtr<IBackgroundCopyJob>&) { return true; },
+                    bits_manager_, &jobs);
+  if (FAILED(hr))
+    return hr;
+
+  *num_jobs = jobs.size();
+  return S_OK;
+}
+
+void BackgroundDownloader::CleanupStaleJobs() {
+  DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(bits_manager_);
+
+  static base::Time last_sweep;
+
+  const base::TimeDelta time_delta(
+      base::TimeDelta::FromDays(kPurgeStaleJobsIntervalBetweenChecksDays));
+  const base::Time current_time(base::Time::Now());
+  if (last_sweep + time_delta > current_time)
+    return;
+
+  last_sweep = current_time;
+
+  std::vector<ComPtr<IBackgroundCopyJob>> jobs;
+  FindBitsJobIf(
+      [](ComPtr<IBackgroundCopyJob> job) {
+        return JobCreationOlderThanDaysPredicate(job, kPurgeStaleJobsAfterDays);
+      },
+      bits_manager_, &jobs);
+
+  for (const auto& job : jobs)
+    CleanupJob(job);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/background_downloader_win.h b/src/updater_components/update_client/background_downloader_win.h
new file mode 100644
index 0000000..28b9693
--- /dev/null
+++ b/src/updater_components/update_client/background_downloader_win.h
@@ -0,0 +1,157 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_
+#define COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_
+
+#include <bits.h>
+#include <windows.h>
+#include <wrl/client.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/update_client/crx_downloader.h"
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+}
+
+namespace update_client {
+
+// Implements a downloader in terms of the BITS service. The public interface
+// of this class and the CrxDownloader overrides are expected to be called
+// from the main thread. The rest of the class code runs on a sequenced
+// task runner, usually associated with a blocking thread pool. The task runner
+// must initialize COM.
+//
+// This class manages a COM client for Windows BITS. The client uses polling,
+// triggered by an one-shot timer, to get state updates from BITS. Since the
+// timer has thread afinity, the callbacks from the timer are delegated to
+// a sequenced task runner, which handles all client COM interaction with
+// the BITS service.
+class BackgroundDownloader : public CrxDownloader {
+ public:
+  explicit BackgroundDownloader(std::unique_ptr<CrxDownloader> successor);
+  ~BackgroundDownloader() override;
+
+ private:
+  // Overrides for CrxDownloader.
+  void DoStartDownload(const GURL& url) override;
+
+  // Called asynchronously on the |com_task_runner_| at different stages during
+  // the download. |OnDownloading| can be called multiple times.
+  // |EndDownload| switches the execution flow from the |com_task_runner_| to
+  // the main thread. Accessing any data members of this object from the
+  // |com_task_runner_| after calling |EndDownload| is unsafe.
+  void BeginDownload(const GURL& url);
+  void OnDownloading();
+  void EndDownload(HRESULT hr);
+
+  HRESULT BeginDownloadHelper(const GURL& url);
+
+  // Handles the job state transitions to a final state. Returns true always
+  // since the download has reached a final state and no further processing for
+  // this download is needed.
+  bool OnStateTransferred();
+  bool OnStateError();
+  bool OnStateCancelled();
+  bool OnStateAcknowledged();
+
+  // Handles the transition to a transient state where the job is in the
+  // queue but not actively transferring data. Returns true if the download has
+  // been in this state for too long and it will be abandoned, or false, if
+  // further processing for this download is needed.
+  bool OnStateQueued();
+
+  // Handles the job state transition to a transient error state, which may or
+  // may not be considered final, depending on the error. Returns true if
+  // the state is final, or false, if the download is allowed to continue.
+  bool OnStateTransientError();
+
+  // Handles the job state corresponding to transferring data. Returns false
+  // always since this is never a final state.
+  bool OnStateTransferring();
+
+  void StartTimer();
+  void OnTimer();
+
+  // Creates or opens a job for the given url and queues it up. Returns S_OK if
+  // a new job was created or S_FALSE if an existing job for the |url| was found
+  // in the BITS queue.
+  HRESULT QueueBitsJob(const GURL& url,
+                       Microsoft::WRL::ComPtr<IBackgroundCopyJob>* job);
+  HRESULT CreateOrOpenJob(const GURL& url,
+                          Microsoft::WRL::ComPtr<IBackgroundCopyJob>* job);
+  HRESULT InitializeNewJob(
+      const Microsoft::WRL::ComPtr<IBackgroundCopyJob>& job,
+      const GURL& url);
+
+  // Returns true if at the time of the call, it appears that the job
+  // has not been making progress toward completion.
+  bool IsStuck();
+
+  // Makes the downloaded file available to the caller by renaming the
+  // temporary file to its destination and removing it from the BITS queue.
+  HRESULT CompleteJob();
+
+  // Revokes the interface pointers from GIT.
+  HRESULT ClearGit();
+
+  // Updates the BITS interface pointers so that they can be used by the
+  // thread calling the function. Call this function to get valid COM interface
+  // pointers when a thread from the thread pool enters the object.
+  HRESULT UpdateInterfacePointers();
+
+  // Resets the BITS interface pointers. Call this function when a thread
+  // from the thread pool leaves the object to release the interface pointers.
+  void ResetInterfacePointers();
+
+  // Returns the number of jobs in the BITS queue which were created by this
+  // downloader.
+  HRESULT GetBackgroundDownloaderJobCount(size_t* num_jobs);
+
+  // Cleans up incompleted jobs that are too old.
+  void CleanupStaleJobs();
+
+  // Ensures that we are running on the same thread we created the object on.
+  base::ThreadChecker thread_checker_;
+
+  // Executes blocking COM calls to BITS.
+  scoped_refptr<base::SequencedTaskRunner> com_task_runner_;
+
+  // The timer has thread affinity. This member is initialized and destroyed
+  // on the main task runner.
+  std::unique_ptr<base::OneShotTimer> timer_;
+
+  DWORD git_cookie_bits_manager_;
+  DWORD git_cookie_job_;
+
+  // COM interface pointers are valid for the thread that called
+  // |UpdateInterfacePointers| to get pointers to COM proxies, which are valid
+  // for that thread only.
+  Microsoft::WRL::ComPtr<IBackgroundCopyManager> bits_manager_;
+  Microsoft::WRL::ComPtr<IBackgroundCopyJob> job_;
+
+  // Contains the time when the download of the current url has started.
+  base::TimeTicks download_start_time_;
+
+  // Contains the time when the BITS job is last seen making progress.
+  base::TimeTicks job_stuck_begin_time_;
+
+  // Contains the path of the downloaded file if the download was successful.
+  base::FilePath response_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundDownloader);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_BACKGROUND_DOWNLOADER_WIN_H_
diff --git a/src/updater_components/update_client/command_line_config_policy.cc b/src/updater_components/update_client/command_line_config_policy.cc
new file mode 100644
index 0000000..899d8ac
--- /dev/null
+++ b/src/updater_components/update_client/command_line_config_policy.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 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 "components/update_client/command_line_config_policy.h"
+
+#include "build/build_config.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+bool CommandLineConfigPolicy::BackgroundDownloadsEnabled() const {
+#if defined(OS_WIN)
+  return true;
+#else
+  return false;
+#endif
+}
+
+bool CommandLineConfigPolicy::DeltaUpdatesEnabled() const {
+  return true;
+}
+
+bool CommandLineConfigPolicy::FastUpdate() const {
+  return false;
+}
+
+bool CommandLineConfigPolicy::PingsEnabled() const {
+  return true;
+}
+
+bool CommandLineConfigPolicy::TestRequest() const {
+  return false;
+}
+
+GURL CommandLineConfigPolicy::UrlSourceOverride() const {
+  return GURL();
+}
+
+int CommandLineConfigPolicy::InitialDelay() const {
+  return 0;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/command_line_config_policy.h b/src/updater_components/update_client/command_line_config_policy.h
new file mode 100644
index 0000000..2d201a5
--- /dev/null
+++ b/src/updater_components/update_client/command_line_config_policy.h
@@ -0,0 +1,44 @@
+// Copyright 2018 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 COMPONENTS_UPDATE_CLIENT_COMMAND_LINE_CONFIG_POLICY_H_
+#define COMPONENTS_UPDATE_CLIENT_COMMAND_LINE_CONFIG_POLICY_H_
+
+class GURL;
+
+namespace update_client {
+
+// This class provides additional settings from command line switches to the
+// main configurator.
+class CommandLineConfigPolicy {
+ public:
+  // If true, background downloads are enabled.
+  virtual bool BackgroundDownloadsEnabled() const;
+
+  // If true, differential updates are enabled.
+  virtual bool DeltaUpdatesEnabled() const;
+
+  // If true, speed up the initial update checking.
+  virtual bool FastUpdate() const;
+
+  // If true, pings are enabled. Pings are the requests sent to the update
+  // server that report the success or failure of installs or update attempts.
+  virtual bool PingsEnabled() const;
+
+  // If true, add "testrequest" attribute to update check requests.
+  virtual bool TestRequest() const;
+
+  // The override URL for updates. Can be empty.
+  virtual GURL UrlSourceOverride() const;
+
+  // If non-zero, time interval in seconds until the first component
+  // update check.
+  virtual int InitialDelay() const;
+
+  virtual ~CommandLineConfigPolicy() {}
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_COMMAND_LINE_CONFIG_POLICY_H_
diff --git a/src/updater_components/update_client/component.cc b/src/updater_components/update_client/component.cc
new file mode 100644
index 0000000..7f5ecb5
--- /dev/null
+++ b/src/updater_components/update_client/component.cc
@@ -0,0 +1,946 @@
+// 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 "components/update_client/component.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "components/update_client/action_runner.h"
+#include "components/update_client/component_unpacker.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/network.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/protocol_definition.h"
+#include "components/update_client/protocol_serializer.h"
+#include "components/update_client/task_traits.h"
+#include "components/update_client/unzipper.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/update_engine.h"
+#include "components/update_client/utils.h"
+
+// The state machine representing how a CRX component changes during an update.
+//
+//     +------------------------- kNew
+//     |                            |
+//     |                            V
+//     |                        kChecking
+//     |                            |
+//     V                error       V     no           no
+//  kUpdateError <------------- [update?] -> [action?] -> kUpToDate  kUpdated
+//     ^                            |           |            ^        ^
+//     |                        yes |           | yes        |        |
+//     |                            V           |            |        |
+//     |                        kCanUpdate      +--------> kRun       |
+//     |                            |                                 |
+//     |                no          V                                 |
+//     |               +-<- [differential update?]                    |
+//     |               |               |                              |
+//     |               |           yes |                              |
+//     |               | error         V                              |
+//     |               +-<----- kDownloadingDiff            kRun---->-+
+//     |               |               |                     ^        |
+//     |               |               |                 yes |        |
+//     |               | error         V                     |        |
+//     |               +-<----- kUpdatingDiff ---------> [action?] ->-+
+//     |               |                                     ^     no
+//     |    error      V                                     |
+//     +-<-------- kDownloading                              |
+//     |               |                                     |
+//     |               |                                     |
+//     |    error      V                                     |
+//     +-<-------- kUpdating --------------------------------+
+
+namespace update_client {
+
+namespace {
+
+using InstallOnBlockingTaskRunnerCompleteCallback = base::OnceCallback<
+    void(ErrorCategory error_category, int error_code, int extra_code1)>;
+
+void InstallComplete(
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+    InstallOnBlockingTaskRunnerCompleteCallback callback,
+    const base::FilePath& unpack_path,
+    const CrxInstaller::Result& result) {
+  base::PostTask(
+      FROM_HERE,
+      {base::ThreadPool(), base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+      base::BindOnce(
+          [](scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+             InstallOnBlockingTaskRunnerCompleteCallback callback,
+             const base::FilePath& unpack_path,
+             const CrxInstaller::Result& result) {
+            base::DeleteFile(unpack_path, true);
+            const ErrorCategory error_category =
+                result.error ? ErrorCategory::kInstall : ErrorCategory::kNone;
+            main_task_runner->PostTask(
+                FROM_HERE, base::BindOnce(std::move(callback), error_category,
+                                          static_cast<int>(result.error),
+                                          result.extended_error));
+          },
+          main_task_runner, std::move(callback), unpack_path, result));
+}
+
+void InstallOnBlockingTaskRunner(
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+    const base::FilePath& unpack_path,
+    const std::string& public_key,
+    const std::string& fingerprint,
+    scoped_refptr<CrxInstaller> installer,
+    InstallOnBlockingTaskRunnerCompleteCallback callback) {
+  DCHECK(base::DirectoryExists(unpack_path));
+
+  // Acquire the ownership of the |unpack_path|.
+  base::ScopedTempDir unpack_path_owner;
+  ignore_result(unpack_path_owner.Set(unpack_path));
+
+  if (static_cast<int>(fingerprint.size()) !=
+      base::WriteFile(
+          unpack_path.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
+          fingerprint.c_str(), base::checked_cast<int>(fingerprint.size()))) {
+    const CrxInstaller::Result result(InstallError::FINGERPRINT_WRITE_FAILED);
+    main_task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback), ErrorCategory::kInstall,
+                       static_cast<int>(result.error), result.extended_error));
+    return;
+  }
+
+  installer->Install(
+      unpack_path, public_key,
+      base::BindOnce(&InstallComplete, main_task_runner, std::move(callback),
+                     unpack_path_owner.Take()));
+}
+
+void UnpackCompleteOnBlockingTaskRunner(
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+    const base::FilePath& crx_path,
+    const std::string& fingerprint,
+    scoped_refptr<CrxInstaller> installer,
+    InstallOnBlockingTaskRunnerCompleteCallback callback,
+    const ComponentUnpacker::Result& result) {
+  update_client::DeleteFileAndEmptyParentDirectory(crx_path);
+
+  if (result.error != UnpackerError::kNone) {
+    main_task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback), ErrorCategory::kUnpack,
+                       static_cast<int>(result.error), result.extended_error));
+    return;
+  }
+
+  base::PostTask(FROM_HERE, kTaskTraits,
+                 base::BindOnce(&InstallOnBlockingTaskRunner, main_task_runner,
+                                result.unpack_path, result.public_key,
+                                fingerprint, installer, std::move(callback)));
+}
+
+void StartInstallOnBlockingTaskRunner(
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+    const std::vector<uint8_t>& pk_hash,
+    const base::FilePath& crx_path,
+    const std::string& fingerprint,
+    scoped_refptr<CrxInstaller> installer,
+    std::unique_ptr<Unzipper> unzipper_,
+    scoped_refptr<Patcher> patcher_,
+    crx_file::VerifierFormat crx_format,
+    InstallOnBlockingTaskRunnerCompleteCallback callback) {
+  auto unpacker = base::MakeRefCounted<ComponentUnpacker>(
+      pk_hash, crx_path, installer, std::move(unzipper_), std::move(patcher_),
+      crx_format);
+
+  unpacker->Unpack(base::BindOnce(&UnpackCompleteOnBlockingTaskRunner,
+                                  main_task_runner, crx_path, fingerprint,
+                                  installer, std::move(callback)));
+}
+
+// Returns a string literal corresponding to the value of the downloader |d|.
+const char* DownloaderToString(CrxDownloader::DownloadMetrics::Downloader d) {
+  switch (d) {
+    case CrxDownloader::DownloadMetrics::kUrlFetcher:
+      return "direct";
+    case CrxDownloader::DownloadMetrics::kBits:
+      return "bits";
+    default:
+      return "unknown";
+  }
+}
+
+}  // namespace
+
+Component::Component(const UpdateContext& update_context, const std::string& id)
+    : id_(id),
+      state_(std::make_unique<StateNew>(this)),
+      update_context_(update_context) {}
+
+Component::~Component() {}
+
+scoped_refptr<Configurator> Component::config() const {
+  return update_context_.config;
+}
+
+std::string Component::session_id() const {
+  return update_context_.session_id;
+}
+
+bool Component::is_foreground() const {
+  return update_context_.is_foreground;
+}
+
+void Component::Handle(CallbackHandleComplete callback_handle_complete) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(state_);
+
+  callback_handle_complete_ = std::move(callback_handle_complete);
+
+  state_->Handle(
+      base::BindOnce(&Component::ChangeState, base::Unretained(this)));
+}
+
+void Component::ChangeState(std::unique_ptr<State> next_state) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  previous_state_ = state();
+  if (next_state)
+    state_ = std::move(next_state);
+  else
+    is_handled_ = true;
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, std::move(callback_handle_complete_));
+}
+
+CrxUpdateItem Component::GetCrxUpdateItem() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  CrxUpdateItem crx_update_item;
+  crx_update_item.state = state_->state();
+  crx_update_item.id = id_;
+  if (crx_component_)
+    crx_update_item.component = *crx_component_;
+  crx_update_item.last_check = last_check_;
+  crx_update_item.next_version = next_version_;
+  crx_update_item.next_fp = next_fp_;
+  crx_update_item.error_category = error_category_;
+  crx_update_item.error_code = error_code_;
+  crx_update_item.extra_code1 = extra_code1_;
+
+  return crx_update_item;
+}
+
+void Component::SetParseResult(const ProtocolParser::Result& result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DCHECK_EQ(0, update_check_error_);
+
+  status_ = result.status;
+  action_run_ = result.action_run;
+
+  if (result.manifest.packages.empty())
+    return;
+
+  next_version_ = base::Version(result.manifest.version);
+  const auto& package = result.manifest.packages.front();
+  next_fp_ = package.fingerprint;
+
+  // Resolve the urls by combining the base urls with the package names.
+  for (const auto& crx_url : result.crx_urls) {
+    const GURL url = crx_url.Resolve(package.name);
+    if (url.is_valid())
+      crx_urls_.push_back(url);
+  }
+  for (const auto& crx_diffurl : result.crx_diffurls) {
+    const GURL url = crx_diffurl.Resolve(package.namediff);
+    if (url.is_valid())
+      crx_diffurls_.push_back(url);
+  }
+
+  hash_sha256_ = package.hash_sha256;
+  hashdiff_sha256_ = package.hashdiff_sha256;
+}
+
+void Component::Uninstall(const base::Version& version, int reason) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DCHECK_EQ(ComponentState::kNew, state());
+
+  crx_component_ = CrxComponent();
+  crx_component_->version = version;
+
+  previous_version_ = version;
+  next_version_ = base::Version("0");
+  extra_code1_ = reason;
+
+  state_ = std::make_unique<StateUninstalled>(this);
+}
+
+void Component::SetUpdateCheckResult(
+    const base::Optional<ProtocolParser::Result>& result,
+    ErrorCategory error_category,
+    int error) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(ComponentState::kChecking, state());
+
+  error_category_ = error_category;
+  error_code_ = error;
+  if (result)
+    SetParseResult(result.value());
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, std::move(update_check_complete_));
+}
+
+bool Component::CanDoBackgroundDownload() const {
+  // Foreground component updates are always downloaded in foreground.
+  return !is_foreground() &&
+         (crx_component() && crx_component()->allows_background_download) &&
+         update_context_.config->EnabledBackgroundDownloader();
+}
+
+void Component::AppendEvent(base::Value event) {
+  events_.push_back(std::move(event));
+}
+
+void Component::NotifyObservers(UpdateClient::Observer::Events event) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  update_context_.notify_observers_callback.Run(event, id_);
+}
+
+base::TimeDelta Component::GetUpdateDuration() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (update_begin_.is_null())
+    return base::TimeDelta();
+
+  const base::TimeDelta update_cost(base::TimeTicks::Now() - update_begin_);
+  DCHECK_GE(update_cost, base::TimeDelta());
+  const base::TimeDelta max_update_delay =
+      base::TimeDelta::FromSeconds(update_context_.config->UpdateDelay());
+  return std::min(update_cost, max_update_delay);
+}
+
+base::Value Component::MakeEventUpdateComplete() const {
+  base::Value event(base::Value::Type::DICTIONARY);
+  event.SetKey("eventtype", base::Value(3));
+  event.SetKey(
+      "eventresult",
+      base::Value(static_cast<int>(state() == ComponentState::kUpdated)));
+  if (error_category() != ErrorCategory::kNone)
+    event.SetKey("errorcat", base::Value(static_cast<int>(error_category())));
+  if (error_code())
+    event.SetKey("errorcode", base::Value(error_code()));
+  if (extra_code1())
+    event.SetKey("extracode1", base::Value(extra_code1()));
+  if (HasDiffUpdate(*this)) {
+    const int diffresult = static_cast<int>(!diff_update_failed());
+    event.SetKey("diffresult", base::Value(diffresult));
+  }
+  if (diff_error_category() != ErrorCategory::kNone) {
+    const int differrorcat = static_cast<int>(diff_error_category());
+    event.SetKey("differrorcat", base::Value(differrorcat));
+  }
+  if (diff_error_code())
+    event.SetKey("differrorcode", base::Value(diff_error_code()));
+  if (diff_extra_code1())
+    event.SetKey("diffextracode1", base::Value(diff_extra_code1()));
+  if (!previous_fp().empty())
+    event.SetKey("previousfp", base::Value(previous_fp()));
+  if (!next_fp().empty())
+    event.SetKey("nextfp", base::Value(next_fp()));
+  DCHECK(previous_version().IsValid());
+  event.SetKey("previousversion", base::Value(previous_version().GetString()));
+  if (next_version().IsValid())
+    event.SetKey("nextversion", base::Value(next_version().GetString()));
+  return event;
+}
+
+base::Value Component::MakeEventDownloadMetrics(
+    const CrxDownloader::DownloadMetrics& dm) const {
+  base::Value event(base::Value::Type::DICTIONARY);
+  event.SetKey("eventtype", base::Value(14));
+  event.SetKey("eventresult", base::Value(static_cast<int>(dm.error == 0)));
+  event.SetKey("downloader", base::Value(DownloaderToString(dm.downloader)));
+  if (dm.error)
+    event.SetKey("errorcode", base::Value(dm.error));
+  event.SetKey("url", base::Value(dm.url.spec()));
+
+  // -1 means that the  byte counts are not known.
+  if (dm.total_bytes != -1 && dm.total_bytes < kProtocolMaxInt)
+    event.SetKey("total", base::Value(static_cast<double>(dm.total_bytes)));
+  if (dm.downloaded_bytes != -1 && dm.total_bytes < kProtocolMaxInt) {
+    event.SetKey("downloaded",
+                 base::Value(static_cast<double>(dm.downloaded_bytes)));
+  }
+  if (dm.download_time_ms && dm.total_bytes < kProtocolMaxInt) {
+    event.SetKey("download_time_ms",
+                 base::Value(static_cast<double>(dm.download_time_ms)));
+  }
+  DCHECK(previous_version().IsValid());
+  event.SetKey("previousversion", base::Value(previous_version().GetString()));
+  if (next_version().IsValid())
+    event.SetKey("nextversion", base::Value(next_version().GetString()));
+  return event;
+}
+
+base::Value Component::MakeEventUninstalled() const {
+  DCHECK(state() == ComponentState::kUninstalled);
+  base::Value event(base::Value::Type::DICTIONARY);
+  event.SetKey("eventtype", base::Value(4));
+  event.SetKey("eventresult", base::Value(1));
+  if (extra_code1())
+    event.SetKey("extracode1", base::Value(extra_code1()));
+  DCHECK(previous_version().IsValid());
+  event.SetKey("previousversion", base::Value(previous_version().GetString()));
+  DCHECK(next_version().IsValid());
+  event.SetKey("nextversion", base::Value(next_version().GetString()));
+  return event;
+}
+
+base::Value Component::MakeEventActionRun(bool succeeded,
+                                          int error_code,
+                                          int extra_code1) const {
+  base::Value event(base::Value::Type::DICTIONARY);
+  event.SetKey("eventtype", base::Value(42));
+  event.SetKey("eventresult", base::Value(static_cast<int>(succeeded)));
+  if (error_code)
+    event.SetKey("errorcode", base::Value(error_code));
+  if (extra_code1)
+    event.SetKey("extracode1", base::Value(extra_code1));
+  return event;
+}
+
+std::vector<base::Value> Component::GetEvents() const {
+  std::vector<base::Value> events;
+  for (const auto& event : events_)
+    events.push_back(event.Clone());
+  return events;
+}
+
+Component::State::State(Component* component, ComponentState state)
+    : state_(state), component_(*component) {}
+
+Component::State::~State() {}
+
+void Component::State::Handle(CallbackNextState callback_next_state) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  callback_next_state_ = std::move(callback_next_state);
+
+  DoHandle();
+}
+
+void Component::State::TransitionState(std::unique_ptr<State> next_state) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(next_state);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback_next_state_), std::move(next_state)));
+}
+
+void Component::State::EndState() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback_next_state_), nullptr));
+}
+
+Component::StateNew::StateNew(Component* component)
+    : State(component, ComponentState::kNew) {}
+
+Component::StateNew::~StateNew() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateNew::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+  if (component.crx_component()) {
+    TransitionState(std::make_unique<StateChecking>(&component));
+  } else {
+    component.error_code_ = static_cast<int>(Error::CRX_NOT_FOUND);
+    component.error_category_ = ErrorCategory::kService;
+    TransitionState(std::make_unique<StateUpdateError>(&component));
+  }
+}
+
+Component::StateChecking::StateChecking(Component* component)
+    : State(component, ComponentState::kChecking) {}
+
+Component::StateChecking::~StateChecking() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+// Unlike how other states are handled, this function does not change the
+// state right away. The state transition happens when the UpdateChecker
+// calls Component::UpdateCheckComplete and |update_check_complete_| is invoked.
+// This is an artifact of how multiple components must be checked for updates
+// together but the state machine defines the transitions for one component
+// at a time.
+void Component::StateChecking::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+  DCHECK(component.crx_component());
+
+  component.last_check_ = base::TimeTicks::Now();
+  component.update_check_complete_ = base::BindOnce(
+      &Component::StateChecking::UpdateCheckComplete, base::Unretained(this));
+
+  component.NotifyObservers(Events::COMPONENT_CHECKING_FOR_UPDATES);
+}
+
+void Component::StateChecking::UpdateCheckComplete() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  auto& component = State::component();
+  if (!component.error_code_) {
+    if (component.status_ == "ok") {
+      TransitionState(std::make_unique<StateCanUpdate>(&component));
+      return;
+    }
+
+    if (component.status_ == "noupdate") {
+      if (component.action_run_.empty())
+        TransitionState(std::make_unique<StateUpToDate>(&component));
+      else
+        TransitionState(std::make_unique<StateRun>(&component));
+      return;
+    }
+  }
+
+  TransitionState(std::make_unique<StateUpdateError>(&component));
+}
+
+Component::StateUpdateError::StateUpdateError(Component* component)
+    : State(component, ComponentState::kUpdateError) {}
+
+Component::StateUpdateError::~StateUpdateError() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateUpdateError::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+
+  DCHECK_NE(ErrorCategory::kNone, component.error_category_);
+  DCHECK_NE(0, component.error_code_);
+
+  // Create an event only when the server response included an update.
+  if (component.IsUpdateAvailable())
+    component.AppendEvent(component.MakeEventUpdateComplete());
+
+  EndState();
+  component.NotifyObservers(Events::COMPONENT_UPDATE_ERROR);
+}
+
+Component::StateCanUpdate::StateCanUpdate(Component* component)
+    : State(component, ComponentState::kCanUpdate) {}
+
+Component::StateCanUpdate::~StateCanUpdate() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateCanUpdate::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+  DCHECK(component.crx_component());
+
+  component.is_update_available_ = true;
+  component.NotifyObservers(Events::COMPONENT_UPDATE_FOUND);
+
+  if (component.crx_component()
+          ->supports_group_policy_enable_component_updates &&
+      !component.update_context_.enabled_component_updates) {
+    component.error_category_ = ErrorCategory::kService;
+    component.error_code_ = static_cast<int>(ServiceError::UPDATE_DISABLED);
+    component.extra_code1_ = 0;
+    TransitionState(std::make_unique<StateUpdateError>(&component));
+    return;
+  }
+
+  // Start computing the cost of the this update from here on.
+  component.update_begin_ = base::TimeTicks::Now();
+
+  if (CanTryDiffUpdate())
+    TransitionState(std::make_unique<StateDownloadingDiff>(&component));
+  else
+    TransitionState(std::make_unique<StateDownloading>(&component));
+}
+
+// Returns true if a differential update is available, it has not failed yet,
+// and the configuration allows this update.
+bool Component::StateCanUpdate::CanTryDiffUpdate() const {
+  const auto& component = Component::State::component();
+  return HasDiffUpdate(component) && !component.diff_error_code_ &&
+         component.update_context_.config->EnabledDeltas();
+}
+
+Component::StateUpToDate::StateUpToDate(Component* component)
+    : State(component, ComponentState::kUpToDate) {}
+
+Component::StateUpToDate::~StateUpToDate() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateUpToDate::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+  DCHECK(component.crx_component());
+
+  component.NotifyObservers(Events::COMPONENT_NOT_UPDATED);
+  EndState();
+}
+
+Component::StateDownloadingDiff::StateDownloadingDiff(Component* component)
+    : State(component, ComponentState::kDownloadingDiff) {}
+
+Component::StateDownloadingDiff::~StateDownloadingDiff() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateDownloadingDiff::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const auto& component = Component::State::component();
+  const auto& update_context = component.update_context_;
+
+  DCHECK(component.crx_component());
+
+  crx_downloader_ = update_context.crx_downloader_factory(
+      component.CanDoBackgroundDownload(),
+      update_context.config->GetNetworkFetcherFactory());
+
+  const auto& id = component.id_;
+  crx_downloader_->set_progress_callback(
+      base::Bind(&Component::StateDownloadingDiff::DownloadProgress,
+                 base::Unretained(this), id));
+  crx_downloader_->StartDownload(
+      component.crx_diffurls_, component.hashdiff_sha256_,
+      base::BindOnce(&Component::StateDownloadingDiff::DownloadComplete,
+                     base::Unretained(this), id));
+
+  component.NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
+}
+
+// Called when progress is being made downloading a CRX. Can be called multiple
+// times due to how the CRX downloader switches between different downloaders
+// and fallback urls.
+void Component::StateDownloadingDiff::DownloadProgress(const std::string& id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  component().NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
+}
+
+void Component::StateDownloadingDiff::DownloadComplete(
+    const std::string& id,
+    const CrxDownloader::Result& download_result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = Component::State::component();
+  for (const auto& download_metrics : crx_downloader_->download_metrics())
+    component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics));
+
+  crx_downloader_.reset();
+
+  if (download_result.error) {
+    DCHECK(download_result.response.empty());
+    component.diff_error_category_ = ErrorCategory::kDownload;
+    component.diff_error_code_ = download_result.error;
+
+    TransitionState(std::make_unique<StateDownloading>(&component));
+    return;
+  }
+
+  component.crx_path_ = download_result.response;
+
+  TransitionState(std::make_unique<StateUpdatingDiff>(&component));
+}
+
+Component::StateDownloading::StateDownloading(Component* component)
+    : State(component, ComponentState::kDownloading) {}
+
+Component::StateDownloading::~StateDownloading() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateDownloading::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const auto& component = Component::State::component();
+  const auto& update_context = component.update_context_;
+
+  DCHECK(component.crx_component());
+
+  crx_downloader_ = update_context.crx_downloader_factory(
+      component.CanDoBackgroundDownload(),
+      update_context.config->GetNetworkFetcherFactory());
+
+  const auto& id = component.id_;
+  crx_downloader_->set_progress_callback(
+      base::Bind(&Component::StateDownloading::DownloadProgress,
+                 base::Unretained(this), id));
+  crx_downloader_->StartDownload(
+      component.crx_urls_, component.hash_sha256_,
+      base::BindOnce(&Component::StateDownloading::DownloadComplete,
+                     base::Unretained(this), id));
+
+  component.NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
+}
+
+// Called when progress is being made downloading a CRX. Can be called multiple
+// times due to how the CRX downloader switches between different downloaders
+// and fallback urls.
+void Component::StateDownloading::DownloadProgress(const std::string& id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  component().NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING);
+}
+
+void Component::StateDownloading::DownloadComplete(
+    const std::string& id,
+    const CrxDownloader::Result& download_result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = Component::State::component();
+
+  for (const auto& download_metrics : crx_downloader_->download_metrics())
+    component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics));
+
+  crx_downloader_.reset();
+
+  if (download_result.error) {
+    DCHECK(download_result.response.empty());
+    component.error_category_ = ErrorCategory::kDownload;
+    component.error_code_ = download_result.error;
+
+    TransitionState(std::make_unique<StateUpdateError>(&component));
+    return;
+  }
+
+  component.crx_path_ = download_result.response;
+
+  TransitionState(std::make_unique<StateUpdating>(&component));
+}
+
+Component::StateUpdatingDiff::StateUpdatingDiff(Component* component)
+    : State(component, ComponentState::kUpdatingDiff) {}
+
+Component::StateUpdatingDiff::~StateUpdatingDiff() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateUpdatingDiff::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const auto& component = Component::State::component();
+  const auto& update_context = component.update_context_;
+
+  DCHECK(component.crx_component());
+
+  component.NotifyObservers(Events::COMPONENT_UPDATE_READY);
+
+  base::CreateSequencedTaskRunner(kTaskTraits)
+      ->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              &update_client::StartInstallOnBlockingTaskRunner,
+              base::ThreadTaskRunnerHandle::Get(),
+              component.crx_component()->pk_hash, component.crx_path_,
+              component.next_fp_, component.crx_component()->installer,
+              update_context.config->GetUnzipperFactory()->Create(),
+              update_context.config->GetPatcherFactory()->Create(),
+              component.crx_component()->crx_format_requirement,
+              base::BindOnce(&Component::StateUpdatingDiff::InstallComplete,
+                             base::Unretained(this))));
+}
+
+void Component::StateUpdatingDiff::InstallComplete(ErrorCategory error_category,
+                                                   int error_code,
+                                                   int extra_code1) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = Component::State::component();
+
+  component.diff_error_category_ = error_category;
+  component.diff_error_code_ = error_code;
+  component.diff_extra_code1_ = extra_code1;
+
+  if (component.diff_error_code_ != 0) {
+    TransitionState(std::make_unique<StateDownloading>(&component));
+    return;
+  }
+
+  DCHECK_EQ(ErrorCategory::kNone, component.diff_error_category_);
+  DCHECK_EQ(0, component.diff_error_code_);
+  DCHECK_EQ(0, component.diff_extra_code1_);
+
+  DCHECK_EQ(ErrorCategory::kNone, component.error_category_);
+  DCHECK_EQ(0, component.error_code_);
+  DCHECK_EQ(0, component.extra_code1_);
+
+  if (component.action_run_.empty())
+    TransitionState(std::make_unique<StateUpdated>(&component));
+  else
+    TransitionState(std::make_unique<StateRun>(&component));
+}
+
+Component::StateUpdating::StateUpdating(Component* component)
+    : State(component, ComponentState::kUpdating) {}
+
+Component::StateUpdating::~StateUpdating() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateUpdating::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const auto& component = Component::State::component();
+  const auto& update_context = component.update_context_;
+
+  DCHECK(component.crx_component());
+
+  component.NotifyObservers(Events::COMPONENT_UPDATE_READY);
+
+  base::CreateSequencedTaskRunner(kTaskTraits)
+      ->PostTask(FROM_HERE,
+                 base::BindOnce(
+                     &update_client::StartInstallOnBlockingTaskRunner,
+                     base::ThreadTaskRunnerHandle::Get(),
+                     component.crx_component()->pk_hash, component.crx_path_,
+                     component.next_fp_, component.crx_component()->installer,
+                     update_context.config->GetUnzipperFactory()->Create(),
+                     update_context.config->GetPatcherFactory()->Create(),
+                     component.crx_component()->crx_format_requirement,
+                     base::BindOnce(&Component::StateUpdating::InstallComplete,
+                                    base::Unretained(this))));
+}
+
+void Component::StateUpdating::InstallComplete(ErrorCategory error_category,
+                                               int error_code,
+                                               int extra_code1) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = Component::State::component();
+
+  component.error_category_ = error_category;
+  component.error_code_ = error_code;
+  component.extra_code1_ = extra_code1;
+
+  if (component.error_code_ != 0) {
+    TransitionState(std::make_unique<StateUpdateError>(&component));
+    return;
+  }
+
+  DCHECK_EQ(ErrorCategory::kNone, component.error_category_);
+  DCHECK_EQ(0, component.error_code_);
+  DCHECK_EQ(0, component.extra_code1_);
+
+  if (component.action_run_.empty())
+    TransitionState(std::make_unique<StateUpdated>(&component));
+  else
+    TransitionState(std::make_unique<StateRun>(&component));
+}
+
+Component::StateUpdated::StateUpdated(Component* component)
+    : State(component, ComponentState::kUpdated) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+Component::StateUpdated::~StateUpdated() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateUpdated::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+  DCHECK(component.crx_component());
+
+  component.crx_component_->version = component.next_version_;
+  component.crx_component_->fingerprint = component.next_fp_;
+
+  component.AppendEvent(component.MakeEventUpdateComplete());
+
+  component.NotifyObservers(Events::COMPONENT_UPDATED);
+  EndState();
+}
+
+Component::StateUninstalled::StateUninstalled(Component* component)
+    : State(component, ComponentState::kUninstalled) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+Component::StateUninstalled::~StateUninstalled() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateUninstalled::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+  DCHECK(component.crx_component());
+
+  component.AppendEvent(component.MakeEventUninstalled());
+
+  EndState();
+}
+
+Component::StateRun::StateRun(Component* component)
+    : State(component, ComponentState::kRun) {}
+
+Component::StateRun::~StateRun() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Component::StateRun::DoHandle() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const auto& component = State::component();
+  DCHECK(component.crx_component());
+
+  action_runner_ = std::make_unique<ActionRunner>(component);
+  action_runner_->Run(
+      base::BindOnce(&StateRun::ActionRunComplete, base::Unretained(this)));
+}
+
+void Component::StateRun::ActionRunComplete(bool succeeded,
+                                            int error_code,
+                                            int extra_code1) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto& component = State::component();
+
+  component.AppendEvent(
+      component.MakeEventActionRun(succeeded, error_code, extra_code1));
+  switch (component.previous_state_) {
+    case ComponentState::kChecking:
+      TransitionState(std::make_unique<StateUpToDate>(&component));
+      return;
+    case ComponentState::kUpdating:
+    case ComponentState::kUpdatingDiff:
+      TransitionState(std::make_unique<StateUpdated>(&component));
+      return;
+    default:
+      break;
+  }
+  NOTREACHED();
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/component.h b/src/updater_components/update_client/component.h
new file mode 100644
index 0000000..1e8ed14
--- /dev/null
+++ b/src/updater_components/update_client/component.h
@@ -0,0 +1,462 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_H_
+#define COMPONENTS_UPDATE_CLIENT_COMPONENT_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/version.h"
+#include "components/update_client/crx_downloader.h"
+#include "components/update_client/protocol_parser.h"
+#include "components/update_client/update_client.h"
+#include "url/gurl.h"
+
+namespace base {
+class Value;
+}  // namespace base
+
+namespace update_client {
+
+class ActionRunner;
+class Configurator;
+struct CrxUpdateItem;
+struct UpdateContext;
+
+// Describes a CRX component managed by the UpdateEngine. Each |Component| is
+// associated with an UpdateContext.
+class Component {
+ public:
+  using Events = UpdateClient::Observer::Events;
+
+  using CallbackHandleComplete = base::OnceCallback<void()>;
+
+  Component(const UpdateContext& update_context, const std::string& id);
+  ~Component();
+
+  // Handles the current state of the component and makes it transition
+  // to the next component state before |callback_handle_complete_| is invoked.
+  void Handle(CallbackHandleComplete callback_handle_complete);
+
+  CrxUpdateItem GetCrxUpdateItem() const;
+
+  // Sets the uninstall state for this component.
+  void Uninstall(const base::Version& cur_version, int reason);
+
+  // Called by the UpdateEngine when an update check for this component is done.
+  void SetUpdateCheckResult(
+      const base::Optional<ProtocolParser::Result>& result,
+      ErrorCategory error_category,
+      int error);
+
+  // Returns true if the component has reached a final state and no further
+  // handling and state transitions are possible.
+  bool IsHandled() const { return is_handled_; }
+
+  // Returns true if an update is available for this component, meaning that
+  // the update server has return a response containing an update.
+  bool IsUpdateAvailable() const { return is_update_available_; }
+
+  base::TimeDelta GetUpdateDuration() const;
+
+  ComponentState state() const { return state_->state(); }
+
+  std::string id() const { return id_; }
+
+  const base::Optional<CrxComponent>& crx_component() const {
+    return crx_component_;
+  }
+  void set_crx_component(const CrxComponent& crx_component) {
+    crx_component_ = crx_component;
+  }
+
+  const base::Version& previous_version() const { return previous_version_; }
+  void set_previous_version(const base::Version& previous_version) {
+    previous_version_ = previous_version;
+  }
+
+  const base::Version& next_version() const { return next_version_; }
+
+  std::string previous_fp() const { return previous_fp_; }
+  void set_previous_fp(const std::string& previous_fp) {
+    previous_fp_ = previous_fp;
+  }
+
+  std::string next_fp() const { return next_fp_; }
+  void set_next_fp(const std::string& next_fp) { next_fp_ = next_fp; }
+
+  bool is_foreground() const;
+
+  const std::vector<GURL>& crx_diffurls() const { return crx_diffurls_; }
+
+  bool diff_update_failed() const { return !!diff_error_code_; }
+
+  ErrorCategory error_category() const { return error_category_; }
+  int error_code() const { return error_code_; }
+  int extra_code1() const { return extra_code1_; }
+  ErrorCategory diff_error_category() const { return diff_error_category_; }
+  int diff_error_code() const { return diff_error_code_; }
+  int diff_extra_code1() const { return diff_extra_code1_; }
+
+  std::string action_run() const { return action_run_; }
+
+  scoped_refptr<Configurator> config() const;
+
+  std::string session_id() const;
+
+  const std::vector<base::Value>& events() const { return events_; }
+
+  // Returns a clone of the component events.
+  std::vector<base::Value> GetEvents() const;
+
+ private:
+  friend class MockPingManagerImpl;
+  friend class UpdateCheckerTest;
+
+  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, SendPing);
+  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, RequiresEncryption);
+  FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, NoUpdateActionRun);
+  FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckCupError);
+  FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckError);
+  FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckInvalidAp);
+  FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest,
+                           UpdateCheckRequiresEncryptionError);
+  FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckSuccess);
+  FRIEND_TEST_ALL_PREFIXES(UpdateCheckerTest, UpdateCheckUpdateDisabled);
+
+  // Describes an abstraction for implementing the behavior of a component and
+  // the transition from one state to another.
+  class State {
+   public:
+    using CallbackNextState =
+        base::OnceCallback<void(std::unique_ptr<State> next_state)>;
+
+    State(Component* component, ComponentState state);
+    virtual ~State();
+
+    // Handles the current state and initiates a transition to a new state.
+    // The transition to the new state is non-blocking and it is completed
+    // by the outer component, after the current state is fully handled.
+    void Handle(CallbackNextState callback);
+
+    ComponentState state() const { return state_; }
+
+   protected:
+    // Initiates the transition to the new state.
+    void TransitionState(std::unique_ptr<State> new_state);
+
+    // Makes the current state a final state where no other state transition
+    // can further occur.
+    void EndState();
+
+    Component& component() { return component_; }
+    const Component& component() const { return component_; }
+
+    base::ThreadChecker thread_checker_;
+
+    const ComponentState state_;
+
+   private:
+    virtual void DoHandle() = 0;
+
+    Component& component_;
+    CallbackNextState callback_next_state_;
+  };
+
+  class StateNew : public State {
+   public:
+    explicit StateNew(Component* component);
+    ~StateNew() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    DISALLOW_COPY_AND_ASSIGN(StateNew);
+  };
+
+  class StateChecking : public State {
+   public:
+    explicit StateChecking(Component* component);
+    ~StateChecking() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    void UpdateCheckComplete();
+
+    DISALLOW_COPY_AND_ASSIGN(StateChecking);
+  };
+
+  class StateUpdateError : public State {
+   public:
+    explicit StateUpdateError(Component* component);
+    ~StateUpdateError() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    DISALLOW_COPY_AND_ASSIGN(StateUpdateError);
+  };
+
+  class StateCanUpdate : public State {
+   public:
+    explicit StateCanUpdate(Component* component);
+    ~StateCanUpdate() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+    bool CanTryDiffUpdate() const;
+
+    DISALLOW_COPY_AND_ASSIGN(StateCanUpdate);
+  };
+
+  class StateUpToDate : public State {
+   public:
+    explicit StateUpToDate(Component* component);
+    ~StateUpToDate() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    DISALLOW_COPY_AND_ASSIGN(StateUpToDate);
+  };
+
+  class StateDownloadingDiff : public State {
+   public:
+    explicit StateDownloadingDiff(Component* component);
+    ~StateDownloadingDiff() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    // Called when progress is being made downloading a CRX. Can be called
+    // multiple times due to how the CRX downloader switches between
+    // different downloaders and fallback urls.
+    void DownloadProgress(const std::string& id);
+
+    void DownloadComplete(const std::string& id,
+                          const CrxDownloader::Result& download_result);
+
+    // Downloads updates for one CRX id only.
+    std::unique_ptr<CrxDownloader> crx_downloader_;
+
+    DISALLOW_COPY_AND_ASSIGN(StateDownloadingDiff);
+  };
+
+  class StateDownloading : public State {
+   public:
+    explicit StateDownloading(Component* component);
+    ~StateDownloading() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    // Called when progress is being made downloading a CRX. Can be called
+    // multiple times due to how the CRX downloader switches between
+    // different downloaders and fallback urls.
+    void DownloadProgress(const std::string& id);
+
+    void DownloadComplete(const std::string& id,
+                          const CrxDownloader::Result& download_result);
+
+    // Downloads updates for one CRX id only.
+    std::unique_ptr<CrxDownloader> crx_downloader_;
+
+    DISALLOW_COPY_AND_ASSIGN(StateDownloading);
+  };
+
+  class StateUpdatingDiff : public State {
+   public:
+    explicit StateUpdatingDiff(Component* component);
+    ~StateUpdatingDiff() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    void InstallComplete(ErrorCategory error_category,
+                         int error_code,
+                         int extra_code1);
+
+    DISALLOW_COPY_AND_ASSIGN(StateUpdatingDiff);
+  };
+
+  class StateUpdating : public State {
+   public:
+    explicit StateUpdating(Component* component);
+    ~StateUpdating() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    void InstallComplete(ErrorCategory error_category,
+                         int error_code,
+                         int extra_code1);
+
+    DISALLOW_COPY_AND_ASSIGN(StateUpdating);
+  };
+
+  class StateUpdated : public State {
+   public:
+    explicit StateUpdated(Component* component);
+    ~StateUpdated() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    DISALLOW_COPY_AND_ASSIGN(StateUpdated);
+  };
+
+  class StateUninstalled : public State {
+   public:
+    explicit StateUninstalled(Component* component);
+    ~StateUninstalled() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    DISALLOW_COPY_AND_ASSIGN(StateUninstalled);
+  };
+
+  class StateRun : public State {
+   public:
+    explicit StateRun(Component* component);
+    ~StateRun() override;
+
+   private:
+    // State overrides.
+    void DoHandle() override;
+
+    void ActionRunComplete(bool succeeded, int error_code, int extra_code1);
+
+    // Runs the action referred by the |action_run_| member of the Component
+    // class.
+    std::unique_ptr<ActionRunner> action_runner_;
+
+    DISALLOW_COPY_AND_ASSIGN(StateRun);
+  };
+
+  // Returns true is the update payload for this component can be downloaded
+  // by a downloader which can do bandwidth throttling on the client side.
+  bool CanDoBackgroundDownload() const;
+
+  void AppendEvent(base::Value event);
+
+  // Changes the component state and notifies the caller of the |Handle|
+  // function that the handling of this component state is complete.
+  void ChangeState(std::unique_ptr<State> next_state);
+
+  // Notifies registered observers about changes in the state of the component.
+  void NotifyObservers(Events event) const;
+
+  void SetParseResult(const ProtocolParser::Result& result);
+
+  // These functions return a specific event. Each data member of the event is
+  // represented as a key-value pair in a dictionary value.
+  base::Value MakeEventUpdateComplete() const;
+  base::Value MakeEventDownloadMetrics(
+      const CrxDownloader::DownloadMetrics& download_metrics) const;
+  base::Value MakeEventUninstalled() const;
+  base::Value MakeEventActionRun(bool succeeded,
+                                 int error_code,
+                                 int extra_code1) const;
+
+  base::ThreadChecker thread_checker_;
+
+  const std::string id_;
+  base::Optional<CrxComponent> crx_component_;
+
+  // The status of the updatecheck response.
+  std::string status_;
+
+  // Time when an update check for this CRX has happened.
+  base::TimeTicks last_check_;
+
+  // Time when the update of this CRX has begun.
+  base::TimeTicks update_begin_;
+
+  // A component can be made available for download from several urls.
+  std::vector<GURL> crx_urls_;
+  std::vector<GURL> crx_diffurls_;
+
+  // The cryptographic hash values for the component payload.
+  std::string hash_sha256_;
+  std::string hashdiff_sha256_;
+
+  // The from/to version and fingerprint values.
+  base::Version previous_version_;
+  base::Version next_version_;
+  std::string previous_fp_;
+  std::string next_fp_;
+
+  // Contains the file name of the payload to run. This member is set by
+  // the update response parser, when the update response includes a run action.
+  std::string action_run_;
+
+  // True if the update check response for this component includes an update.
+  bool is_update_available_ = false;
+
+  // The error reported by the update checker.
+  int update_check_error_ = 0;
+
+  base::FilePath crx_path_;
+
+  // The error information for full and differential updates.
+  // The |error_category| contains a hint about which module in the component
+  // updater generated the error. The |error_code| constains the error and
+  // the |extra_code1| usually contains a system error, but it can contain
+  // any extended information that is relevant to either the category or the
+  // error itself.
+  ErrorCategory error_category_ = ErrorCategory::kNone;
+  int error_code_ = 0;
+  int extra_code1_ = 0;
+  ErrorCategory diff_error_category_ = ErrorCategory::kNone;
+  int diff_error_code_ = 0;
+  int diff_extra_code1_ = 0;
+
+  // Contains the events which are therefore serialized in the requests.
+  std::vector<base::Value> events_;
+
+  CallbackHandleComplete callback_handle_complete_;
+  std::unique_ptr<State> state_;
+  const UpdateContext& update_context_;
+
+  base::OnceClosure update_check_complete_;
+
+  ComponentState previous_state_ = ComponentState::kLastStatus;
+
+  // True if this component has reached a final state because all its states
+  // have been handled.
+  bool is_handled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(Component);
+};
+
+using IdToComponentPtrMap = std::map<std::string, std::unique_ptr<Component>>;
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_COMPONENT_H_
diff --git a/src/updater_components/update_client/component_patcher.cc b/src/updater_components/update_client/component_patcher.cc
new file mode 100644
index 0000000..ba81f1d
--- /dev/null
+++ b/src/updater_components/update_client/component_patcher.cc
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/component_patcher.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/values.h"
+#include "components/update_client/component_patcher_operation.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_client_errors.h"
+
+namespace update_client {
+
+namespace {
+
+// Deserialize the commands file (present in delta update packages). The top
+// level must be a list.
+base::ListValue* ReadCommands(const base::FilePath& unpack_path) {
+  const base::FilePath commands =
+      unpack_path.Append(FILE_PATH_LITERAL("commands.json"));
+  if (!base::PathExists(commands))
+    return nullptr;
+
+  JSONFileValueDeserializer deserializer(commands);
+  std::unique_ptr<base::Value> root =
+      deserializer.Deserialize(nullptr, nullptr);
+
+  return (root.get() && root->is_list())
+             ? static_cast<base::ListValue*>(root.release())
+             : nullptr;
+}
+
+}  // namespace
+
+ComponentPatcher::ComponentPatcher(const base::FilePath& input_dir,
+                                   const base::FilePath& unpack_dir,
+                                   scoped_refptr<CrxInstaller> installer,
+                                   scoped_refptr<Patcher> patcher)
+    : input_dir_(input_dir),
+      unpack_dir_(unpack_dir),
+      installer_(installer),
+      patcher_(patcher) {}
+
+ComponentPatcher::~ComponentPatcher() {
+}
+
+void ComponentPatcher::Start(Callback callback) {
+  callback_ = std::move(callback);
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ComponentPatcher::StartPatching,
+                                scoped_refptr<ComponentPatcher>(this)));
+}
+
+void ComponentPatcher::StartPatching() {
+  commands_.reset(ReadCommands(input_dir_));
+  if (!commands_) {
+    DonePatching(UnpackerError::kDeltaBadCommands, 0);
+  } else {
+    next_command_ = commands_->begin();
+    PatchNextFile();
+  }
+}
+
+void ComponentPatcher::PatchNextFile() {
+  if (next_command_ == commands_->end()) {
+    DonePatching(UnpackerError::kNone, 0);
+    return;
+  }
+  const base::DictionaryValue* command_args;
+  if (!next_command_->GetAsDictionary(&command_args)) {
+    DonePatching(UnpackerError::kDeltaBadCommands, 0);
+    return;
+  }
+
+  std::string operation;
+  if (command_args->GetString(kOp, &operation)) {
+    current_operation_ = CreateDeltaUpdateOp(operation, patcher_);
+  }
+
+  if (!current_operation_) {
+    DonePatching(UnpackerError::kDeltaUnsupportedCommand, 0);
+    return;
+  }
+  current_operation_->Run(
+      command_args, input_dir_, unpack_dir_, installer_,
+      base::BindOnce(&ComponentPatcher::DonePatchingFile,
+                     scoped_refptr<ComponentPatcher>(this)));
+}
+
+void ComponentPatcher::DonePatchingFile(UnpackerError error,
+                                        int extended_error) {
+  if (error != UnpackerError::kNone) {
+    DonePatching(error, extended_error);
+  } else {
+    ++next_command_;
+    PatchNextFile();
+  }
+}
+
+void ComponentPatcher::DonePatching(UnpackerError error, int extended_error) {
+  current_operation_ = nullptr;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback_), error, extended_error));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/component_patcher.h b/src/updater_components/update_client/component_patcher.h
new file mode 100644
index 0000000..e31a812
--- /dev/null
+++ b/src/updater_components/update_client/component_patcher.h
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Component updates can be either differential updates or full updates.
+// Full updates come in CRX format; differential updates come in CRX-style
+// archives, but have a different magic number. They contain "commands.json", a
+// list of commands for the patcher to follow. The patcher uses these commands,
+// the other files in the archive, and the files from the existing installation
+// of the component to create the contents of a full update, which is then
+// installed normally.
+// Component updates are specified by the 'codebasediff' attribute of an
+// updatecheck response:
+//   <updatecheck codebase="http://example.com/extension_1.2.3.4.crx"
+//                hash="12345" size="9854" status="ok" version="1.2.3.4"
+//                prodversionmin="2.0.143.0"
+//                codebasediff="http://example.com/diff_1.2.3.4.crx"
+//                hashdiff="123" sizediff="101"
+//                fp="1.123"/>
+// The component updater will attempt a differential update if it is available
+// and allowed to, and fall back to a full update if it fails.
+//
+// After installation (diff or full), the component updater records "fp", the
+// fingerprint of the installed files, to later identify the existing files to
+// the server so that a proper differential update can be provided next cycle.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_H_
+#define COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "components/update_client/component_unpacker.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace update_client {
+
+class CrxInstaller;
+class DeltaUpdateOp;
+enum class UnpackerError;
+
+// The type of a patch file.
+enum PatchType {
+  kPatchTypeUnknown,
+  kPatchTypeCourgette,
+  kPatchTypeBsdiff,
+};
+
+// Encapsulates a task for applying a differential update to a component.
+class ComponentPatcher : public base::RefCountedThreadSafe<ComponentPatcher> {
+ public:
+  using Callback = base::OnceCallback<void(UnpackerError, int)>;
+
+  // Takes an unpacked differential CRX (|input_dir|) and a component installer,
+  // and sets up the class to create a new (non-differential) unpacked CRX.
+  // If |in_process| is true, patching will be done completely within the
+  // existing process. Otherwise, some steps of patching may be done
+  // out-of-process.
+  ComponentPatcher(const base::FilePath& input_dir,
+                   const base::FilePath& unpack_dir,
+                   scoped_refptr<CrxInstaller> installer,
+                   scoped_refptr<Patcher> patcher);
+
+  // Starts patching files. This member function returns immediately, after
+  // posting a task to do the patching. When patching has been completed,
+  // |callback| will be called with the error codes if any error codes were
+  // encountered.
+  void Start(Callback callback);
+
+ private:
+  friend class base::RefCountedThreadSafe<ComponentPatcher>;
+
+  virtual ~ComponentPatcher();
+
+  void StartPatching();
+
+  void PatchNextFile();
+
+  void DonePatchingFile(UnpackerError error, int extended_error);
+
+  void DonePatching(UnpackerError error, int extended_error);
+
+  const base::FilePath input_dir_;
+  const base::FilePath unpack_dir_;
+  scoped_refptr<CrxInstaller> installer_;
+  scoped_refptr<Patcher> patcher_;
+  Callback callback_;
+  std::unique_ptr<base::ListValue> commands_;
+  base::ListValue::const_iterator next_command_;
+  scoped_refptr<DeltaUpdateOp> current_operation_;
+
+  DISALLOW_COPY_AND_ASSIGN(ComponentPatcher);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_H_
diff --git a/src/updater_components/update_client/component_patcher_operation.cc b/src/updater_components/update_client/component_patcher_operation.cc
new file mode 100644
index 0000000..c54a30e
--- /dev/null
+++ b/src/updater_components/update_client/component_patcher_operation.cc
@@ -0,0 +1,226 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/component_patcher_operation.h"
+
+#include <stdint.h>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/location.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+#include "courgette/courgette.h"
+#include "courgette/third_party/bsdiff/bsdiff.h"
+
+namespace update_client {
+
+namespace {
+
+const char kOutput[] = "output";
+const char kSha256[] = "sha256";
+
+// The integer offset disambiguates between overlapping error ranges.
+const int kCourgetteErrorOffset = 300;
+const int kBsdiffErrorOffset = 600;
+
+}  // namespace
+
+const char kOp[] = "op";
+const char kBsdiff[] = "bsdiff";
+const char kCourgette[] = "courgette";
+const char kInput[] = "input";
+const char kPatch[] = "patch";
+
+DeltaUpdateOp* CreateDeltaUpdateOp(const std::string& operation,
+                                   scoped_refptr<Patcher> patcher) {
+  if (operation == "copy") {
+    return new DeltaUpdateOpCopy();
+  } else if (operation == "create") {
+    return new DeltaUpdateOpCreate();
+  } else if (operation == "bsdiff" || operation == "courgette") {
+    return new DeltaUpdateOpPatch(operation, patcher);
+  }
+  return nullptr;
+}
+
+DeltaUpdateOp::DeltaUpdateOp() {
+}
+
+DeltaUpdateOp::~DeltaUpdateOp() {
+}
+
+void DeltaUpdateOp::Run(const base::DictionaryValue* command_args,
+                        const base::FilePath& input_dir,
+                        const base::FilePath& unpack_dir,
+                        scoped_refptr<CrxInstaller> installer,
+                        ComponentPatcher::Callback callback) {
+  callback_ = std::move(callback);
+  std::string output_rel_path;
+  if (!command_args->GetString(kOutput, &output_rel_path) ||
+      !command_args->GetString(kSha256, &output_sha256_)) {
+    DoneRunning(UnpackerError::kDeltaBadCommands, 0);
+    return;
+  }
+
+  output_abs_path_ =
+      unpack_dir.Append(base::FilePath::FromUTF8Unsafe(output_rel_path));
+  UnpackerError parse_result =
+      DoParseArguments(command_args, input_dir, installer);
+  if (parse_result != UnpackerError::kNone) {
+    DoneRunning(parse_result, 0);
+    return;
+  }
+
+  const base::FilePath parent = output_abs_path_.DirName();
+  if (!base::DirectoryExists(parent)) {
+    if (!base::CreateDirectory(parent)) {
+      DoneRunning(UnpackerError::kIoError, 0);
+      return;
+    }
+  }
+
+  DoRun(base::BindOnce(&DeltaUpdateOp::DoneRunning,
+                       scoped_refptr<DeltaUpdateOp>(this)));
+}
+
+void DeltaUpdateOp::DoneRunning(UnpackerError error, int extended_error) {
+  if (error == UnpackerError::kNone)
+    error = CheckHash();
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback_), error, extended_error));
+}
+
+// Uses the hash as a checksum to confirm that the file now residing in the
+// output directory probably has the contents it should.
+UnpackerError DeltaUpdateOp::CheckHash() {
+  return VerifyFileHash256(output_abs_path_, output_sha256_)
+             ? UnpackerError::kNone
+             : UnpackerError::kDeltaVerificationFailure;
+}
+
+DeltaUpdateOpCopy::DeltaUpdateOpCopy() {
+}
+
+DeltaUpdateOpCopy::~DeltaUpdateOpCopy() {
+}
+
+UnpackerError DeltaUpdateOpCopy::DoParseArguments(
+    const base::DictionaryValue* command_args,
+    const base::FilePath& input_dir,
+    scoped_refptr<CrxInstaller> installer) {
+  std::string input_rel_path;
+  if (!command_args->GetString(kInput, &input_rel_path))
+    return UnpackerError::kDeltaBadCommands;
+
+  if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
+    return UnpackerError::kDeltaMissingExistingFile;
+
+  return UnpackerError::kNone;
+}
+
+void DeltaUpdateOpCopy::DoRun(ComponentPatcher::Callback callback) {
+  if (!base::CopyFile(input_abs_path_, output_abs_path_))
+    std::move(callback).Run(UnpackerError::kDeltaOperationFailure, 0);
+  else
+    std::move(callback).Run(UnpackerError::kNone, 0);
+}
+
+DeltaUpdateOpCreate::DeltaUpdateOpCreate() {
+}
+
+DeltaUpdateOpCreate::~DeltaUpdateOpCreate() {
+}
+
+UnpackerError DeltaUpdateOpCreate::DoParseArguments(
+    const base::DictionaryValue* command_args,
+    const base::FilePath& input_dir,
+    scoped_refptr<CrxInstaller> installer) {
+  std::string patch_rel_path;
+  if (!command_args->GetString(kPatch, &patch_rel_path))
+    return UnpackerError::kDeltaBadCommands;
+
+  patch_abs_path_ =
+      input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path));
+
+  return UnpackerError::kNone;
+}
+
+void DeltaUpdateOpCreate::DoRun(ComponentPatcher::Callback callback) {
+  if (!base::Move(patch_abs_path_, output_abs_path_))
+    std::move(callback).Run(UnpackerError::kDeltaOperationFailure, 0);
+  else
+    std::move(callback).Run(UnpackerError::kNone, 0);
+}
+
+DeltaUpdateOpPatch::DeltaUpdateOpPatch(const std::string& operation,
+                                       scoped_refptr<Patcher> patcher)
+    : operation_(operation), patcher_(patcher) {
+  DCHECK(operation == kBsdiff || operation == kCourgette);
+}
+
+DeltaUpdateOpPatch::~DeltaUpdateOpPatch() {
+}
+
+UnpackerError DeltaUpdateOpPatch::DoParseArguments(
+    const base::DictionaryValue* command_args,
+    const base::FilePath& input_dir,
+    scoped_refptr<CrxInstaller> installer) {
+  std::string patch_rel_path;
+  std::string input_rel_path;
+  if (!command_args->GetString(kPatch, &patch_rel_path) ||
+      !command_args->GetString(kInput, &input_rel_path))
+    return UnpackerError::kDeltaBadCommands;
+
+  if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
+    return UnpackerError::kDeltaMissingExistingFile;
+
+  patch_abs_path_ =
+      input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path));
+
+  return UnpackerError::kNone;
+}
+
+void DeltaUpdateOpPatch::DoRun(ComponentPatcher::Callback callback) {
+  if (operation_ == kBsdiff) {
+    patcher_->PatchBsdiff(input_abs_path_, patch_abs_path_, output_abs_path_,
+                          base::BindOnce(&DeltaUpdateOpPatch::DonePatching,
+                                         this, std::move(callback)));
+  } else {
+    patcher_->PatchCourgette(input_abs_path_, patch_abs_path_, output_abs_path_,
+                             base::BindOnce(&DeltaUpdateOpPatch::DonePatching,
+                                            this, std::move(callback)));
+  }
+}
+
+void DeltaUpdateOpPatch::DonePatching(ComponentPatcher::Callback callback,
+                                      int result) {
+  if (operation_ == kBsdiff) {
+    if (result == bsdiff::OK) {
+      std::move(callback).Run(UnpackerError::kNone, 0);
+    } else {
+      std::move(callback).Run(UnpackerError::kDeltaOperationFailure,
+                              result + kBsdiffErrorOffset);
+    }
+  } else if (operation_ == kCourgette) {
+    if (result == courgette::C_OK) {
+      std::move(callback).Run(UnpackerError::kNone, 0);
+    } else {
+      std::move(callback).Run(UnpackerError::kDeltaOperationFailure,
+                              result + kCourgetteErrorOffset);
+    }
+  } else {
+    NOTREACHED();
+  }
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/component_patcher_operation.h b/src/updater_components/update_client/component_patcher_operation.h
new file mode 100644
index 0000000..0649ff9
--- /dev/null
+++ b/src/updater_components/update_client/component_patcher_operation.h
@@ -0,0 +1,163 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_OPERATION_H_
+#define COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_OPERATION_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/component_patcher.h"
+#include "components/update_client/component_unpacker.h"
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+namespace update_client {
+
+extern const char kOp[];
+extern const char kBsdiff[];
+extern const char kCourgette[];
+extern const char kInput[];
+extern const char kPatch[];
+
+class CrxInstaller;
+class Patcher;
+enum class UnpackerError;
+
+class DeltaUpdateOp : public base::RefCountedThreadSafe<DeltaUpdateOp> {
+ public:
+  DeltaUpdateOp();
+
+  // Parses, runs, and verifies the operation. Calls |callback| with the
+  // result of the operation. The callback is called using |task_runner|.
+  void Run(const base::DictionaryValue* command_args,
+           const base::FilePath& input_dir,
+           const base::FilePath& unpack_dir,
+           scoped_refptr<CrxInstaller> installer,
+           ComponentPatcher::Callback callback);
+
+ protected:
+  virtual ~DeltaUpdateOp();
+
+  std::string output_sha256_;
+  base::FilePath output_abs_path_;
+
+ private:
+  friend class base::RefCountedThreadSafe<DeltaUpdateOp>;
+
+  UnpackerError CheckHash();
+
+  // Subclasses must override DoParseArguments to parse operation-specific
+  // arguments. DoParseArguments returns DELTA_OK on success; any other code
+  // represents failure.
+  virtual UnpackerError DoParseArguments(
+      const base::DictionaryValue* command_args,
+      const base::FilePath& input_dir,
+      scoped_refptr<CrxInstaller> installer) = 0;
+
+  // Subclasses must override DoRun to actually perform the patching operation.
+  // They must call the provided callback when they have completed their
+  // operations. In practice, the provided callback is always for "DoneRunning".
+  virtual void DoRun(ComponentPatcher::Callback callback) = 0;
+
+  // Callback given to subclasses for when they complete their operation.
+  // Validates the output, and posts a task to the patching operation's
+  // callback.
+  void DoneRunning(UnpackerError error, int extended_error);
+
+  ComponentPatcher::Callback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOp);
+};
+
+// A 'copy' operation takes a file currently residing on the disk and moves it
+// into the unpacking directory: this represents "no change" in the file being
+// installed.
+class DeltaUpdateOpCopy : public DeltaUpdateOp {
+ public:
+  DeltaUpdateOpCopy();
+
+ private:
+  ~DeltaUpdateOpCopy() override;
+
+  // Overrides of DeltaUpdateOp.
+  UnpackerError DoParseArguments(
+      const base::DictionaryValue* command_args,
+      const base::FilePath& input_dir,
+      scoped_refptr<CrxInstaller> installer) override;
+
+  void DoRun(ComponentPatcher::Callback callback) override;
+
+  base::FilePath input_abs_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpCopy);
+};
+
+// A 'create' operation takes a full file that was sent in the delta update
+// archive and moves it into the unpacking directory: this represents the
+// addition of a new file, or a file so different that no bandwidth could be
+// saved by transmitting a differential update.
+class DeltaUpdateOpCreate : public DeltaUpdateOp {
+ public:
+  DeltaUpdateOpCreate();
+
+ private:
+  ~DeltaUpdateOpCreate() override;
+
+  // Overrides of DeltaUpdateOp.
+  UnpackerError DoParseArguments(
+      const base::DictionaryValue* command_args,
+      const base::FilePath& input_dir,
+      scoped_refptr<CrxInstaller> installer) override;
+
+  void DoRun(ComponentPatcher::Callback callback) override;
+
+  base::FilePath patch_abs_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpCreate);
+};
+
+// Both 'bsdiff' and 'courgette' operations take an existing file on disk,
+// and a bsdiff- or Courgette-format patch file provided in the delta update
+// package, and run bsdiff or Courgette to construct an output file in the
+// unpacking directory.
+class DeltaUpdateOpPatch : public DeltaUpdateOp {
+ public:
+  DeltaUpdateOpPatch(const std::string& operation,
+                     scoped_refptr<Patcher> patcher);
+
+ private:
+  ~DeltaUpdateOpPatch() override;
+
+  // Overrides of DeltaUpdateOp.
+  UnpackerError DoParseArguments(
+      const base::DictionaryValue* command_args,
+      const base::FilePath& input_dir,
+      scoped_refptr<CrxInstaller> installer) override;
+
+  void DoRun(ComponentPatcher::Callback callback) override;
+
+  // |success_code| is the code that indicates a successful patch.
+  // |result| is the code the patching operation returned.
+  void DonePatching(ComponentPatcher::Callback callback, int result);
+
+  std::string operation_;
+  scoped_refptr<Patcher> patcher_;
+  base::FilePath patch_abs_path_;
+  base::FilePath input_abs_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpPatch);
+};
+
+DeltaUpdateOp* CreateDeltaUpdateOp(const std::string& operation,
+                                   scoped_refptr<Patcher> patcher);
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_OPERATION_H_
diff --git a/src/updater_components/update_client/component_patcher_unittest.cc b/src/updater_components/update_client/component_patcher_unittest.cc
new file mode 100644
index 0000000..78b1cc8
--- /dev/null
+++ b/src/updater_components/update_client/component_patcher_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/component_patcher.h"
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "components/services/patch/in_process_file_patcher.h"
+#include "components/update_client/component_patcher_operation.h"
+#include "components/update_client/component_patcher_unittest.h"
+#include "components/update_client/patch/patch_impl.h"
+#include "components/update_client/test_installer.h"
+#include "components/update_client/update_client_errors.h"
+#include "courgette/courgette.h"
+#include "courgette/third_party/bsdiff/bsdiff.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestCallback {
+ public:
+  TestCallback();
+  virtual ~TestCallback() {}
+  void Set(update_client::UnpackerError error, int extra_code);
+
+  update_client::UnpackerError error_;
+  int extra_code_;
+  bool called_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestCallback);
+};
+
+TestCallback::TestCallback()
+    : error_(update_client::UnpackerError::kNone),
+      extra_code_(-1),
+      called_(false) {}
+
+void TestCallback::Set(update_client::UnpackerError error, int extra_code) {
+  error_ = error;
+  extra_code_ = extra_code;
+  called_ = true;
+}
+
+base::FilePath test_file(const char* file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("update_client")
+      .AppendASCII(file);
+}
+
+}  // namespace
+
+namespace update_client {
+
+ComponentPatcherOperationTest::ComponentPatcherOperationTest()
+    : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {
+  EXPECT_TRUE(unpack_dir_.CreateUniqueTempDir());
+  EXPECT_TRUE(input_dir_.CreateUniqueTempDir());
+  EXPECT_TRUE(installed_dir_.CreateUniqueTempDir());
+  installer_ =
+      base::MakeRefCounted<ReadOnlyTestInstaller>(installed_dir_.GetPath());
+}
+
+ComponentPatcherOperationTest::~ComponentPatcherOperationTest() {
+}
+
+// Verify that a 'create' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckCreateOperation) {
+  EXPECT_TRUE(base::CopyFile(
+      test_file("binary_output.bin"),
+      input_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_output.bin"))));
+
+  std::unique_ptr<base::DictionaryValue> command_args =
+      std::make_unique<base::DictionaryValue>();
+  command_args->SetString("output", "output.bin");
+  command_args->SetString("sha256", binary_output_hash);
+  command_args->SetString("op", "create");
+  command_args->SetString("patch", "binary_output.bin");
+
+  TestCallback callback;
+  scoped_refptr<DeltaUpdateOp> op = base::MakeRefCounted<DeltaUpdateOpCreate>();
+  op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(),
+          nullptr,
+          base::BindOnce(&TestCallback::Set, base::Unretained(&callback)));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_EQ(true, callback.called_);
+  EXPECT_EQ(UnpackerError::kNone, callback.error_);
+  EXPECT_EQ(0, callback.extra_code_);
+  EXPECT_TRUE(base::ContentsEqual(
+      unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")),
+      test_file("binary_output.bin")));
+}
+
+// Verify that a 'copy' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckCopyOperation) {
+  EXPECT_TRUE(base::CopyFile(
+      test_file("binary_output.bin"),
+      installed_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_output.bin"))));
+
+  std::unique_ptr<base::DictionaryValue> command_args =
+      std::make_unique<base::DictionaryValue>();
+  command_args->SetString("output", "output.bin");
+  command_args->SetString("sha256", binary_output_hash);
+  command_args->SetString("op", "copy");
+  command_args->SetString("input", "binary_output.bin");
+
+  TestCallback callback;
+  scoped_refptr<DeltaUpdateOp> op = base::MakeRefCounted<DeltaUpdateOpCopy>();
+  op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(),
+          installer_.get(),
+          base::BindOnce(&TestCallback::Set, base::Unretained(&callback)));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_EQ(true, callback.called_);
+  EXPECT_EQ(UnpackerError::kNone, callback.error_);
+  EXPECT_EQ(0, callback.extra_code_);
+  EXPECT_TRUE(base::ContentsEqual(
+      unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")),
+      test_file("binary_output.bin")));
+}
+
+// Verify that a 'courgette' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckCourgetteOperation) {
+  EXPECT_TRUE(base::CopyFile(
+      test_file("binary_input.bin"),
+      installed_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_input.bin"))));
+  EXPECT_TRUE(base::CopyFile(test_file("binary_courgette_patch.bin"),
+                             input_dir_.GetPath().Append(FILE_PATH_LITERAL(
+                                 "binary_courgette_patch.bin"))));
+
+  std::unique_ptr<base::DictionaryValue> command_args =
+      std::make_unique<base::DictionaryValue>();
+  command_args->SetString("output", "output.bin");
+  command_args->SetString("sha256", binary_output_hash);
+  command_args->SetString("op", "courgette");
+  command_args->SetString("input", "binary_input.bin");
+  command_args->SetString("patch", "binary_courgette_patch.bin");
+
+  scoped_refptr<Patcher> patcher =
+      base::MakeRefCounted<PatchChromiumFactory>(
+          base::BindRepeating(&patch::LaunchInProcessFilePatcher))
+          ->Create();
+
+  TestCallback callback;
+  scoped_refptr<DeltaUpdateOp> op = CreateDeltaUpdateOp("courgette", patcher);
+  op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(),
+          installer_.get(),
+          base::BindOnce(&TestCallback::Set, base::Unretained(&callback)));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_EQ(true, callback.called_);
+  EXPECT_EQ(UnpackerError::kNone, callback.error_);
+  EXPECT_EQ(0, callback.extra_code_);
+  EXPECT_TRUE(base::ContentsEqual(
+      unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")),
+      test_file("binary_output.bin")));
+}
+
+// Verify that a 'bsdiff' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckBsdiffOperation) {
+  EXPECT_TRUE(base::CopyFile(
+      test_file("binary_input.bin"),
+      installed_dir_.GetPath().Append(FILE_PATH_LITERAL("binary_input.bin"))));
+  EXPECT_TRUE(base::CopyFile(test_file("binary_bsdiff_patch.bin"),
+                             input_dir_.GetPath().Append(FILE_PATH_LITERAL(
+                                 "binary_bsdiff_patch.bin"))));
+
+  std::unique_ptr<base::DictionaryValue> command_args =
+      std::make_unique<base::DictionaryValue>();
+  command_args->SetString("output", "output.bin");
+  command_args->SetString("sha256", binary_output_hash);
+  command_args->SetString("op", "courgette");
+  command_args->SetString("input", "binary_input.bin");
+  command_args->SetString("patch", "binary_bsdiff_patch.bin");
+
+  // The operation needs a Patcher to access the PatchService.
+  scoped_refptr<Patcher> patcher =
+      base::MakeRefCounted<PatchChromiumFactory>(
+          base::BindRepeating(&patch::LaunchInProcessFilePatcher))
+          ->Create();
+
+  TestCallback callback;
+  scoped_refptr<DeltaUpdateOp> op = CreateDeltaUpdateOp("bsdiff", patcher);
+  op->Run(command_args.get(), input_dir_.GetPath(), unpack_dir_.GetPath(),
+          installer_.get(),
+          base::BindOnce(&TestCallback::Set, base::Unretained(&callback)));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_EQ(true, callback.called_);
+  EXPECT_EQ(UnpackerError::kNone, callback.error_);
+  EXPECT_EQ(0, callback.extra_code_);
+  EXPECT_TRUE(base::ContentsEqual(
+      unpack_dir_.GetPath().Append(FILE_PATH_LITERAL("output.bin")),
+      test_file("binary_output.bin")));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/component_patcher_unittest.h b/src/updater_components/update_client/component_patcher_unittest.h
new file mode 100644
index 0000000..cb50e87
--- /dev/null
+++ b/src/updater_components/update_client/component_patcher_unittest.h
@@ -0,0 +1,41 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_UNITTEST_H_
+#define COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_UNITTEST_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/test/task_environment.h"
+#include "courgette/courgette.h"
+#include "courgette/third_party/bsdiff/bsdiff.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+class ReadOnlyTestInstaller;
+
+const char binary_output_hash[] =
+    "599aba6d15a7da390621ef1bacb66601ed6aed04dadc1f9b445dcfe31296142a";
+
+class ComponentPatcherOperationTest : public testing::Test {
+ public:
+  ComponentPatcherOperationTest();
+  ~ComponentPatcherOperationTest() override;
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  base::ScopedTempDir input_dir_;
+  base::ScopedTempDir installed_dir_;
+  base::ScopedTempDir unpack_dir_;
+  scoped_refptr<ReadOnlyTestInstaller> installer_;
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_COMPONENT_PATCHER_UNITTEST_H_
diff --git a/src/updater_components/update_client/component_unpacker.cc b/src/updater_components/update_client/component_unpacker.cc
new file mode 100644
index 0000000..a9f34b8
--- /dev/null
+++ b/src/updater_components/update_client/component_unpacker.cc
@@ -0,0 +1,155 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/component_unpacker.h"
+
+#include <stdint.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/update_client/component_patcher.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/unzipper.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_client_errors.h"
+
+namespace update_client {
+
+ComponentUnpacker::Result::Result() {}
+
+ComponentUnpacker::ComponentUnpacker(const std::vector<uint8_t>& pk_hash,
+                                     const base::FilePath& path,
+                                     scoped_refptr<CrxInstaller> installer,
+                                     std::unique_ptr<Unzipper> unzipper,
+                                     scoped_refptr<Patcher> patcher,
+                                     crx_file::VerifierFormat crx_format)
+    : pk_hash_(pk_hash),
+      path_(path),
+      is_delta_(false),
+      installer_(installer),
+      unzipper_(std::move(unzipper)),
+      patcher_tool_(patcher),
+      crx_format_(crx_format),
+      error_(UnpackerError::kNone),
+      extended_error_(0) {}
+
+ComponentUnpacker::~ComponentUnpacker() {}
+
+void ComponentUnpacker::Unpack(Callback callback) {
+  callback_ = std::move(callback);
+  if (!Verify() || !BeginUnzipping())
+    EndUnpacking();
+}
+
+bool ComponentUnpacker::Verify() {
+  VLOG(1) << "Verifying component: " << path_.value();
+  if (pk_hash_.empty() || path_.empty()) {
+    error_ = UnpackerError::kInvalidParams;
+    return false;
+  }
+  const std::vector<std::vector<uint8_t>> required_keys = {pk_hash_};
+  const crx_file::VerifierResult result =
+      crx_file::Verify(path_, crx_format_, required_keys,
+                       std::vector<uint8_t>(), &public_key_, nullptr);
+  if (result != crx_file::VerifierResult::OK_FULL &&
+      result != crx_file::VerifierResult::OK_DELTA) {
+    error_ = UnpackerError::kInvalidFile;
+    extended_error_ = static_cast<int>(result);
+    return false;
+  }
+  is_delta_ = result == crx_file::VerifierResult::OK_DELTA;
+  VLOG(1) << "Verification successful: " << path_.value();
+  return true;
+}
+
+bool ComponentUnpacker::BeginUnzipping() {
+  // Mind the reference to non-const type, passed as an argument below.
+  base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_;
+  if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
+                                    &destination)) {
+    VLOG(1) << "Unable to create temporary directory for unpacking.";
+    error_ = UnpackerError::kUnzipPathError;
+    return false;
+  }
+  VLOG(1) << "Unpacking in: " << destination.value();
+  unzipper_->Unzip(path_, destination,
+                   base::BindOnce(&ComponentUnpacker::EndUnzipping, this));
+  return true;
+}
+
+void ComponentUnpacker::EndUnzipping(bool result) {
+  if (!result) {
+    VLOG(1) << "Unzipping failed.";
+    error_ = UnpackerError::kUnzipFailed;
+    EndUnpacking();
+    return;
+  }
+  VLOG(1) << "Unpacked successfully";
+  BeginPatching();
+}
+
+bool ComponentUnpacker::BeginPatching() {
+  if (is_delta_) {  // Package is a diff package.
+    // Use a different temp directory for the patch output files.
+    if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
+                                      &unpack_path_)) {
+      error_ = UnpackerError::kUnzipPathError;
+      return false;
+    }
+    patcher_ = base::MakeRefCounted<ComponentPatcher>(
+        unpack_diff_path_, unpack_path_, installer_, patcher_tool_);
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ComponentPatcher::Start, patcher_,
+                       base::BindOnce(&ComponentUnpacker::EndPatching,
+                                      scoped_refptr<ComponentUnpacker>(this))));
+  } else {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&ComponentUnpacker::EndPatching,
+                                  scoped_refptr<ComponentUnpacker>(this),
+                                  UnpackerError::kNone, 0));
+  }
+  return true;
+}
+
+void ComponentUnpacker::EndPatching(UnpackerError error, int extended_error) {
+  error_ = error;
+  extended_error_ = extended_error;
+  patcher_ = nullptr;
+
+  EndUnpacking();
+}
+
+void ComponentUnpacker::EndUnpacking() {
+  if (!unpack_diff_path_.empty())
+    base::DeleteFile(unpack_diff_path_, true);
+  if (error_ != UnpackerError::kNone && !unpack_path_.empty())
+    base::DeleteFile(unpack_path_, true);
+
+  Result result;
+  result.error = error_;
+  result.extended_error = extended_error_;
+  if (error_ == UnpackerError::kNone) {
+    result.unpack_path = unpack_path_;
+    result.public_key = public_key_;
+  }
+
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback_), result));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/component_unpacker.h b/src/updater_components/update_client/component_unpacker.h
new file mode 100644
index 0000000..1d79891
--- /dev/null
+++ b/src/updater_components/update_client/component_unpacker.h
@@ -0,0 +1,148 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_COMPONENT_UNPACKER_H_
+#define COMPONENTS_UPDATE_CLIENT_COMPONENT_UNPACKER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/update_client_errors.h"
+
+namespace crx_file {
+enum class VerifierFormat;
+}
+
+namespace update_client {
+
+class CrxInstaller;
+class ComponentPatcher;
+class Patcher;
+class Unzipper;
+
+// In charge of unpacking the component CRX package and verifying that it is
+// well formed and the cryptographic signature is correct.
+//
+// This class should be used only by the component updater. It is inspired by
+// and overlaps with code in the extension's SandboxedUnpacker.
+// The main differences are:
+// - The public key hash is full SHA256.
+// - Does not use a sandboxed unpacker. A valid component is fully trusted.
+// - The manifest can have different attributes and resources are not
+//   transcoded.
+//
+// If the CRX is a delta CRX, the flow is:
+//   [ComponentUpdater]      [ComponentPatcher]
+//   Unpack
+//     \_ Verify
+//     \_ Unzip
+//     \_ BeginPatching ---> DifferentialUpdatePatch
+//                             ...
+//   EndPatching <------------ ...
+//     \_ EndUnpacking
+//
+// For a full CRX, the flow is:
+//   [ComponentUpdater]
+//   Unpack
+//     \_ Verify
+//     \_ Unzip
+//     \_ BeginPatching
+//          |
+//          V
+//   EndPatching
+//     \_ EndUnpacking
+//
+// In both cases, if there is an error at any point, the remaining steps will
+// be skipped and EndUnpacking will be called.
+class ComponentUnpacker : public base::RefCountedThreadSafe<ComponentUnpacker> {
+ public:
+  // Contains the result of the unpacking.
+  struct Result {
+    Result();
+
+    // Unpack error: 0 indicates success.
+    UnpackerError error = UnpackerError::kNone;
+
+    // Additional error information, such as errno or last error.
+    int extended_error = 0;
+
+    // Path of the unpacked files if the unpacking was successful.
+    base::FilePath unpack_path;
+
+    // The extracted public key of the package if the unpacking was successful.
+    std::string public_key;
+  };
+
+  using Callback = base::OnceCallback<void(const Result& result)>;
+
+  // Constructs an unpacker for a specific component unpacking operation.
+  // |pk_hash| is the expected/ public key SHA256 hash. |path| is the current
+  // location of the CRX.
+  ComponentUnpacker(const std::vector<uint8_t>& pk_hash,
+                    const base::FilePath& path,
+                    scoped_refptr<CrxInstaller> installer,
+                    std::unique_ptr<Unzipper> unzipper,
+                    scoped_refptr<Patcher> patcher,
+                    crx_file::VerifierFormat crx_format);
+
+  // Begins the actual unpacking of the files. May invoke a patcher and the
+  // component installer if the package is a differential update.
+  // Calls |callback| with the result.
+  void Unpack(Callback callback);
+
+ private:
+  friend class base::RefCountedThreadSafe<ComponentUnpacker>;
+
+  virtual ~ComponentUnpacker();
+
+  // The first step of unpacking is to verify the file. Returns false if an
+  // error is encountered, the file is malformed, or the file is incorrectly
+  // signed.
+  bool Verify();
+
+  // The second step of unpacking is to unzip. Returns false if an early error
+  // is encountered.
+  bool BeginUnzipping();
+  void EndUnzipping(bool error);
+
+  // The third step is to optionally patch files - this is a no-op for full
+  // (non-differential) updates. This step is asynchronous. Returns false if an
+  // error is encountered.
+  bool BeginPatching();
+  void EndPatching(UnpackerError error, int extended_error);
+
+  // The final step is to do clean-up for things that can't be tidied as we go.
+  // If there is an error at any step, the remaining steps are skipped and
+  // EndUnpacking is called. EndUnpacking is responsible for calling the
+  // callback provided in Unpack().
+  void EndUnpacking();
+
+  std::vector<uint8_t> pk_hash_;
+  base::FilePath path_;
+  base::FilePath unpack_path_;
+  base::FilePath unpack_diff_path_;
+  bool is_delta_;
+  scoped_refptr<ComponentPatcher> patcher_;
+  scoped_refptr<CrxInstaller> installer_;
+  Callback callback_;
+  std::unique_ptr<Unzipper> unzipper_;
+  scoped_refptr<Patcher> patcher_tool_;
+  crx_file::VerifierFormat crx_format_;
+  UnpackerError error_;
+  int extended_error_;
+  std::string public_key_;
+
+  DISALLOW_COPY_AND_ASSIGN(ComponentUnpacker);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_COMPONENT_UNPACKER_H_
diff --git a/src/updater_components/update_client/component_unpacker_unittest.cc b/src/updater_components/update_client/component_unpacker_unittest.cc
new file mode 100644
index 0000000..0bc1905
--- /dev/null
+++ b/src/updater_components/update_client/component_unpacker_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/update_client/component_unpacker.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/test_configurator.h"
+#include "components/update_client/test_installer.h"
+#include "components/update_client/unzipper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestCallback {
+ public:
+  TestCallback();
+  virtual ~TestCallback() {}
+  void Set(update_client::UnpackerError error, int extra_code);
+
+  update_client::UnpackerError error_;
+  int extra_code_;
+  bool called_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestCallback);
+};
+
+TestCallback::TestCallback()
+    : error_(update_client::UnpackerError::kNone),
+      extra_code_(-1),
+      called_(false) {}
+
+void TestCallback::Set(update_client::UnpackerError error, int extra_code) {
+  error_ = error;
+  extra_code_ = extra_code;
+  called_ = true;
+}
+
+base::FilePath test_file(const char* file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("update_client")
+      .AppendASCII(file);
+}
+
+}  // namespace
+
+namespace update_client {
+
+class ComponentUnpackerTest : public testing::Test {
+ public:
+  ComponentUnpackerTest();
+  ~ComponentUnpackerTest() override;
+
+  void UnpackComplete(const ComponentUnpacker::Result& result);
+
+ protected:
+  void RunThreads();
+
+  base::test::TaskEnvironment task_environment_;
+  const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ =
+      base::ThreadTaskRunnerHandle::Get();
+  base::RunLoop runloop_;
+  base::OnceClosure quit_closure_ = runloop_.QuitClosure();
+
+  ComponentUnpacker::Result result_;
+};
+
+ComponentUnpackerTest::ComponentUnpackerTest() = default;
+
+ComponentUnpackerTest::~ComponentUnpackerTest() = default;
+
+void ComponentUnpackerTest::RunThreads() {
+  runloop_.Run();
+}
+
+void ComponentUnpackerTest::UnpackComplete(
+    const ComponentUnpacker::Result& result) {
+  result_ = result;
+  main_thread_task_runner_->PostTask(FROM_HERE, std::move(quit_closure_));
+}
+
+TEST_F(ComponentUnpackerTest, UnpackFullCrx) {
+  auto config = base::MakeRefCounted<TestConfigurator>();
+  scoped_refptr<ComponentUnpacker> component_unpacker =
+      base::MakeRefCounted<ComponentUnpacker>(
+          std::vector<uint8_t>(std::begin(jebg_hash), std::end(jebg_hash)),
+          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"), nullptr,
+          config->GetUnzipperFactory()->Create(),
+          config->GetPatcherFactory()->Create(),
+          crx_file::VerifierFormat::CRX2_OR_CRX3);
+  component_unpacker->Unpack(base::BindOnce(
+      &ComponentUnpackerTest::UnpackComplete, base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(UnpackerError::kNone, result_.error);
+  EXPECT_EQ(0, result_.extended_error);
+
+  base::FilePath unpack_path = result_.unpack_path;
+  EXPECT_FALSE(unpack_path.empty());
+  EXPECT_TRUE(base::DirectoryExists(unpack_path));
+  EXPECT_EQ(jebg_public_key, result_.public_key);
+
+  int64_t file_size = 0;
+  EXPECT_TRUE(
+      base::GetFileSize(unpack_path.AppendASCII("component1.dll"), &file_size));
+  EXPECT_EQ(1024, file_size);
+  EXPECT_TRUE(
+      base::GetFileSize(unpack_path.AppendASCII("flashtest.pem"), &file_size));
+  EXPECT_EQ(911, file_size);
+  EXPECT_TRUE(
+      base::GetFileSize(unpack_path.AppendASCII("manifest.json"), &file_size));
+  EXPECT_EQ(144, file_size);
+
+  EXPECT_TRUE(base::DeleteFile(unpack_path, true));
+}
+
+TEST_F(ComponentUnpackerTest, UnpackFileNotFound) {
+  scoped_refptr<ComponentUnpacker> component_unpacker =
+      base::MakeRefCounted<ComponentUnpacker>(
+          std::vector<uint8_t>(std::begin(jebg_hash), std::end(jebg_hash)),
+          test_file("file-not-found.crx"), nullptr, nullptr, nullptr,
+          crx_file::VerifierFormat::CRX2_OR_CRX3);
+  component_unpacker->Unpack(base::BindOnce(
+      &ComponentUnpackerTest::UnpackComplete, base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(UnpackerError::kInvalidFile, result_.error);
+  EXPECT_EQ(static_cast<int>(crx_file::VerifierResult::ERROR_FILE_NOT_READABLE),
+            result_.extended_error);
+
+  EXPECT_TRUE(result_.unpack_path.empty());
+}
+
+// Tests a mismatch between the public key hash and the id of the component.
+TEST_F(ComponentUnpackerTest, UnpackFileHashMismatch) {
+  scoped_refptr<ComponentUnpacker> component_unpacker =
+      base::MakeRefCounted<ComponentUnpacker>(
+          std::vector<uint8_t>(std::begin(abag_hash), std::end(abag_hash)),
+          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"), nullptr, nullptr,
+          nullptr, crx_file::VerifierFormat::CRX2_OR_CRX3);
+  component_unpacker->Unpack(base::BindOnce(
+      &ComponentUnpackerTest::UnpackComplete, base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(UnpackerError::kInvalidFile, result_.error);
+  EXPECT_EQ(
+      static_cast<int>(crx_file::VerifierResult::ERROR_REQUIRED_PROOF_MISSING),
+      result_.extended_error);
+
+  EXPECT_TRUE(result_.unpack_path.empty());
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/configurator.h b/src/updater_components/update_client/configurator.h
new file mode 100644
index 0000000..f2ea5a2
--- /dev/null
+++ b/src/updater_components/update_client/configurator.h
@@ -0,0 +1,177 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_CONFIGURATOR_H_
+#define COMPONENTS_UPDATE_CLIENT_CONFIGURATOR_H_
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/ref_counted.h"
+
+class GURL;
+class PrefService;
+
+namespace base {
+class FilePath;
+class Version;
+}
+
+namespace update_client {
+
+class ActivityDataService;
+class NetworkFetcherFactory;
+class PatcherFactory;
+class ProtocolHandlerFactory;
+class UnzipperFactory;
+
+using RecoveryCRXElevator = base::OnceCallback<std::tuple<bool, int, int>(
+    const base::FilePath& crx_path,
+    const std::string& browser_appid,
+    const std::string& browser_version,
+    const std::string& session_id)>;
+
+// Controls the component updater behavior.
+// TODO(sorin): this class will be split soon in two. One class controls
+// the behavior of the update client, and the other class controls the
+// behavior of the component updater.
+class Configurator : public base::RefCountedThreadSafe<Configurator> {
+ public:
+  // Delay in seconds from calling Start() to the first update check.
+  virtual int InitialDelay() const = 0;
+
+  // Delay in seconds to every subsequent update check. 0 means don't check.
+  virtual int NextCheckDelay() const = 0;
+
+  // Minimum delta time in seconds before an on-demand check is allowed
+  // for the same component.
+  virtual int OnDemandDelay() const = 0;
+
+  // The time delay in seconds between applying updates for different
+  // components.
+  virtual int UpdateDelay() const = 0;
+
+  // The URLs for the update checks. The URLs are tried in order, the first one
+  // that succeeds wins.
+  virtual std::vector<GURL> UpdateUrl() const = 0;
+
+  // The URLs for pings. Returns an empty vector if and only if pings are
+  // disabled. Similarly, these URLs have a fall back behavior too.
+  virtual std::vector<GURL> PingUrl() const = 0;
+
+  // The ProdId is used as a prefix in some of the version strings which appear
+  // in the protocol requests. Possible values include "chrome", "chromecrx",
+  // "chromiumcrx", and "unknown".
+  virtual std::string GetProdId() const = 0;
+
+  // Version of the application. Used to compare the component manifests.
+  virtual base::Version GetBrowserVersion() const = 0;
+
+  // Returns the value we use for the "updaterchannel=" and "prodchannel="
+  // parameters. Possible return values include: "canary", "dev", "beta", and
+  // "stable".
+  virtual std::string GetChannel() const = 0;
+
+  // Returns the brand code or distribution tag that has been assigned to
+  // a partner. A brand code is a 4-character string used to identify
+  // installations that took place as a result of partner deals or website
+  // promotions.
+  virtual std::string GetBrand() const = 0;
+
+  // Returns the language for the present locale. Possible return values are
+  // standard tags for languages, such as "en", "en-US", "de", "fr", "af", etc.
+  virtual std::string GetLang() const = 0;
+
+  // Returns the OS's long name like "Windows", "Mac OS X", etc.
+  virtual std::string GetOSLongName() const = 0;
+
+  // Parameters added to each url request. It can be empty if none are needed.
+  // Returns a map of name-value pairs that match ^[-_a-zA-Z0-9]$ regex.
+  virtual base::flat_map<std::string, std::string> ExtraRequestParams()
+      const = 0;
+
+  // Provides a hint for the server to control the order in which multiple
+  // download urls are returned. The hint may or may not be honored in the
+  // response returned by the server.
+  // Returns an empty string if no policy is in effect.
+  virtual std::string GetDownloadPreference() const = 0;
+
+  virtual scoped_refptr<NetworkFetcherFactory> GetNetworkFetcherFactory() = 0;
+
+  virtual scoped_refptr<UnzipperFactory> GetUnzipperFactory() = 0;
+
+  virtual scoped_refptr<PatcherFactory> GetPatcherFactory() = 0;
+
+  // True means that this client can handle delta updates.
+  virtual bool EnabledDeltas() const = 0;
+
+  // True if component updates are enabled. Updates for all components are
+  // enabled by default. This method allows enabling or disabling
+  // updates for certain components such as the plugins. Updates for some
+  // components are always enabled and can't be disabled programatically.
+  virtual bool EnabledComponentUpdates() const = 0;
+
+  // True means that the background downloader can be used for downloading
+  // non on-demand components.
+  virtual bool EnabledBackgroundDownloader() const = 0;
+
+  // True if signing of update checks is enabled.
+  virtual bool EnabledCupSigning() const = 0;
+
+  // Returns a PrefService that the update_client can use to store persistent
+  // update information. The PrefService must outlive the entire update_client,
+  // and be safe to access from the thread the update_client is constructed
+  // on.
+  // Returning null is safe and will disable any functionality that requires
+  // persistent storage.
+  virtual PrefService* GetPrefService() const = 0;
+
+  // Returns an ActivityDataService that the update_client can use to access
+  // to update information (namely active bit, last active/rollcall days)
+  // normally stored in the user extension profile.
+  // Similar to PrefService, ActivityDataService must outlive the entire
+  // update_client, and be safe to access from the thread the update_client
+  // is constructed on.
+  // Returning null is safe and will disable any functionality that requires
+  // accessing to the information provided by ActivityDataService.
+  virtual ActivityDataService* GetActivityDataService() const = 0;
+
+  // Returns true if the Chrome is installed for the current user only, or false
+  // if Chrome is installed for all users on the machine. This function must be
+  // called only from a blocking pool thread, as it may access the file system.
+  virtual bool IsPerUserInstall() const = 0;
+
+  // Returns the key hash corresponding to a CRX trusted by ActionRun. The
+  // CRX payloads are signed with this key, and their integrity is verified
+  // during the unpacking by the action runner. This is a dependency injection
+  // feature to support testing.
+  virtual std::vector<uint8_t> GetRunActionKeyHash() const = 0;
+
+  // Returns the app GUID with which Chrome is registered with Google Update, or
+  // an empty string if this brand does not integrate with Google Update.
+  virtual std::string GetAppGuid() const = 0;
+
+  // Returns the class factory to create protocol parser and protocol
+  // serializer object instances.
+  virtual std::unique_ptr<ProtocolHandlerFactory> GetProtocolHandlerFactory()
+      const = 0;
+
+  // Returns a callback which can elevate and run the CRX payload associated
+  // with the improved recovery component. Running this payload repairs the
+  // Chrome update functionality.
+  virtual RecoveryCRXElevator GetRecoveryCRXElevator() const = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<Configurator>;
+
+  virtual ~Configurator() {}
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_CONFIGURATOR_H_
diff --git a/src/updater_components/update_client/crx_downloader.cc b/src/updater_components/update_client/crx_downloader.cc
new file mode 100644
index 0000000..cf45d4c
--- /dev/null
+++ b/src/updater_components/update_client/crx_downloader.cc
@@ -0,0 +1,214 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/crx_downloader.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#if defined(OS_WIN)
+#include "components/update_client/background_downloader_win.h"
+#endif
+#include "components/update_client/network.h"
+#include "components/update_client/task_traits.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/url_fetcher_downloader.h"
+#include "components/update_client/utils.h"
+
+namespace update_client {
+
+CrxDownloader::DownloadMetrics::DownloadMetrics()
+    : downloader(kNone),
+      error(0),
+      downloaded_bytes(-1),
+      total_bytes(-1),
+      download_time_ms(0) {
+}
+
+// On Windows, the first downloader in the chain is a background downloader,
+// which uses the BITS service.
+std::unique_ptr<CrxDownloader> CrxDownloader::Create(
+    bool is_background_download,
+    scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+  std::unique_ptr<CrxDownloader> url_fetcher_downloader =
+      std::make_unique<UrlFetcherDownloader>(nullptr, network_fetcher_factory);
+
+#if defined(OS_WIN)
+  if (is_background_download) {
+    return std::make_unique<BackgroundDownloader>(
+        std::move(url_fetcher_downloader));
+  }
+#endif
+
+  return url_fetcher_downloader;
+}
+
+CrxDownloader::CrxDownloader(std::unique_ptr<CrxDownloader> successor)
+    : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      successor_(std::move(successor)) {}
+
+CrxDownloader::~CrxDownloader() {}
+
+void CrxDownloader::set_progress_callback(
+    const ProgressCallback& progress_callback) {
+  progress_callback_ = progress_callback;
+}
+
+GURL CrxDownloader::url() const {
+  return current_url_ != urls_.end() ? *current_url_ : GURL();
+}
+
+const std::vector<CrxDownloader::DownloadMetrics>
+CrxDownloader::download_metrics() const {
+  if (!successor_)
+    return download_metrics_;
+
+  std::vector<DownloadMetrics> retval(successor_->download_metrics());
+  retval.insert(retval.begin(), download_metrics_.begin(),
+                download_metrics_.end());
+  return retval;
+}
+
+void CrxDownloader::StartDownloadFromUrl(const GURL& url,
+                                         const std::string& expected_hash,
+                                         DownloadCallback download_callback) {
+  std::vector<GURL> urls;
+  urls.push_back(url);
+  StartDownload(urls, expected_hash, std::move(download_callback));
+}
+
+void CrxDownloader::StartDownload(const std::vector<GURL>& urls,
+                                  const std::string& expected_hash,
+                                  DownloadCallback download_callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto error = CrxDownloaderError::NONE;
+  if (urls.empty()) {
+    error = CrxDownloaderError::NO_URL;
+  } else if (expected_hash.empty()) {
+    error = CrxDownloaderError::NO_HASH;
+  }
+
+  if (error != CrxDownloaderError::NONE) {
+    Result result;
+    result.error = static_cast<int>(error);
+    main_task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(download_callback), result));
+    return;
+  }
+
+  urls_ = urls;
+  expected_hash_ = expected_hash;
+  current_url_ = urls_.begin();
+  download_callback_ = std::move(download_callback);
+
+  DoStartDownload(*current_url_);
+}
+
+void CrxDownloader::OnDownloadComplete(
+    bool is_handled,
+    const Result& result,
+    const DownloadMetrics& download_metrics) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!result.error)
+    base::PostTask(
+        FROM_HERE, kTaskTraits,
+        base::BindOnce(&CrxDownloader::VerifyResponse, base::Unretained(this),
+                       is_handled, result, download_metrics));
+  else
+    main_task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&CrxDownloader::HandleDownloadError,
+                                  base::Unretained(this), is_handled, result,
+                                  download_metrics));
+}
+
+void CrxDownloader::OnDownloadProgress() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (progress_callback_.is_null())
+    return;
+
+  progress_callback_.Run();
+}
+
+// The function mutates the values of the parameters |result| and
+// |download_metrics|.
+void CrxDownloader::VerifyResponse(bool is_handled,
+                                   Result result,
+                                   DownloadMetrics download_metrics) {
+  DCHECK_EQ(0, result.error);
+  DCHECK_EQ(0, download_metrics.error);
+  DCHECK(is_handled);
+
+  if (VerifyFileHash256(result.response, expected_hash_)) {
+    download_metrics_.push_back(download_metrics);
+    main_task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(download_callback_), result));
+    return;
+  }
+
+  // The download was successful but the response is not trusted. Clean up
+  // the download, mutate the result, and try the remaining fallbacks when
+  // handling the error.
+  result.error = static_cast<int>(CrxDownloaderError::BAD_HASH);
+  download_metrics.error = result.error;
+  DeleteFileAndEmptyParentDirectory(result.response);
+  result.response.clear();
+
+  main_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&CrxDownloader::HandleDownloadError,
+                                base::Unretained(this), is_handled, result,
+                                download_metrics));
+}
+
+void CrxDownloader::HandleDownloadError(
+    bool is_handled,
+    const Result& result,
+    const DownloadMetrics& download_metrics) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_NE(0, result.error);
+  DCHECK(result.response.empty());
+  DCHECK_NE(0, download_metrics.error);
+
+  download_metrics_.push_back(download_metrics);
+
+  // If an error has occured, try the next url if there is any,
+  // or try the successor in the chain if there is any successor.
+  // If this downloader has received a 5xx error for the current url,
+  // as indicated by the |is_handled| flag, remove that url from the list of
+  // urls so the url is never tried again down the chain.
+  if (is_handled) {
+    current_url_ = urls_.erase(current_url_);
+  } else {
+    ++current_url_;
+  }
+
+  // Try downloading from another url from the list.
+  if (current_url_ != urls_.end()) {
+    DoStartDownload(*current_url_);
+    return;
+  }
+
+  // Try downloading using the next downloader.
+  if (successor_ && !urls_.empty()) {
+    successor_->StartDownload(urls_, expected_hash_,
+                              std::move(download_callback_));
+    return;
+  }
+
+  // The download ends here since there is no url nor downloader to handle this
+  // download request further.
+  main_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(download_callback_), result));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/crx_downloader.h b/src/updater_components/update_client/crx_downloader.h
new file mode 100644
index 0000000..4824e3f
--- /dev/null
+++ b/src/updater_components/update_client/crx_downloader.h
@@ -0,0 +1,164 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_CRX_DOWNLOADER_H_
+#define COMPONENTS_UPDATE_CLIENT_CRX_DOWNLOADER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+class NetworkFetcherFactory;
+
+// Defines a download interface for downloading components, with retrying on
+// fallback urls in case of errors. This class implements a chain of
+// responsibility design pattern. It can give successors in the chain a chance
+// to handle a download request, until one of them succeeds, or there are no
+// more urls or successors to try. A callback is always called at the end of
+// the download, one time only.
+// When multiple urls and downloaders exists, first all the urls are tried, in
+// the order they are provided in the StartDownload function argument. After
+// that, the download request is routed to the next downloader in the chain.
+// The members of this class expect to be called from the main thread only.
+class CrxDownloader {
+ public:
+  struct DownloadMetrics {
+    enum Downloader { kNone = 0, kUrlFetcher, kBits };
+
+    DownloadMetrics();
+
+    GURL url;
+
+    Downloader downloader;
+
+    int error;
+
+    int64_t downloaded_bytes;  // -1 means that the byte count is unknown.
+    int64_t total_bytes;
+
+    uint64_t download_time_ms;
+  };
+
+  // Contains the progress or the outcome of the download.
+  struct Result {
+    // Download error: 0 indicates success.
+    int error = 0;
+
+    // Path of the downloaded file if the download was successful.
+    base::FilePath response;
+  };
+
+  // The callback fires only once, regardless of how many urls are tried, and
+  // how many successors in the chain of downloaders have handled the
+  // download. The callback interface can be extended if needed to provide
+  // more visibility into how the download has been handled, including
+  // specific error codes and download metrics.
+  using DownloadCallback = base::OnceCallback<void(const Result& result)>;
+
+  // The callback may fire 0 or once during a download. Since this
+  // class implements a chain of responsibility, the callback can fire for
+  // different urls and different downloaders.
+  using ProgressCallback = base::RepeatingCallback<void()>;
+
+  using Factory =
+      std::unique_ptr<CrxDownloader> (*)(bool,
+                                         scoped_refptr<NetworkFetcherFactory>);
+
+  // Factory method to create an instance of this class and build the
+  // chain of responsibility. |is_background_download| specifies that a
+  // background downloader be used, if the platform supports it.
+  // |task_runner| should be a task runner able to run blocking
+  // code such as file IO operations.
+  static std::unique_ptr<CrxDownloader> Create(
+      bool is_background_download,
+      scoped_refptr<NetworkFetcherFactory> network_fetcher_factory);
+  virtual ~CrxDownloader();
+
+  void set_progress_callback(const ProgressCallback& progress_callback);
+
+  // Starts the download. One instance of the class handles one download only.
+  // One instance of CrxDownloader can only be started once, otherwise the
+  // behavior is undefined. The callback gets invoked if the download can't
+  // be started. |expected_hash| represents the SHA256 cryptographic hash of
+  // the download payload, represented as a hexadecimal string.
+  void StartDownloadFromUrl(const GURL& url,
+                            const std::string& expected_hash,
+                            DownloadCallback download_callback);
+  void StartDownload(const std::vector<GURL>& urls,
+                     const std::string& expected_hash,
+                     DownloadCallback download_callback);
+
+  const std::vector<DownloadMetrics> download_metrics() const;
+
+ protected:
+  explicit CrxDownloader(std::unique_ptr<CrxDownloader> successor);
+
+  // Handles the fallback in the case of multiple urls and routing of the
+  // download to the following successor in the chain. Derived classes must call
+  // this function after each attempt at downloading the urls provided
+  // in the StartDownload function.
+  // In case of errors, |is_handled| indicates that a server side error has
+  // occured for the current url and the url should not be retried down
+  // the chain to avoid DDOS of the server. This url will be removed from the
+  // list of url and never tried again.
+  void OnDownloadComplete(bool is_handled,
+                          const Result& result,
+                          const DownloadMetrics& download_metrics);
+
+  // Calls the callback when progress is made.
+  void OnDownloadProgress();
+
+  // Returns the url which is currently being downloaded from.
+  GURL url() const;
+
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner() const {
+    return main_task_runner_;
+  }
+
+ private:
+  virtual void DoStartDownload(const GURL& url) = 0;
+
+  void VerifyResponse(bool is_handled,
+                      Result result,
+                      DownloadMetrics download_metrics);
+
+  void HandleDownloadError(bool is_handled,
+                           const Result& result,
+                           const DownloadMetrics& download_metrics);
+
+  base::ThreadChecker thread_checker_;
+
+  // Used to post callbacks to the main thread.
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+  std::vector<GURL> urls_;
+
+  // The SHA256 hash of the download payload in hexadecimal format.
+  std::string expected_hash_;
+  std::unique_ptr<CrxDownloader> successor_;
+  DownloadCallback download_callback_;
+  ProgressCallback progress_callback_;
+
+  std::vector<GURL>::iterator current_url_;
+
+  std::vector<DownloadMetrics> download_metrics_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrxDownloader);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_CRX_DOWNLOADER_H_
diff --git a/src/updater_components/update_client/crx_downloader_unittest.cc b/src/updater_components/update_client/crx_downloader_unittest.cc
new file mode 100644
index 0000000..db3611e
--- /dev/null
+++ b/src/updater_components/update_client/crx_downloader_unittest.cc
@@ -0,0 +1,417 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/crx_downloader.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/update_client/net/network_chromium.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+#include "net/base/net_errors.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::ContentsEqual;
+
+namespace update_client {
+
+namespace {
+
+const char kTestFileName[] = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+
+const char hash_jebg[] =
+    "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+base::FilePath MakeTestFilePath(const char* file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components/test/data/update_client")
+      .AppendASCII(file);
+}
+
+}  // namespace
+
+class CrxDownloaderTest : public testing::Test {
+ public:
+  CrxDownloaderTest();
+  ~CrxDownloaderTest() override;
+
+  // Overrides from testing::Test.
+  void SetUp() override;
+  void TearDown() override;
+
+  void Quit();
+  void RunThreads();
+  void RunThreadsUntilIdle();
+
+  void DownloadComplete(int crx_context, const CrxDownloader::Result& result);
+
+  void DownloadProgress(int crx_context);
+
+  int GetInterceptorCount() { return interceptor_count_; }
+
+  void AddResponse(const GURL& url,
+                   const base::FilePath& file_path,
+                   int net_error);
+
+ protected:
+  std::unique_ptr<CrxDownloader> crx_downloader_;
+
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
+  CrxDownloader::DownloadCallback callback_;
+  CrxDownloader::ProgressCallback progress_callback_;
+
+  int crx_context_;
+
+  int num_download_complete_calls_;
+  CrxDownloader::Result download_complete_result_;
+
+  // These members are updated by DownloadProgress.
+  int num_progress_calls_;
+
+  // Accumulates the number of loads triggered.
+  int interceptor_count_ = 0;
+
+  // A magic value for the context to be used in the tests.
+  static const int kExpectedContext = 0xaabb;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<network::SharedURLLoaderFactory>
+      test_shared_url_loader_factory_;
+  base::OnceClosure quit_closure_;
+};
+
+const int CrxDownloaderTest::kExpectedContext;
+
+CrxDownloaderTest::CrxDownloaderTest()
+    : callback_(base::BindOnce(&CrxDownloaderTest::DownloadComplete,
+                               base::Unretained(this),
+                               kExpectedContext)),
+      progress_callback_(base::Bind(&CrxDownloaderTest::DownloadProgress,
+                                    base::Unretained(this),
+                                    kExpectedContext)),
+      crx_context_(0),
+      num_download_complete_calls_(0),
+      num_progress_calls_(0),
+      task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
+      test_shared_url_loader_factory_(
+          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+              &test_url_loader_factory_)) {}
+
+CrxDownloaderTest::~CrxDownloaderTest() {}
+
+void CrxDownloaderTest::SetUp() {
+  num_download_complete_calls_ = 0;
+  download_complete_result_ = CrxDownloader::Result();
+  num_progress_calls_ = 0;
+
+  // Do not use the background downloader in these tests.
+  crx_downloader_ = CrxDownloader::Create(
+      false, base::MakeRefCounted<NetworkFetcherChromiumFactory>(
+                 test_shared_url_loader_factory_));
+  crx_downloader_->set_progress_callback(progress_callback_);
+
+  test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
+      [&](const network::ResourceRequest& request) { interceptor_count_++; }));
+}
+
+void CrxDownloaderTest::TearDown() {
+  crx_downloader_.reset();
+}
+
+void CrxDownloaderTest::Quit() {
+  if (!quit_closure_.is_null())
+    std::move(quit_closure_).Run();
+}
+
+void CrxDownloaderTest::DownloadComplete(int crx_context,
+                                         const CrxDownloader::Result& result) {
+  ++num_download_complete_calls_;
+  crx_context_ = crx_context;
+  download_complete_result_ = result;
+  Quit();
+}
+
+void CrxDownloaderTest::DownloadProgress(int crx_context) {
+  ++num_progress_calls_;
+}
+
+void CrxDownloaderTest::AddResponse(const GURL& url,
+                                    const base::FilePath& file_path,
+                                    int net_error) {
+  if (net_error == net::OK) {
+    std::string data;
+    EXPECT_TRUE(base::ReadFileToString(file_path, &data));
+    network::ResourceResponseHead head;
+    head.content_length = data.size();
+    network::URLLoaderCompletionStatus status(net_error);
+    status.decoded_body_length = data.size();
+    test_url_loader_factory_.AddResponse(url, head, data, status);
+    return;
+  }
+
+  EXPECT_NE(net_error, net::OK);
+  test_url_loader_factory_.AddResponse(
+      url, network::ResourceResponseHead(), std::string(),
+      network::URLLoaderCompletionStatus(net_error));
+}
+
+void CrxDownloaderTest::RunThreads() {
+  base::RunLoop runloop;
+  quit_closure_ = runloop.QuitClosure();
+  runloop.Run();
+
+  // Since some tests need to drain currently enqueued tasks such as network
+  // intercepts on the IO thread, run the threads until they are
+  // idle. The component updater service won't loop again until the loop count
+  // is set and the service is started.
+  RunThreadsUntilIdle();
+}
+
+void CrxDownloaderTest::RunThreadsUntilIdle() {
+  task_environment_.RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
+}
+
+// Tests that starting a download without a url results in an error.
+TEST_F(CrxDownloaderTest, NoUrl) {
+  std::vector<GURL> urls;
+  crx_downloader_->StartDownload(urls, std::string("abcd"),
+                                 std::move(callback_));
+  RunThreadsUntilIdle();
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_EQ(static_cast<int>(CrxDownloaderError::NO_URL),
+            download_complete_result_.error);
+  EXPECT_TRUE(download_complete_result_.response.empty());
+  EXPECT_EQ(0, num_progress_calls_);
+}
+
+// Tests that starting a download without providing a hash results in an error.
+TEST_F(CrxDownloaderTest, NoHash) {
+  std::vector<GURL> urls(1, GURL("http://somehost/somefile"));
+
+  crx_downloader_->StartDownload(urls, std::string(), std::move(callback_));
+  RunThreadsUntilIdle();
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_EQ(static_cast<int>(CrxDownloaderError::NO_HASH),
+            download_complete_result_.error);
+  EXPECT_TRUE(download_complete_result_.response.empty());
+  EXPECT_EQ(0, num_progress_calls_);
+}
+
+// Tests that downloading from one url is successful.
+TEST_F(CrxDownloaderTest, OneUrl) {
+  const GURL expected_crx_url =
+      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
+
+  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+  AddResponse(expected_crx_url, test_file, net::OK);
+
+  crx_downloader_->StartDownloadFromUrl(
+      expected_crx_url, std::string(hash_jebg), std::move(callback_));
+  RunThreads();
+
+  EXPECT_EQ(1, GetInterceptorCount());
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_EQ(0, download_complete_result_.error);
+  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
+
+  EXPECT_TRUE(
+      DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
+
+  EXPECT_LE(1, num_progress_calls_);
+}
+
+// Tests that downloading from one url fails if the actual hash of the file
+// does not match the expected hash.
+TEST_F(CrxDownloaderTest, OneUrlBadHash) {
+  const GURL expected_crx_url =
+      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
+
+  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+  AddResponse(expected_crx_url, test_file, net::OK);
+
+  crx_downloader_->StartDownloadFromUrl(
+      expected_crx_url,
+      std::string(
+          "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"),
+      std::move(callback_));
+  RunThreads();
+
+  EXPECT_EQ(1, GetInterceptorCount());
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_EQ(static_cast<int>(CrxDownloaderError::BAD_HASH),
+            download_complete_result_.error);
+  EXPECT_TRUE(download_complete_result_.response.empty());
+
+  EXPECT_LE(1, num_progress_calls_);
+}
+
+// Tests that specifying two urls has no side effects. Expect a successful
+// download, and only one download request be made.
+TEST_F(CrxDownloaderTest, TwoUrls) {
+  const GURL expected_crx_url =
+      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
+
+  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+  AddResponse(expected_crx_url, test_file, net::OK);
+
+  std::vector<GURL> urls;
+  urls.push_back(expected_crx_url);
+  urls.push_back(expected_crx_url);
+
+  crx_downloader_->StartDownload(urls, std::string(hash_jebg),
+                                 std::move(callback_));
+  RunThreads();
+
+  EXPECT_EQ(1, GetInterceptorCount());
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_EQ(0, download_complete_result_.error);
+  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
+
+  EXPECT_TRUE(
+      DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
+
+  EXPECT_LE(1, num_progress_calls_);
+}
+
+// Tests that the fallback to a valid url is successful.
+TEST_F(CrxDownloaderTest, TwoUrls_FirstInvalid) {
+  const GURL expected_crx_url =
+      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
+  const GURL no_file_url =
+      GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx");
+
+  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+  AddResponse(expected_crx_url, test_file, net::OK);
+  AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
+
+  std::vector<GURL> urls;
+  urls.push_back(no_file_url);
+  urls.push_back(expected_crx_url);
+
+  crx_downloader_->StartDownload(urls, std::string(hash_jebg),
+                                 std::move(callback_));
+  RunThreads();
+
+  EXPECT_EQ(2, GetInterceptorCount());
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_EQ(0, download_complete_result_.error);
+  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
+
+  EXPECT_TRUE(
+      DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
+
+  // Expect at least some progress reported by the loader.
+  EXPECT_LE(1, num_progress_calls_);
+
+  const auto download_metrics = crx_downloader_->download_metrics();
+  ASSERT_EQ(2u, download_metrics.size());
+  EXPECT_EQ(no_file_url, download_metrics[0].url);
+  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[0].error);
+  EXPECT_EQ(-1, download_metrics[0].downloaded_bytes);
+  EXPECT_EQ(-1, download_metrics[0].total_bytes);
+  EXPECT_EQ(expected_crx_url, download_metrics[1].url);
+  EXPECT_EQ(0, download_metrics[1].error);
+  EXPECT_EQ(1843, download_metrics[1].downloaded_bytes);
+  EXPECT_EQ(1843, download_metrics[1].total_bytes);
+}
+
+// Tests that the download succeeds if the first url is correct and the
+// second bad url does not have a side-effect.
+TEST_F(CrxDownloaderTest, TwoUrls_SecondInvalid) {
+  const GURL expected_crx_url =
+      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
+  const GURL no_file_url =
+      GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx");
+
+  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
+  AddResponse(expected_crx_url, test_file, net::OK);
+  AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
+
+  std::vector<GURL> urls;
+  urls.push_back(expected_crx_url);
+  urls.push_back(no_file_url);
+
+  crx_downloader_->StartDownload(urls, std::string(hash_jebg),
+                                 std::move(callback_));
+  RunThreads();
+
+  EXPECT_EQ(1, GetInterceptorCount());
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_EQ(0, download_complete_result_.error);
+  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
+
+  EXPECT_TRUE(
+      DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
+
+  EXPECT_LE(1, num_progress_calls_);
+
+  EXPECT_EQ(1u, crx_downloader_->download_metrics().size());
+}
+
+// Tests that the download fails if both urls don't serve content.
+TEST_F(CrxDownloaderTest, TwoUrls_BothInvalid) {
+  const GURL expected_crx_url =
+      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
+
+  AddResponse(expected_crx_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
+
+  std::vector<GURL> urls;
+  urls.push_back(expected_crx_url);
+  urls.push_back(expected_crx_url);
+
+  crx_downloader_->StartDownload(urls, std::string(hash_jebg),
+                                 std::move(callback_));
+  RunThreads();
+
+  EXPECT_EQ(2, GetInterceptorCount());
+
+  EXPECT_EQ(1, num_download_complete_calls_);
+  EXPECT_EQ(kExpectedContext, crx_context_);
+  EXPECT_NE(0, download_complete_result_.error);
+  EXPECT_TRUE(download_complete_result_.response.empty());
+
+  const auto download_metrics = crx_downloader_->download_metrics();
+  ASSERT_EQ(2u, download_metrics.size());
+  EXPECT_EQ(expected_crx_url, download_metrics[0].url);
+  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[0].error);
+  EXPECT_EQ(-1, download_metrics[0].downloaded_bytes);
+  EXPECT_EQ(-1, download_metrics[0].total_bytes);
+  EXPECT_EQ(expected_crx_url, download_metrics[1].url);
+  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[1].error);
+  EXPECT_EQ(-1, download_metrics[1].downloaded_bytes);
+  EXPECT_EQ(-1, download_metrics[1].total_bytes);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/crx_update_item.h b/src/updater_components/update_client/crx_update_item.h
new file mode 100644
index 0000000..ac645b5
--- /dev/null
+++ b/src/updater_components/update_client/crx_update_item.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_CRX_UPDATE_ITEM_H_
+#define COMPONENTS_UPDATE_CLIENT_CRX_UPDATE_ITEM_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "base/version.h"
+#include "components/update_client/crx_downloader.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_client_errors.h"
+
+namespace update_client {
+
+struct CrxUpdateItem {
+  CrxUpdateItem();
+  CrxUpdateItem(const CrxUpdateItem& other);
+  ~CrxUpdateItem();
+
+  ComponentState state;
+
+  std::string id;
+
+  // The value of this data member is provided to the |UpdateClient| by the
+  // caller by responding to the |CrxDataCallback|. If the caller can't
+  // provide this value, for instance, in cases where the CRX was uninstalled,
+  // then the |component| member will not be present.
+  base::Optional<CrxComponent> component;
+
+  // Time when an update check for this CRX has happened.
+  base::TimeTicks last_check;
+
+  base::Version next_version;
+  std::string next_fp;
+
+  ErrorCategory error_category = ErrorCategory::kNone;
+  int error_code = 0;
+  int extra_code1 = 0;
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_CRX_UPDATE_ITEM_H_
diff --git a/src/updater_components/update_client/net/network_chromium.h b/src/updater_components/update_client/net/network_chromium.h
new file mode 100644
index 0000000..e8ac9bb
--- /dev/null
+++ b/src/updater_components/update_client/net/network_chromium.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 COMPONENTS_UPDATE_CLIENT_NET_NETWORK_CHROMIUM_H_
+#define COMPONENTS_UPDATE_CLIENT_NET_NETWORK_CHROMIUM_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/network.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}
+
+namespace update_client {
+
+class NetworkFetcherChromiumFactory : public NetworkFetcherFactory {
+ public:
+  explicit NetworkFetcherChromiumFactory(
+      scoped_refptr<network::SharedURLLoaderFactory>
+          shared_url_network_factory);
+
+  std::unique_ptr<NetworkFetcher> Create() const override;
+
+ protected:
+  ~NetworkFetcherChromiumFactory() override;
+
+ private:
+  scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcherChromiumFactory);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_NET_NETWORK_CHROMIUM_H_
diff --git a/src/updater_components/update_client/net/network_impl.cc b/src/updater_components/update_client/net/network_impl.cc
new file mode 100644
index 0000000..1e241a6
--- /dev/null
+++ b/src/updater_components/update_client/net/network_impl.cc
@@ -0,0 +1,205 @@
+// 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 "components/update_client/net/network_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/numerics/safe_conversions.h"
+#include "components/update_client/net/network_chromium.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/gurl.h"
+
+namespace {
+
+const net::NetworkTrafficAnnotationTag traffic_annotation =
+    net::DefineNetworkTrafficAnnotation("update_client", R"(
+        semantics {
+          sender: "Component Updater and Extension Updater"
+          description:
+            "This network module is used by both the component and the "
+            "extension updaters in Chrome. "
+            "The component updater is responsible for updating code and data "
+            "modules such as Flash, CrlSet, Origin Trials, etc. These modules "
+            "are updated on cycles independent of the Chrome release tracks. "
+            "It runs in the browser process and communicates with a set of "
+            "servers using the Omaha protocol to find the latest versions of "
+            "components, download them, and register them with the rest of "
+            "Chrome. "
+            "The extension updater works similarly, but it updates user "
+            "extensions instead of Chrome components. "
+          trigger: "Manual or automatic software updates."
+          data:
+            "Various OS and Chrome parameters such as version, bitness, "
+            "release tracks, etc. The component and the extension ids are also "
+            "present in both the request and the response from the servers. "
+            "The URL that refers to a component CRX payload is obfuscated for "
+            "most components."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting: "This feature cannot be disabled."
+          chrome_policy {
+            ComponentUpdatesEnabled {
+              policy_options {mode: MANDATORY}
+              ComponentUpdatesEnabled: false
+            }
+          }
+        })");
+
+// Returns the string value of a header of the server response or an empty
+// string if the header is not available. Only the first header is returned
+// if multiple instances of the same header are present.
+std::string GetStringHeader(const network::SimpleURLLoader* simple_url_loader,
+                            const char* header_name) {
+  DCHECK(simple_url_loader);
+
+  const auto* response_info = simple_url_loader->ResponseInfo();
+  if (!response_info || !response_info->headers)
+    return {};
+
+  std::string header_value;
+  return response_info->headers->EnumerateHeader(nullptr, header_name,
+                                                 &header_value)
+             ? header_value
+             : std::string{};
+}
+
+// Returns the integral value of a header of the server response or -1 if
+// if the header is not available or a conversion error has occured.
+int64_t GetInt64Header(const network::SimpleURLLoader* simple_url_loader,
+                       const char* header_name) {
+  DCHECK(simple_url_loader);
+
+  const auto* response_info = simple_url_loader->ResponseInfo();
+  if (!response_info || !response_info->headers)
+    return -1;
+
+  return response_info->headers->GetInt64HeaderValue(header_name);
+}
+
+}  // namespace
+
+namespace update_client {
+
+NetworkFetcherImpl::NetworkFetcherImpl(
+    scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory)
+    : shared_url_network_factory_(shared_url_network_factory) {}
+NetworkFetcherImpl::~NetworkFetcherImpl() = default;
+
+void NetworkFetcherImpl::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(!simple_url_loader_);
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url;
+  resource_request->method = "POST";
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  for (const auto& header : post_additional_headers)
+    resource_request->headers.SetHeader(header.first, header.second);
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+  simple_url_loader_->SetRetryOptions(
+      kMaxRetriesOnNetworkChange,
+      network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+  simple_url_loader_->AttachStringForUpload(post_data, "application/json");
+  simple_url_loader_->SetOnResponseStartedCallback(base::BindOnce(
+      &NetworkFetcherImpl::OnResponseStartedCallback, base::Unretained(this),
+      std::move(response_started_callback)));
+  simple_url_loader_->SetOnDownloadProgressCallback(base::BindRepeating(
+      &NetworkFetcherImpl::OnProgressCallback, base::Unretained(this),
+      std::move(progress_callback)));
+  constexpr size_t kMaxResponseSize = 1024 * 1024;
+  simple_url_loader_->DownloadToString(
+      shared_url_network_factory_.get(),
+      base::BindOnce(
+          [](const network::SimpleURLLoader* simple_url_loader,
+             PostRequestCompleteCallback post_request_complete_callback,
+             std::unique_ptr<std::string> response_body) {
+            std::move(post_request_complete_callback)
+                .Run(std::move(response_body), simple_url_loader->NetError(),
+                     GetStringHeader(simple_url_loader, kHeaderEtag),
+                     GetInt64Header(simple_url_loader, kHeaderXRetryAfter));
+          },
+          simple_url_loader_.get(), std::move(post_request_complete_callback)),
+      kMaxResponseSize);
+}
+
+void NetworkFetcherImpl::DownloadToFile(
+    const GURL& url,
+    const base::FilePath& file_path,
+    ResponseStartedCallback response_started_callback,
+    ProgressCallback progress_callback,
+    DownloadToFileCompleteCallback download_to_file_complete_callback) {
+  DCHECK(!simple_url_loader_);
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url;
+  resource_request->method = "GET";
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+  simple_url_loader_->SetRetryOptions(
+      kMaxRetriesOnNetworkChange,
+      network::SimpleURLLoader::RetryMode::RETRY_ON_NETWORK_CHANGE);
+  simple_url_loader_->SetAllowPartialResults(true);
+  simple_url_loader_->SetOnResponseStartedCallback(base::BindOnce(
+      &NetworkFetcherImpl::OnResponseStartedCallback, base::Unretained(this),
+      std::move(response_started_callback)));
+  simple_url_loader_->SetOnDownloadProgressCallback(base::BindRepeating(
+      &NetworkFetcherImpl::OnProgressCallback, base::Unretained(this),
+      std::move(progress_callback)));
+  simple_url_loader_->DownloadToFile(
+      shared_url_network_factory_.get(),
+      base::BindOnce(
+          [](const network::SimpleURLLoader* simple_url_loader,
+             DownloadToFileCompleteCallback download_to_file_complete_callback,
+             base::FilePath file_path) {
+            std::move(download_to_file_complete_callback)
+                .Run(file_path, simple_url_loader->NetError(),
+                     simple_url_loader->GetContentSize());
+          },
+          simple_url_loader_.get(),
+          std::move(download_to_file_complete_callback)),
+      file_path);
+}
+
+void NetworkFetcherImpl::OnResponseStartedCallback(
+    ResponseStartedCallback response_started_callback,
+    const GURL& final_url,
+    const network::ResourceResponseHead& response_head) {
+  std::move(response_started_callback)
+      .Run(final_url,
+           response_head.headers ? response_head.headers->response_code() : -1,
+           response_head.content_length);
+}
+
+void NetworkFetcherImpl::OnProgressCallback(ProgressCallback progress_callback,
+                                            uint64_t current) {
+  progress_callback.Run(base::saturated_cast<int64_t>(current));
+}
+
+NetworkFetcherChromiumFactory::NetworkFetcherChromiumFactory(
+    scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory)
+    : shared_url_network_factory_(shared_url_network_factory) {}
+
+NetworkFetcherChromiumFactory::~NetworkFetcherChromiumFactory() = default;
+
+std::unique_ptr<NetworkFetcher> NetworkFetcherChromiumFactory::Create() const {
+  return std::make_unique<NetworkFetcherImpl>(shared_url_network_factory_);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/net/network_impl.h b/src/updater_components/update_client/net/network_impl.h
new file mode 100644
index 0000000..f28cf2f
--- /dev/null
+++ b/src/updater_components/update_client/net/network_impl.h
@@ -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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_NET_NETWORK_IMPL_H_
+#define COMPONENTS_UPDATE_CLIENT_NET_NETWORK_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/network.h"
+
+namespace network {
+struct ResourceResponseHead;
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
+
+namespace update_client {
+
+class NetworkFetcherImpl : public NetworkFetcher {
+ public:
+  explicit NetworkFetcherImpl(scoped_refptr<network::SharedURLLoaderFactory>
+                                  shared_url_network_factory);
+  ~NetworkFetcherImpl() 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:
+  void OnResponseStartedCallback(
+      ResponseStartedCallback response_started_callback,
+      const GURL& final_url,
+      const network::ResourceResponseHead& response_head);
+
+  void OnProgressCallback(ProgressCallback response_started_callback,
+                          uint64_t current);
+
+  static constexpr int kMaxRetriesOnNetworkChange = 3;
+
+  scoped_refptr<network::SharedURLLoaderFactory> shared_url_network_factory_;
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcherImpl);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_NET_NETWORK_IMPL_H_
diff --git a/src/updater_components/update_client/net/url_loader_post_interceptor.cc b/src/updater_components/update_client/net/url_loader_post_interceptor.cc
new file mode 100644
index 0000000..11a2af8
--- /dev/null
+++ b/src/updater_components/update_client/net/url_loader_post_interceptor.cc
@@ -0,0 +1,260 @@
+// Copyright 2018 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 "components/update_client/net/url_loader_post_interceptor.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/bind_test_util.h"
+#include "components/update_client/test_configurator.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+URLLoaderPostInterceptor::URLLoaderPostInterceptor(
+    network::TestURLLoaderFactory* url_loader_factory)
+    : url_loader_factory_(url_loader_factory) {
+  filtered_urls_.push_back(
+      GURL(base::StringPrintf("%s://%s%s", POST_INTERCEPT_SCHEME,
+                              POST_INTERCEPT_HOSTNAME, POST_INTERCEPT_PATH)));
+  InitializeWithInterceptor();
+}
+
+URLLoaderPostInterceptor::URLLoaderPostInterceptor(
+    std::vector<GURL> supported_urls,
+    network::TestURLLoaderFactory* url_loader_factory)
+    : url_loader_factory_(url_loader_factory) {
+  DCHECK_LT(0u, supported_urls.size());
+  filtered_urls_.swap(supported_urls);
+  InitializeWithInterceptor();
+}
+
+URLLoaderPostInterceptor::URLLoaderPostInterceptor(
+    std::vector<GURL> supported_urls,
+    net::test_server::EmbeddedTestServer* embedded_test_server)
+    : embedded_test_server_(embedded_test_server) {
+  DCHECK_LT(0u, supported_urls.size());
+  filtered_urls_.swap(supported_urls);
+  InitializeWithRequestHandler();
+}
+
+URLLoaderPostInterceptor::~URLLoaderPostInterceptor() {}
+
+bool URLLoaderPostInterceptor::ExpectRequest(
+    std::unique_ptr<RequestMatcher> request_matcher) {
+  return ExpectRequest(std::move(request_matcher), net::HTTP_OK);
+}
+
+bool URLLoaderPostInterceptor::ExpectRequest(
+    std::unique_ptr<RequestMatcher> request_matcher,
+    net::HttpStatusCode response_code) {
+  expectations_.push(
+      {std::move(request_matcher), ExpectationResponse(response_code, "")});
+  return true;
+}
+
+bool URLLoaderPostInterceptor::ExpectRequest(
+    std::unique_ptr<RequestMatcher> request_matcher,
+    const base::FilePath& filepath) {
+  std::string response;
+  if (filepath.empty() || !base::ReadFileToString(filepath, &response))
+    return false;
+
+  expectations_.push({std::move(request_matcher),
+                      ExpectationResponse(net::HTTP_OK, response)});
+  return true;
+}
+
+// Returns how many requests have been intercepted and matched by
+// an expectation. One expectation can only be matched by one request.
+int URLLoaderPostInterceptor::GetHitCount() const {
+  return hit_count_;
+}
+
+// Returns how many requests in total have been captured by the interceptor.
+int URLLoaderPostInterceptor::GetCount() const {
+  return static_cast<int>(requests_.size());
+}
+
+// Returns all requests that have been intercepted, matched or not.
+std::vector<URLLoaderPostInterceptor::InterceptedRequest>
+URLLoaderPostInterceptor::GetRequests() const {
+  return requests_;
+}
+
+// Return the body of the n-th request, zero-based.
+std::string URLLoaderPostInterceptor::GetRequestBody(size_t n) const {
+  return std::get<0>(requests_[n]);
+}
+
+// Returns the joined bodies of all requests for debugging purposes.
+std::string URLLoaderPostInterceptor::GetRequestsAsString() const {
+  const std::vector<InterceptedRequest> requests = GetRequests();
+  std::string s = "Requests are:";
+  int i = 0;
+  for (auto it = requests.cbegin(); it != requests.cend(); ++it)
+    s.append(base::StringPrintf("\n  [%d]: %s", ++i, std::get<0>(*it).c_str()));
+  return s;
+}
+
+// Resets the state of the interceptor so that new expectations can be set.
+void URLLoaderPostInterceptor::Reset() {
+  hit_count_ = 0;
+  requests_.clear();
+  base::queue<Expectation>().swap(expectations_);
+}
+
+void URLLoaderPostInterceptor::Pause() {
+  is_paused_ = true;
+}
+
+void URLLoaderPostInterceptor::Resume() {
+  is_paused_ = false;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        if (!pending_expectations_.size())
+          return;
+
+        PendingExpectation expectation =
+            std::move(pending_expectations_.front());
+        pending_expectations_.pop();
+        url_loader_factory_->AddResponse(expectation.first.spec(),
+                                         expectation.second.response_body,
+                                         expectation.second.response_code);
+      }));
+}
+
+void URLLoaderPostInterceptor::url_job_request_ready_callback(
+    UrlJobRequestReadyCallback url_job_request_ready_callback) {
+  url_job_request_ready_callback_ = std::move(url_job_request_ready_callback);
+}
+
+int URLLoaderPostInterceptor::GetHitCountForURL(const GURL& url) {
+  int hit_count = 0;
+  const std::vector<InterceptedRequest> requests = GetRequests();
+  for (auto it = requests.cbegin(); it != requests.cend(); ++it) {
+    GURL url_no_query = std::get<2>(*it);
+    if (url_no_query.has_query()) {
+      GURL::Replacements replacements;
+      replacements.ClearQuery();
+      url_no_query = url_no_query.ReplaceComponents(replacements);
+    }
+    if (url_no_query == url)
+      hit_count++;
+  }
+  return hit_count;
+}
+
+void URLLoaderPostInterceptor::InitializeWithInterceptor() {
+  DCHECK(url_loader_factory_);
+  url_loader_factory_->SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        GURL url = request.url;
+        if (url.has_query()) {
+          GURL::Replacements replacements;
+          replacements.ClearQuery();
+          url = url.ReplaceComponents(replacements);
+        }
+        auto it = std::find_if(
+            filtered_urls_.begin(), filtered_urls_.end(),
+            [url](const GURL& filtered_url) { return filtered_url == url; });
+        if (it == filtered_urls_.end())
+          return;
+
+        std::string request_body = network::GetUploadData(request);
+        requests_.push_back({request_body, request.headers, request.url});
+        if (expectations_.empty())
+          return;
+        const auto& expectation = expectations_.front();
+        if (expectation.first->Match(request_body)) {
+          const net::HttpStatusCode response_code(
+              expectation.second.response_code);
+          const std::string response_body(expectation.second.response_body);
+
+          if (url_job_request_ready_callback_) {
+            base::ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, std::move(url_job_request_ready_callback_));
+          }
+
+          if (!is_paused_) {
+            url_loader_factory_->AddResponse(request.url.spec(), response_body,
+                                             response_code);
+          } else {
+            pending_expectations_.push({request.url, expectation.second});
+          }
+          expectations_.pop();
+          ++hit_count_;
+        }
+      }));
+}
+
+void URLLoaderPostInterceptor::InitializeWithRequestHandler() {
+  DCHECK(embedded_test_server_);
+  DCHECK(!url_loader_factory_);
+  embedded_test_server_->RegisterRequestHandler(base::BindRepeating(
+      &URLLoaderPostInterceptor::RequestHandler, base::Unretained(this)));
+}
+
+std::unique_ptr<net::test_server::HttpResponse>
+URLLoaderPostInterceptor::RequestHandler(
+    const net::test_server::HttpRequest& request) {
+  // Only intercepts POST.
+  if (request.method != net::test_server::METHOD_POST)
+    return nullptr;
+
+  GURL url = request.GetURL();
+  if (url.has_query()) {
+    GURL::Replacements replacements;
+    replacements.ClearQuery();
+    url = url.ReplaceComponents(replacements);
+  }
+  auto it = std::find_if(
+      filtered_urls_.begin(), filtered_urls_.end(),
+      [url](const GURL& filtered_url) { return filtered_url == url; });
+  if (it == filtered_urls_.end())
+    return nullptr;
+
+  std::string request_body = request.content;
+  net::HttpRequestHeaders headers;
+  for (auto it : request.headers)
+    headers.SetHeader(it.first, it.second);
+  requests_.push_back({request_body, headers, url});
+  if (expectations_.empty())
+    return nullptr;
+
+  const auto& expectation = expectations_.front();
+  if (expectation.first->Match(request_body)) {
+    const net::HttpStatusCode response_code(expectation.second.response_code);
+    const std::string response_body(expectation.second.response_body);
+    expectations_.pop();
+    ++hit_count_;
+
+    std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
+        new net::test_server::BasicHttpResponse);
+    http_response->set_code(response_code);
+    http_response->set_content(response_body);
+    return http_response;
+  }
+
+  return nullptr;
+}
+
+bool PartialMatch::Match(const std::string& actual) const {
+  return actual.find(expected_) != std::string::npos;
+}
+
+bool AnyMatch::Match(const std::string& actual) const {
+  return true;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/net/url_loader_post_interceptor.h b/src/updater_components/update_client/net/url_loader_post_interceptor.h
new file mode 100644
index 0000000..9907f49
--- /dev/null
+++ b/src/updater_components/update_client/net/url_loader_post_interceptor.h
@@ -0,0 +1,181 @@
+// Copyright 2018 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 COMPONENTS_UPDATE_CLIENT_NET_URL_LOADER_POST_INTERCEPTOR_H_
+#define COMPONENTS_UPDATE_CLIENT_NET_URL_LOADER_POST_INTERCEPTOR_H_
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/queue.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
+#include "url/gurl.h"
+
+namespace network {
+class TestURLLoaderFactory;
+}
+
+namespace net {
+namespace test_server {
+class EmbeddedTestServer;
+class HttpResponse;
+struct HttpRequest;
+}  // namespace test_server
+}  // namespace net
+
+namespace update_client {
+
+// Intercepts requests to a file path, counts them, and captures the body of
+// the requests. Optionally, for each request, it can return a canned response
+// from a given file. The class maintains a queue of expectations, and returns
+// one and only one response for each request that matches the expectation.
+// Then, the expectation is removed from the queue.
+class URLLoaderPostInterceptor {
+ public:
+  using InterceptedRequest =
+      std::tuple<std::string, net::HttpRequestHeaders, GURL>;
+
+  // Called when the load associated with the url request is intercepted
+  // by this object:.
+  using UrlJobRequestReadyCallback = base::OnceCallback<void()>;
+
+  // Allows a generic string maching interface when setting up expectations.
+  class RequestMatcher {
+   public:
+    virtual bool Match(const std::string& actual) const = 0;
+    virtual ~RequestMatcher() {}
+  };
+
+  explicit URLLoaderPostInterceptor(
+      network::TestURLLoaderFactory* url_loader_factory);
+  URLLoaderPostInterceptor(std::vector<GURL> supported_urls,
+                           network::TestURLLoaderFactory* url_loader_factory);
+  URLLoaderPostInterceptor(std::vector<GURL> supported_urls,
+                           net::test_server::EmbeddedTestServer*);
+
+  ~URLLoaderPostInterceptor();
+
+  // Sets an expection for the body of the POST request and optionally,
+  // provides a canned response identified by a |file_path| to be returned when
+  // the expectation is met. If no |file_path| is provided, then an empty
+  // response body is served. If |response_code| is provided, then an empty
+  // response body with that response code is returned.
+  // Returns |true| if the expectation was set.
+  bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher);
+
+  bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher,
+                     net::HttpStatusCode response_code);
+
+  bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher,
+                     const base::FilePath& filepath);
+
+  // Returns how many requests have been intercepted and matched by
+  // an expectation. One expectation can only be matched by one request.
+  int GetHitCount() const;
+
+  // Returns how many requests in total have been captured by the interceptor.
+  int GetCount() const;
+
+  // Returns all requests that have been intercepted, matched or not.
+  std::vector<InterceptedRequest> GetRequests() const;
+
+  // Return the body of the n-th request, zero-based.
+  std::string GetRequestBody(size_t n) const;
+
+  // Returns the joined bodies of all requests for debugging purposes.
+  std::string GetRequestsAsString() const;
+
+  // Resets the state of the interceptor so that new expectations can be set.
+  void Reset();
+
+  // Prevents the intercepted request from starting, as a way to simulate
+  // the effects of a very slow network. Call this function before the actual
+  // network request occurs.
+  void Pause();
+
+  // Allows a previously paused request to continue.
+  void Resume();
+
+  // Sets a callback to be invoked when the request job associated with
+  // an intercepted request is created. This allows the test execution to
+  // synchronize with network tasks running on the IO thread and avoid polling
+  // using idle run loops. A paused request can be resumed after this callback
+  // has been invoked.
+  void url_job_request_ready_callback(
+      UrlJobRequestReadyCallback url_job_request_ready_callback);
+
+  int GetHitCountForURL(const GURL& url);
+
+ private:
+  void InitializeWithInterceptor();
+  void InitializeWithRequestHandler();
+
+  std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
+      const net::test_server::HttpRequest& request);
+
+  struct ExpectationResponse {
+    ExpectationResponse(net::HttpStatusCode code, const std::string& body)
+        : response_code(code), response_body(body) {}
+    const net::HttpStatusCode response_code;
+    const std::string response_body;
+  };
+  using Expectation =
+      std::pair<std::unique_ptr<RequestMatcher>, ExpectationResponse>;
+
+  using PendingExpectation = std::pair<GURL, ExpectationResponse>;
+
+  // Contains the count of the request matching expectations.
+  int hit_count_ = 0;
+
+  // Contains the request body and the extra headers of the intercepted
+  // requests.
+  std::vector<InterceptedRequest> requests_;
+
+  // Contains the expectations which this interceptor tries to match.
+  base::queue<Expectation> expectations_;
+
+  base::queue<PendingExpectation> pending_expectations_;
+
+  network::TestURLLoaderFactory* url_loader_factory_ = nullptr;
+  net::test_server::EmbeddedTestServer* embedded_test_server_ = nullptr;
+
+  bool is_paused_ = false;
+
+  std::vector<GURL> filtered_urls_;
+
+  UrlJobRequestReadyCallback url_job_request_ready_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLLoaderPostInterceptor);
+};
+
+class PartialMatch : public URLLoaderPostInterceptor::RequestMatcher {
+ public:
+  explicit PartialMatch(const std::string& expected) : expected_(expected) {}
+  bool Match(const std::string& actual) const override;
+
+ private:
+  const std::string expected_;
+
+  DISALLOW_COPY_AND_ASSIGN(PartialMatch);
+};
+
+class AnyMatch : public URLLoaderPostInterceptor::RequestMatcher {
+ public:
+  AnyMatch() = default;
+  bool Match(const std::string& actual) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AnyMatch);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_NET_URL_LOADER_POST_INTERCEPTOR_H_
diff --git a/src/updater_components/update_client/network.cc b/src/updater_components/update_client/network.cc
new file mode 100644
index 0000000..cb8e151
--- /dev/null
+++ b/src/updater_components/update_client/network.cc
@@ -0,0 +1,12 @@
+// 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 "components/update_client/network.h"
+
+namespace update_client {
+
+constexpr char NetworkFetcher::kHeaderEtag[];
+constexpr char NetworkFetcher::kHeaderXRetryAfter[];
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/network.h b/src/updater_components/update_client/network.h
new file mode 100644
index 0000000..daed162
--- /dev/null
+++ b/src/updater_components/update_client/network.h
@@ -0,0 +1,89 @@
+// 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 COMPONENTS_UPDATE_CLIENT_NETWORK_H_
+#define COMPONENTS_UPDATE_CLIENT_NETWORK_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+class GURL;
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace update_client {
+
+class NetworkFetcher {
+ public:
+  using PostRequestCompleteCallback =
+      base::OnceCallback<void(std::unique_ptr<std::string> response_body,
+                              int net_error,
+                              const std::string& header_etag,
+                              int64_t xheader_retry_after_sec)>;
+  using DownloadToFileCompleteCallback = base::OnceCallback<
+      void(base::FilePath path, int net_error, int64_t content_size)>;
+  using ResponseStartedCallback = base::OnceCallback<
+      void(const GURL& final_url, int response_code, int64_t content_length)>;
+  using ProgressCallback = base::RepeatingCallback<void(int64_t current)>;
+
+  // The ETag header carries the ECSDA signature of the POST response, if
+  // signing has been used.
+  static constexpr char kHeaderEtag[] = "ETag";
+
+  // The server uses the optional X-Retry-After header to indicate that the
+  // current request should not be attempted again.
+  //
+  // The value of the header is the number of seconds to wait before trying to
+  // do a subsequent update check. Only the values retrieved over HTTPS are
+  // trusted.
+  static constexpr char kHeaderXRetryAfter[] = "X-Retry-After";
+
+  virtual ~NetworkFetcher() = default;
+
+  virtual 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) = 0;
+  virtual void DownloadToFile(
+      const GURL& url,
+      const base::FilePath& file_path,
+      ResponseStartedCallback response_started_callback,
+      ProgressCallback progress_callback,
+      DownloadToFileCompleteCallback download_to_file_complete_callback) = 0;
+
+ protected:
+  NetworkFetcher() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcher);
+};
+
+class NetworkFetcherFactory : public base::RefCounted<NetworkFetcherFactory> {
+ public:
+  virtual std::unique_ptr<NetworkFetcher> Create() const = 0;
+
+ protected:
+  friend class base::RefCounted<NetworkFetcherFactory>;
+  NetworkFetcherFactory() = default;
+  virtual ~NetworkFetcherFactory() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NetworkFetcherFactory);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_NETWORK_H_
diff --git a/src/updater_components/update_client/patch/patch_impl.cc b/src/updater_components/update_client/patch/patch_impl.cc
new file mode 100644
index 0000000..599101f
--- /dev/null
+++ b/src/updater_components/update_client/patch/patch_impl.cc
@@ -0,0 +1,53 @@
+// 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 "components/update_client/patch/patch_impl.h"
+
+#include "components/services/patch/public/cpp/patch.h"
+#include "components/update_client/component_patcher_operation.h"
+
+namespace update_client {
+
+namespace {
+
+class PatcherImpl : public Patcher {
+ public:
+  explicit PatcherImpl(PatchChromiumFactory::Callback callback)
+      : callback_(std::move(callback)) {}
+
+  void PatchBsdiff(const base::FilePath& old_file,
+                   const base::FilePath& patch_file,
+                   const base::FilePath& destination,
+                   PatchCompleteCallback callback) const override {
+    patch::Patch(callback_.Run(), update_client::kBsdiff, old_file, patch_file,
+                 destination, std::move(callback));
+  }
+
+  void PatchCourgette(const base::FilePath& old_file,
+                      const base::FilePath& patch_file,
+                      const base::FilePath& destination,
+                      PatchCompleteCallback callback) const override {
+    patch::Patch(callback_.Run(), update_client::kCourgette, old_file,
+                 patch_file, destination, std::move(callback));
+  }
+
+ protected:
+  ~PatcherImpl() override = default;
+
+ private:
+  const PatchChromiumFactory::Callback callback_;
+};
+
+}  // namespace
+
+PatchChromiumFactory::PatchChromiumFactory(Callback callback)
+    : callback_(std::move(callback)) {}
+
+scoped_refptr<Patcher> PatchChromiumFactory::Create() const {
+  return base::MakeRefCounted<PatcherImpl>(callback_);
+}
+
+PatchChromiumFactory::~PatchChromiumFactory() = default;
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/patch/patch_impl.h b/src/updater_components/update_client/patch/patch_impl.h
new file mode 100644
index 0000000..e76666d
--- /dev/null
+++ b/src/updater_components/update_client/patch/patch_impl.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 COMPONENTS_UPDATE_CLIENT_PATCH_PATCH_IMPL_H_
+#define COMPONENTS_UPDATE_CLIENT_PATCH_PATCH_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/services/patch/public/mojom/file_patcher.mojom.h"
+#include "components/update_client/patcher.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+
+namespace update_client {
+
+class PatchChromiumFactory : public PatcherFactory {
+ public:
+  using Callback =
+      base::RepeatingCallback<mojo::PendingRemote<patch::mojom::FilePatcher>()>;
+  explicit PatchChromiumFactory(Callback callback);
+
+  scoped_refptr<Patcher> Create() const override;
+
+ protected:
+  ~PatchChromiumFactory() override;
+
+ private:
+  const Callback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(PatchChromiumFactory);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PATCH_PATCH_IMPL_H_
diff --git a/src/updater_components/update_client/patcher.h b/src/updater_components/update_client/patcher.h
new file mode 100644
index 0000000..1316d26
--- /dev/null
+++ b/src/updater_components/update_client/patcher.h
@@ -0,0 +1,56 @@
+// 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 COMPONENTS_UPDATE_CLIENT_PATCHER_H_
+#define COMPONENTS_UPDATE_CLIENT_PATCHER_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace update_client {
+
+class Patcher : public base::RefCountedThreadSafe<Patcher> {
+ public:
+  using PatchCompleteCallback = base::OnceCallback<void(int result)>;
+
+  virtual void PatchBsdiff(const base::FilePath& input_file,
+                           const base::FilePath& patch_file,
+                           const base::FilePath& destination,
+                           PatchCompleteCallback callback) const = 0;
+
+  virtual void PatchCourgette(const base::FilePath& input_file,
+                              const base::FilePath& patch_file,
+                              const base::FilePath& destination,
+                              PatchCompleteCallback callback) const = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<Patcher>;
+  Patcher() = default;
+  virtual ~Patcher() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Patcher);
+};
+
+class PatcherFactory : public base::RefCountedThreadSafe<PatcherFactory> {
+ public:
+  virtual scoped_refptr<Patcher> Create() const = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<PatcherFactory>;
+  PatcherFactory() = default;
+  virtual ~PatcherFactory() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PatcherFactory);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PATCHER_H_
diff --git a/src/updater_components/update_client/persisted_data.cc b/src/updater_components/update_client/persisted_data.cc
new file mode 100644
index 0000000..ea1ad1e
--- /dev/null
+++ b/src/updater_components/update_client/persisted_data.cc
@@ -0,0 +1,175 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/persisted_data.h"
+
+#include <string>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_checker.h"
+#include "base/values.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/update_client/activity_data_service.h"
+
+const char kPersistedDataPreference[] = "updateclientdata";
+
+namespace update_client {
+
+PersistedData::PersistedData(PrefService* pref_service,
+                             ActivityDataService* activity_data_service)
+    : pref_service_(pref_service),
+      activity_data_service_(activity_data_service) {}
+
+PersistedData::~PersistedData() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+int PersistedData::GetInt(const std::string& id,
+                          const std::string& key,
+                          int fallback) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // We assume ids do not contain '.' characters.
+  DCHECK_EQ(std::string::npos, id.find('.'));
+  if (!pref_service_)
+    return fallback;
+  const base::DictionaryValue* dict =
+      pref_service_->GetDictionary(kPersistedDataPreference);
+  if (!dict)
+    return fallback;
+  int result = 0;
+  return dict->GetInteger(
+             base::StringPrintf("apps.%s.%s", id.c_str(), key.c_str()), &result)
+             ? result
+             : fallback;
+}
+
+std::string PersistedData::GetString(const std::string& id,
+                                     const std::string& key) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // We assume ids do not contain '.' characters.
+  DCHECK_EQ(std::string::npos, id.find('.'));
+  if (!pref_service_)
+    return std::string();
+  const base::DictionaryValue* dict =
+      pref_service_->GetDictionary(kPersistedDataPreference);
+  if (!dict)
+    return std::string();
+  std::string result;
+  return dict->GetString(
+             base::StringPrintf("apps.%s.%s", id.c_str(), key.c_str()), &result)
+             ? result
+             : std::string();
+}
+
+int PersistedData::GetDateLastRollCall(const std::string& id) const {
+  return GetInt(id, "dlrc", kDateUnknown);
+}
+
+int PersistedData::GetDateLastActive(const std::string& id) const {
+  return GetInt(id, "dla", kDateUnknown);
+}
+
+std::string PersistedData::GetPingFreshness(const std::string& id) const {
+  std::string result = GetString(id, "pf");
+  return !result.empty() ? base::StringPrintf("{%s}", result.c_str()) : result;
+}
+
+std::string PersistedData::GetCohort(const std::string& id) const {
+  return GetString(id, "cohort");
+}
+
+std::string PersistedData::GetCohortName(const std::string& id) const {
+  return GetString(id, "cohortname");
+}
+
+std::string PersistedData::GetCohortHint(const std::string& id) const {
+  return GetString(id, "cohorthint");
+}
+
+void PersistedData::SetDateLastRollCall(const std::vector<std::string>& ids,
+                                        int datenum) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!pref_service_ || datenum < 0)
+    return;
+  DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference);
+  for (const auto& id : ids) {
+    // We assume ids do not contain '.' characters.
+    DCHECK_EQ(std::string::npos, id.find('.'));
+    update->SetInteger(base::StringPrintf("apps.%s.dlrc", id.c_str()), datenum);
+    update->SetString(base::StringPrintf("apps.%s.pf", id.c_str()),
+                      base::GenerateGUID());
+  }
+}
+
+void PersistedData::SetDateLastActive(const std::vector<std::string>& ids,
+                                      int datenum) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!pref_service_ || datenum < 0)
+    return;
+  DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference);
+  for (const auto& id : ids) {
+    if (GetActiveBit(id)) {
+      // We assume ids do not contain '.' characters.
+      DCHECK_EQ(std::string::npos, id.find('.'));
+      update->SetInteger(base::StringPrintf("apps.%s.dla", id.c_str()),
+                         datenum);
+      activity_data_service_->ClearActiveBit(id);
+    }
+  }
+}
+
+void PersistedData::SetString(const std::string& id,
+                              const std::string& key,
+                              const std::string& value) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!pref_service_)
+    return;
+  DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference);
+  update->SetString(base::StringPrintf("apps.%s.%s", id.c_str(), key.c_str()),
+                    value);
+}
+
+void PersistedData::SetCohort(const std::string& id,
+                              const std::string& cohort) {
+  SetString(id, "cohort", cohort);
+}
+
+void PersistedData::SetCohortName(const std::string& id,
+                                  const std::string& cohort_name) {
+  SetString(id, "cohortname", cohort_name);
+}
+
+void PersistedData::SetCohortHint(const std::string& id,
+                                  const std::string& cohort_hint) {
+  SetString(id, "cohorthint", cohort_hint);
+}
+
+bool PersistedData::GetActiveBit(const std::string& id) const {
+  return activity_data_service_ && activity_data_service_->GetActiveBit(id);
+}
+
+int PersistedData::GetDaysSinceLastRollCall(const std::string& id) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return activity_data_service_
+             ? activity_data_service_->GetDaysSinceLastRollCall(id)
+             : kDaysUnknown;
+}
+
+int PersistedData::GetDaysSinceLastActive(const std::string& id) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return activity_data_service_
+             ? activity_data_service_->GetDaysSinceLastActive(id)
+             : kDaysUnknown;
+}
+
+void PersistedData::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(kPersistedDataPreference);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/persisted_data.h b/src/updater_components/update_client/persisted_data.h
new file mode 100644
index 0000000..d35bd15
--- /dev/null
+++ b/src/updater_components/update_client/persisted_data.h
@@ -0,0 +1,118 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_PERSISTED_DATA_H_
+#define COMPONENTS_UPDATE_CLIENT_PERSISTED_DATA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/values.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace update_client {
+
+class ActivityDataService;
+
+// A PersistedData is a wrapper layer around a PrefService, designed to maintain
+// update data that outlives the browser process and isn't exposed outside of
+// update_client.
+//
+// The public methods of this class should be called only on the thread that
+// initializes it - which also has to match the thread the PrefService has been
+// initialized on.
+class PersistedData {
+ public:
+  // Constructs a provider using the specified |pref_service| and
+  // |activity_data_service|.
+  // The associated preferences are assumed to already be registered.
+  // The |pref_service| and |activity_data_service| must outlive the entire
+  // update_client.
+  PersistedData(PrefService* pref_service,
+                ActivityDataService* activity_data_service);
+
+  ~PersistedData();
+
+  // Returns the DateLastRollCall (the server-localized calendar date number the
+  // |id| was last checked by this client on) for the specified |id|.
+  // -2 indicates that there is no recorded date number for the |id|.
+  int GetDateLastRollCall(const std::string& id) const;
+
+  // Returns the DateLastActive (the server-localized calendar date number the
+  // |id| was last active by this client on) for the specified |id|.
+  // -1 indicates that there is no recorded date for the |id| (i.e. this is the
+  // first time the |id| is active).
+  // -2 indicates that the |id| has an unknown value of last active date.
+  int GetDateLastActive(const std::string& id) const;
+
+  // Returns the PingFreshness (a random token that is written into the profile
+  // data whenever the DateLastRollCall it is modified) for the specified |id|.
+  // "" indicates that there is no recorded freshness value for the |id|.
+  std::string GetPingFreshness(const std::string& id) const;
+
+  // Records the DateLastRollCall for the specified |ids|. |datenum| must be a
+  // non-negative integer: calls with a negative |datenum| are simply ignored.
+  // Calls to SetDateLastRollCall that occur prior to the persisted data store
+  // has been fully initialized are ignored. Also sets the PingFreshness.
+  void SetDateLastRollCall(const std::vector<std::string>& ids, int datenum);
+
+  // Records the DateLastActive for the specified |ids|. |datenum| must be a
+  // non-negative integer: calls with a negative |datenum| are simply ignored.
+  // Calls to SetDateLastActive that occur prior to the persisted data store
+  // has been fully initialized or the active bit of the |ids| are not set
+  // are ignored.
+  // This function also clears the active bits of the specified |ids| if they
+  // are set.
+  void SetDateLastActive(const std::vector<std::string>& ids, int datenum);
+
+  // This is called only via update_client's RegisterUpdateClientPreferences.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  // These functions return cohort data for the specified |id|. "Cohort"
+  // indicates the membership of the client in any release channels components
+  // have set up in a machine-readable format, while "CohortName" does so in a
+  // human-readable form. "CohortHint" indicates the client's channel selection
+  // preference.
+  std::string GetCohort(const std::string& id) const;
+  std::string GetCohortHint(const std::string& id) const;
+  std::string GetCohortName(const std::string& id) const;
+
+  // These functions set cohort data for the specified |id|.
+  void SetCohort(const std::string& id, const std::string& cohort);
+  void SetCohortHint(const std::string& id, const std::string& cohort_hint);
+  void SetCohortName(const std::string& id, const std::string& cohort_name);
+
+  // Returns true if the active bit of the specified |id| is set.
+  bool GetActiveBit(const std::string& id) const;
+
+  // The following two functions returns the number of days since the last
+  // time the client checked for update/was active.
+  // -1 indicates that this is the first time the client reports
+  // an update check/active for the specified |id|.
+  // -2 indicates that the client has no information about the
+  // update check/last active of the specified |id|.
+  int GetDaysSinceLastRollCall(const std::string& id) const;
+  int GetDaysSinceLastActive(const std::string& id) const;
+
+ private:
+  int GetInt(const std::string& id, const std::string& key, int fallback) const;
+  std::string GetString(const std::string& id, const std::string& key) const;
+  void SetString(const std::string& id,
+                 const std::string& key,
+                 const std::string& value);
+
+  base::ThreadChecker thread_checker_;
+  PrefService* pref_service_;
+  ActivityDataService* activity_data_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(PersistedData);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PERSISTED_DATA_H_
diff --git a/src/updater_components/update_client/persisted_data_unittest.cc b/src/updater_components/update_client/persisted_data_unittest.cc
new file mode 100644
index 0000000..7f7ae6e
--- /dev/null
+++ b/src/updater_components/update_client/persisted_data_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/prefs/testing_pref_service.h"
+#include "components/update_client/activity_data_service.h"
+#include "components/update_client/persisted_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+TEST(PersistedDataTest, Simple) {
+  auto pref = std::make_unique<TestingPrefServiceSimple>();
+  PersistedData::RegisterPrefs(pref->registry());
+  auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr);
+  EXPECT_EQ(-2, metadata->GetDateLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid"));
+  std::vector<std::string> items;
+  items.push_back("someappid");
+  metadata->SetDateLastRollCall(items, 3383);
+  metadata->SetDateLastActive(items, 3383);
+  EXPECT_EQ(3383, metadata->GetDateLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastRollCall("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someotherappid"));
+  const std::string pf1 = metadata->GetPingFreshness("someappid");
+  EXPECT_FALSE(pf1.empty());
+  metadata->SetDateLastRollCall(items, 3386);
+  metadata->SetDateLastActive(items, 3386);
+  EXPECT_EQ(3386, metadata->GetDateLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastRollCall("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someotherappid"));
+  const std::string pf2 = metadata->GetPingFreshness("someappid");
+  EXPECT_FALSE(pf2.empty());
+  // The following has a 1 / 2^128 chance of being flaky.
+  EXPECT_NE(pf1, pf2);
+}
+
+TEST(PersistedDataTest, SharedPref) {
+  auto pref = std::make_unique<TestingPrefServiceSimple>();
+  PersistedData::RegisterPrefs(pref->registry());
+  auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr);
+  EXPECT_EQ(-2, metadata->GetDateLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid"));
+  std::vector<std::string> items;
+  items.push_back("someappid");
+  metadata->SetDateLastRollCall(items, 3383);
+  metadata->SetDateLastActive(items, 3383);
+
+  // Now, create a new PersistedData reading from the same path, verify
+  // that it loads the value.
+  metadata = std::make_unique<PersistedData>(pref.get(), nullptr);
+  EXPECT_EQ(3383, metadata->GetDateLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastRollCall("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastRollCall("someotherappid"));
+  EXPECT_EQ(-2, metadata->GetDaysSinceLastActive("someotherappid"));
+}
+
+TEST(PersistedDataTest, SimpleCohort) {
+  auto pref = std::make_unique<TestingPrefServiceSimple>();
+  PersistedData::RegisterPrefs(pref->registry());
+  auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr);
+  EXPECT_EQ("", metadata->GetCohort("someappid"));
+  EXPECT_EQ("", metadata->GetCohort("someotherappid"));
+  EXPECT_EQ("", metadata->GetCohortHint("someappid"));
+  EXPECT_EQ("", metadata->GetCohortHint("someotherappid"));
+  EXPECT_EQ("", metadata->GetCohortName("someappid"));
+  EXPECT_EQ("", metadata->GetCohortName("someotherappid"));
+  metadata->SetCohort("someappid", "c1");
+  metadata->SetCohort("someotherappid", "c2");
+  metadata->SetCohortHint("someappid", "ch1");
+  metadata->SetCohortHint("someotherappid", "ch2");
+  metadata->SetCohortName("someappid", "cn1");
+  metadata->SetCohortName("someotherappid", "cn2");
+  EXPECT_EQ("c1", metadata->GetCohort("someappid"));
+  EXPECT_EQ("c2", metadata->GetCohort("someotherappid"));
+  EXPECT_EQ("ch1", metadata->GetCohortHint("someappid"));
+  EXPECT_EQ("ch2", metadata->GetCohortHint("someotherappid"));
+  EXPECT_EQ("cn1", metadata->GetCohortName("someappid"));
+  EXPECT_EQ("cn2", metadata->GetCohortName("someotherappid"));
+  metadata->SetCohort("someappid", "oc1");
+  metadata->SetCohort("someotherappid", "oc2");
+  metadata->SetCohortHint("someappid", "och1");
+  metadata->SetCohortHint("someotherappid", "och2");
+  metadata->SetCohortName("someappid", "ocn1");
+  metadata->SetCohortName("someotherappid", "ocn2");
+  EXPECT_EQ("oc1", metadata->GetCohort("someappid"));
+  EXPECT_EQ("oc2", metadata->GetCohort("someotherappid"));
+  EXPECT_EQ("och1", metadata->GetCohortHint("someappid"));
+  EXPECT_EQ("och2", metadata->GetCohortHint("someotherappid"));
+  EXPECT_EQ("ocn1", metadata->GetCohortName("someappid"));
+  EXPECT_EQ("ocn2", metadata->GetCohortName("someotherappid"));
+}
+
+TEST(PersistedDataTest, ActivityData) {
+  class TestActivityDataService : public ActivityDataService {
+   public:
+    bool GetActiveBit(const std::string& id) const override {
+      auto it = active_.find(id);
+      return it != active_.end() ? it->second : false;
+    }
+
+    int GetDaysSinceLastActive(const std::string& id) const override {
+      auto it = days_last_active_.find(id);
+      return it != days_last_active_.end() ? it->second : -1;
+    }
+
+    int GetDaysSinceLastRollCall(const std::string& id) const override {
+      auto it = days_last_roll_call_.find(id);
+      return it != days_last_roll_call_.end() ? it->second : -1;
+    }
+
+    void ClearActiveBit(const std::string& id) override { active_[id] = false; }
+
+    void SetDaysSinceLastActive(const std::string& id, int numdays) {
+      days_last_active_[id] = numdays;
+    }
+
+    void SetDaysSinceLastRollCall(const std::string& id, int numdays) {
+      days_last_roll_call_[id] = numdays;
+    }
+
+    void SetActiveBit(const std::string& id) { active_[id] = true; }
+
+   private:
+    std::map<std::string, bool> active_;
+    std::map<std::string, int> days_last_active_;
+    std::map<std::string, int> days_last_roll_call_;
+  };
+
+  auto pref = std::make_unique<TestingPrefServiceSimple>();
+  auto activity_service = std::make_unique<TestActivityDataService>();
+  PersistedData::RegisterPrefs(pref->registry());
+  auto metadata =
+      std::make_unique<PersistedData>(pref.get(), activity_service.get());
+
+  std::vector<std::string> items({"id1", "id2", "id3"});
+
+  for (const auto& item : items) {
+    EXPECT_EQ(-2, metadata->GetDateLastActive(item));
+    EXPECT_EQ(-2, metadata->GetDateLastRollCall(item));
+    EXPECT_EQ(-1, metadata->GetDaysSinceLastActive(item));
+    EXPECT_EQ(-1, metadata->GetDaysSinceLastRollCall(item));
+    EXPECT_EQ(false, metadata->GetActiveBit(item));
+  }
+
+  metadata->SetDateLastActive(items, 1234);
+  metadata->SetDateLastRollCall(items, 1234);
+  for (const auto& item : items) {
+    EXPECT_EQ(false, metadata->GetActiveBit(item));
+    EXPECT_EQ(-2, metadata->GetDateLastActive(item));
+    EXPECT_EQ(1234, metadata->GetDateLastRollCall(item));
+    EXPECT_EQ(-1, metadata->GetDaysSinceLastActive(item));
+    EXPECT_EQ(-1, metadata->GetDaysSinceLastRollCall(item));
+  }
+
+  activity_service->SetActiveBit("id1");
+  activity_service->SetDaysSinceLastActive("id1", 3);
+  activity_service->SetDaysSinceLastRollCall("id1", 2);
+  activity_service->SetDaysSinceLastRollCall("id2", 3);
+  activity_service->SetDaysSinceLastRollCall("id3", 4);
+  EXPECT_EQ(true, metadata->GetActiveBit("id1"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id2"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id2"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3"));
+  EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2"));
+  EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3"));
+
+  metadata->SetDateLastActive(items, 3384);
+  metadata->SetDateLastRollCall(items, 3383);
+  EXPECT_EQ(false, metadata->GetActiveBit("id1"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id2"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id3"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3"));
+  EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2"));
+  EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3"));
+  EXPECT_EQ(3384, metadata->GetDateLastActive("id1"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("id2"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("id3"));
+  EXPECT_EQ(3383, metadata->GetDateLastRollCall("id1"));
+  EXPECT_EQ(3383, metadata->GetDateLastRollCall("id2"));
+  EXPECT_EQ(3383, metadata->GetDateLastRollCall("id3"));
+
+  metadata->SetDateLastActive(items, 5000);
+  metadata->SetDateLastRollCall(items, 3390);
+  EXPECT_EQ(false, metadata->GetActiveBit("id1"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id2"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id3"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3"));
+  EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2"));
+  EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3"));
+  EXPECT_EQ(3384, metadata->GetDateLastActive("id1"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("id2"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("id3"));
+  EXPECT_EQ(3390, metadata->GetDateLastRollCall("id1"));
+  EXPECT_EQ(3390, metadata->GetDateLastRollCall("id2"));
+  EXPECT_EQ(3390, metadata->GetDateLastRollCall("id3"));
+
+  activity_service->SetActiveBit("id2");
+  metadata->SetDateLastActive(items, 5678);
+  metadata->SetDateLastRollCall(items, 6789);
+  EXPECT_EQ(false, metadata->GetActiveBit("id1"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id2"));
+  EXPECT_EQ(false, metadata->GetActiveBit("id3"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastActive("id1"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id2"));
+  EXPECT_EQ(-1, metadata->GetDaysSinceLastActive("id3"));
+  EXPECT_EQ(2, metadata->GetDaysSinceLastRollCall("id1"));
+  EXPECT_EQ(3, metadata->GetDaysSinceLastRollCall("id2"));
+  EXPECT_EQ(4, metadata->GetDaysSinceLastRollCall("id3"));
+  EXPECT_EQ(3384, metadata->GetDateLastActive("id1"));
+  EXPECT_EQ(5678, metadata->GetDateLastActive("id2"));
+  EXPECT_EQ(-2, metadata->GetDateLastActive("id3"));
+  EXPECT_EQ(6789, metadata->GetDateLastRollCall("id1"));
+  EXPECT_EQ(6789, metadata->GetDateLastRollCall("id2"));
+  EXPECT_EQ(6789, metadata->GetDateLastRollCall("id3"));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/ping_manager.cc b/src/updater_components/update_client/ping_manager.cc
new file mode 100644
index 0000000..3c2a60c
--- /dev/null
+++ b/src/updater_components/update_client/ping_manager.cc
@@ -0,0 +1,129 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/ping_manager.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/update_client/component.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/protocol_definition.h"
+#include "components/update_client/protocol_handler.h"
+#include "components/update_client/protocol_serializer.h"
+#include "components/update_client/request_sender.h"
+#include "components/update_client/utils.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+namespace {
+
+const int kErrorNoEvents = -1;
+const int kErrorNoUrl = -2;
+
+// An instance of this class can send only one ping.
+class PingSender : public base::RefCountedThreadSafe<PingSender> {
+ public:
+  using Callback = PingManager::Callback;
+  explicit PingSender(scoped_refptr<Configurator> config);
+  void SendPing(const Component& component, Callback callback);
+
+ protected:
+  virtual ~PingSender();
+
+ private:
+  friend class base::RefCountedThreadSafe<PingSender>;
+  void SendPingComplete(int error,
+                        const std::string& response,
+                        int retry_after_sec);
+
+  THREAD_CHECKER(thread_checker_);
+
+  const scoped_refptr<Configurator> config_;
+  Callback callback_;
+  std::unique_ptr<RequestSender> request_sender_;
+
+  DISALLOW_COPY_AND_ASSIGN(PingSender);
+};
+
+PingSender::PingSender(scoped_refptr<Configurator> config) : config_(config) {}
+
+PingSender::~PingSender() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void PingSender::SendPing(const Component& component, Callback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (component.events().empty()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), kErrorNoEvents, ""));
+    return;
+  }
+
+  DCHECK(component.crx_component());
+
+  auto urls(config_->PingUrl());
+  if (component.crx_component()->requires_network_encryption)
+    RemoveUnsecureUrls(&urls);
+
+  if (urls.empty()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), kErrorNoUrl, ""));
+    return;
+  }
+
+  callback_ = std::move(callback);
+
+  std::vector<protocol_request::App> apps;
+  apps.push_back(MakeProtocolApp(component.id(),
+                                 component.crx_component()->version,
+                                 component.GetEvents()));
+  request_sender_ = std::make_unique<RequestSender>(config_);
+  request_sender_->Send(
+      urls, {},
+      config_->GetProtocolHandlerFactory()->CreateSerializer()->Serialize(
+          MakeProtocolRequest(
+              component.session_id(), config_->GetProdId(),
+              config_->GetBrowserVersion().GetString(), config_->GetLang(),
+              config_->GetChannel(), config_->GetOSLongName(),
+              config_->GetDownloadPreference(), config_->ExtraRequestParams(),
+              nullptr, std::move(apps))),
+      false, base::BindOnce(&PingSender::SendPingComplete, this));
+}
+
+void PingSender::SendPingComplete(int error,
+                                  const std::string& response,
+                                  int retry_after_sec) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  std::move(callback_).Run(error, response);
+}
+
+}  // namespace
+
+PingManager::PingManager(scoped_refptr<Configurator> config)
+    : config_(config) {}
+
+PingManager::~PingManager() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void PingManager::SendPing(const Component& component, Callback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  auto ping_sender = base::MakeRefCounted<PingSender>(config_);
+  ping_sender->SendPing(component, std::move(callback));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/ping_manager.h b/src/updater_components/update_client/ping_manager.h
new file mode 100644
index 0000000..24040e6
--- /dev/null
+++ b/src/updater_components/update_client/ping_manager.h
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_PING_MANAGER_H_
+#define COMPONENTS_UPDATE_CLIENT_PING_MANAGER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+
+namespace update_client {
+
+class Configurator;
+class Component;
+
+class PingManager : public base::RefCountedThreadSafe<PingManager> {
+ public:
+  // |error| is 0 if the ping was sent successfully, otherwise |error| contains
+  // a value with no particular meaning for the caller.
+  using Callback =
+      base::OnceCallback<void(int error, const std::string& response)>;
+
+  explicit PingManager(scoped_refptr<Configurator> config);
+
+  // Sends a ping for the |item|. |callback| is invoked after the ping is sent
+  // or an error has occured. The ping itself is not persisted and it will
+  // be discarded if it has not been sent for any reason.
+  virtual void SendPing(const Component& component, Callback callback);
+
+ protected:
+  virtual ~PingManager();
+
+ private:
+  friend class base::RefCountedThreadSafe<PingManager>;
+
+  THREAD_CHECKER(thread_checker_);
+  const scoped_refptr<Configurator> config_;
+
+  DISALLOW_COPY_AND_ASSIGN(PingManager);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PING_MANAGER_H_
diff --git a/src/updater_components/update_client/ping_manager_unittest.cc b/src/updater_components/update_client/ping_manager_unittest.cc
new file mode 100644
index 0000000..0277b9b
--- /dev/null
+++ b/src/updater_components/update_client/ping_manager_unittest.cc
@@ -0,0 +1,442 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/ping_manager.h"
+
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/version.h"
+#include "components/update_client/component.h"
+#include "components/update_client/net/url_loader_post_interceptor.h"
+#include "components/update_client/protocol_definition.h"
+#include "components/update_client/protocol_serializer.h"
+#include "components/update_client/test_configurator.h"
+#include "components/update_client/update_engine.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/re2/src/re2/re2.h"
+
+using std::string;
+
+namespace update_client {
+
+class PingManagerTest : public testing::Test,
+                        public testing::WithParamInterface<bool> {
+ public:
+  PingManagerTest();
+  ~PingManagerTest() override {}
+
+  PingManager::Callback MakePingCallback();
+  scoped_refptr<UpdateContext> MakeMockUpdateContext() const;
+
+  // Overrides from testing::Test.
+  void SetUp() override;
+  void TearDown() override;
+
+  void PingSentCallback(int error, const std::string& response);
+
+ protected:
+  void Quit();
+  void RunThreads();
+
+  scoped_refptr<TestConfigurator> config_;
+  scoped_refptr<PingManager> ping_manager_;
+
+  int error_ = -1;
+  std::string response_;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  base::OnceClosure quit_closure_;
+};
+
+PingManagerTest::PingManagerTest()
+    : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {
+  config_ = base::MakeRefCounted<TestConfigurator>();
+}
+
+void PingManagerTest::SetUp() {
+  ping_manager_ = base::MakeRefCounted<PingManager>(config_);
+}
+
+void PingManagerTest::TearDown() {
+  // Run the threads until they are idle to allow the clean up
+  // of the network interceptors on the IO thread.
+  task_environment_.RunUntilIdle();
+  ping_manager_ = nullptr;
+}
+
+void PingManagerTest::RunThreads() {
+  base::RunLoop runloop;
+  quit_closure_ = runloop.QuitClosure();
+  runloop.Run();
+}
+
+void PingManagerTest::Quit() {
+  if (!quit_closure_.is_null())
+    std::move(quit_closure_).Run();
+}
+
+PingManager::Callback PingManagerTest::MakePingCallback() {
+  return base::BindOnce(&PingManagerTest::PingSentCallback,
+                        base::Unretained(this));
+}
+
+void PingManagerTest::PingSentCallback(int error, const std::string& response) {
+  error_ = error;
+  response_ = response;
+  Quit();
+}
+
+scoped_refptr<UpdateContext> PingManagerTest::MakeMockUpdateContext() const {
+  return base::MakeRefCounted<UpdateContext>(
+      config_, false, std::vector<std::string>(),
+      UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
+      UpdateEngine::Callback(), nullptr);
+}
+
+// This test is parameterized for using JSON or XML serialization. |true| means
+// JSON serialization is used.
+INSTANTIATE_TEST_SUITE_P(Parameterized, PingManagerTest, testing::Bool());
+
+TEST_P(PingManagerTest, SendPing) {
+  auto interceptor = std::make_unique<URLLoaderPostInterceptor>(
+      config_->test_url_loader_factory());
+  EXPECT_TRUE(interceptor);
+
+  const auto update_context = MakeMockUpdateContext();
+  {
+    // Test eventresult="1" is sent for successful updates.
+    Component component(*update_context, "abc");
+    component.crx_component_ = CrxComponent();
+    component.crx_component_->version = base::Version("1.0");
+    component.state_ = std::make_unique<Component::StateUpdated>(&component);
+    component.previous_version_ = base::Version("1.0");
+    component.next_version_ = base::Version("2.0");
+    component.AppendEvent(component.MakeEventUpdateComplete());
+
+    EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>()));
+    ping_manager_->SendPing(component, MakePingCallback());
+    RunThreads();
+
+    EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString();
+    const auto msg = interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(msg);
+      ASSERT_TRUE(root);
+      const auto* request = root->FindKey("request");
+      ASSERT_TRUE(request);
+      EXPECT_TRUE(request->FindKey("@os"));
+      EXPECT_EQ("fake_prodid", request->FindKey("@updater")->GetString());
+      EXPECT_EQ("crx2,crx3", request->FindKey("acceptformat")->GetString());
+      EXPECT_TRUE(request->FindKey("arch"));
+      EXPECT_EQ("cr", request->FindKey("dedup")->GetString());
+      EXPECT_LT(0, request->FindPath({"hw", "physmemory"})->GetInt());
+      EXPECT_EQ("fake_lang", request->FindKey("lang")->GetString());
+      EXPECT_TRUE(request->FindKey("nacl_arch"));
+      EXPECT_EQ("fake_channel_string",
+                request->FindKey("prodchannel")->GetString());
+      EXPECT_EQ("30.0", request->FindKey("prodversion")->GetString());
+      EXPECT_EQ("3.1", request->FindKey("protocol")->GetString());
+      EXPECT_TRUE(request->FindKey("requestid"));
+      EXPECT_TRUE(request->FindKey("sessionid"));
+      EXPECT_EQ("fake_channel_string",
+                request->FindKey("updaterchannel")->GetString());
+      EXPECT_EQ("30.0", request->FindKey("updaterversion")->GetString());
+
+      EXPECT_TRUE(request->FindPath({"os", "arch"})->is_string());
+      EXPECT_EQ("Fake Operating System",
+                request->FindPath({"os", "platform"})->GetString());
+      EXPECT_TRUE(request->FindPath({"os", "version"})->is_string());
+
+      const auto& app = request->FindKey("app")->GetList()[0];
+      EXPECT_EQ("abc", app.FindKey("appid")->GetString());
+      EXPECT_EQ("1.0", app.FindKey("version")->GetString());
+      const auto& event = app.FindKey("event")->GetList()[0];
+      EXPECT_EQ(1, event.FindKey("eventresult")->GetInt());
+      EXPECT_EQ(3, event.FindKey("eventtype")->GetInt());
+      EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString());
+      EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+
+    // Check the ping request does not carry the specific extra request headers.
+    const auto headers = std::get<1>(interceptor->GetRequests()[0]);
+    EXPECT_FALSE(headers.HasHeader("X-Goog-Update-Interactivity"));
+    EXPECT_FALSE(headers.HasHeader("X-Goog-Update-Updater"));
+    EXPECT_FALSE(headers.HasHeader("X-Goog-Update-AppId"));
+    interceptor->Reset();
+  }
+
+  {
+    // Test eventresult="0" is sent for failed updates.
+    Component component(*update_context, "abc");
+    component.crx_component_ = CrxComponent();
+    component.crx_component_->version = base::Version("1.0");
+    component.state_ =
+        std::make_unique<Component::StateUpdateError>(&component);
+    component.previous_version_ = base::Version("1.0");
+    component.next_version_ = base::Version("2.0");
+    component.AppendEvent(component.MakeEventUpdateComplete());
+
+    EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>()));
+    ping_manager_->SendPing(component, MakePingCallback());
+    RunThreads();
+
+    EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString();
+    const auto msg = interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(msg);
+      ASSERT_TRUE(root);
+      const auto* request = root->FindKey("request");
+      const auto& app = request->FindKey("app")->GetList()[0];
+      EXPECT_EQ("abc", app.FindKey("appid")->GetString());
+      EXPECT_EQ("1.0", app.FindKey("version")->GetString());
+      const auto& event = app.FindKey("event")->GetList()[0];
+      EXPECT_EQ(0, event.FindKey("eventresult")->GetInt());
+      EXPECT_EQ(3, event.FindKey("eventtype")->GetInt());
+      EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString());
+      EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+    interceptor->Reset();
+  }
+
+  {
+    // Test the error values and the fingerprints.
+    Component component(*update_context, "abc");
+    component.crx_component_ = CrxComponent();
+    component.crx_component_->version = base::Version("1.0");
+    component.state_ =
+        std::make_unique<Component::StateUpdateError>(&component);
+    component.previous_version_ = base::Version("1.0");
+    component.next_version_ = base::Version("2.0");
+    component.previous_fp_ = "prev fp";
+    component.next_fp_ = "next fp";
+    component.error_category_ = ErrorCategory::kDownload;
+    component.error_code_ = 2;
+    component.extra_code1_ = -1;
+    component.diff_error_category_ = ErrorCategory::kService;
+    component.diff_error_code_ = 20;
+    component.diff_extra_code1_ = -10;
+    component.crx_diffurls_.push_back(GURL("http://host/path"));
+    component.AppendEvent(component.MakeEventUpdateComplete());
+
+    EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>()));
+    ping_manager_->SendPing(component, MakePingCallback());
+    RunThreads();
+
+    EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString();
+    const auto msg = interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(msg);
+      ASSERT_TRUE(root);
+      const auto* request = root->FindKey("request");
+      const auto& app = request->FindKey("app")->GetList()[0];
+      EXPECT_EQ("abc", app.FindKey("appid")->GetString());
+      EXPECT_EQ("1.0", app.FindKey("version")->GetString());
+      const auto& event = app.FindKey("event")->GetList()[0];
+      EXPECT_EQ(0, event.FindKey("eventresult")->GetInt());
+      EXPECT_EQ(3, event.FindKey("eventtype")->GetInt());
+      EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString());
+      EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+      EXPECT_EQ(4, event.FindKey("differrorcat")->GetInt());
+      EXPECT_EQ(20, event.FindKey("differrorcode")->GetInt());
+      EXPECT_EQ(-10, event.FindKey("diffextracode1")->GetInt());
+      EXPECT_EQ(0, event.FindKey("diffresult")->GetInt());
+      EXPECT_EQ(1, event.FindKey("errorcat")->GetInt());
+      EXPECT_EQ(2, event.FindKey("errorcode")->GetInt());
+      EXPECT_EQ(-1, event.FindKey("extracode1")->GetInt());
+      EXPECT_EQ("next fp", event.FindKey("nextfp")->GetString());
+      EXPECT_EQ("prev fp", event.FindKey("previousfp")->GetString());
+    interceptor->Reset();
+  }
+
+  {
+    // Test an invalid |next_version| is not serialized.
+    Component component(*update_context, "abc");
+    component.crx_component_ = CrxComponent();
+    component.crx_component_->version = base::Version("1.0");
+    component.state_ =
+        std::make_unique<Component::StateUpdateError>(&component);
+    component.previous_version_ = base::Version("1.0");
+
+    component.AppendEvent(component.MakeEventUpdateComplete());
+
+    EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>()));
+    ping_manager_->SendPing(component, MakePingCallback());
+    RunThreads();
+
+    EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString();
+    const auto msg = interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(msg);
+      ASSERT_TRUE(root);
+      const auto* request = root->FindKey("request");
+      const auto& app = request->FindKey("app")->GetList()[0];
+      EXPECT_EQ("abc", app.FindKey("appid")->GetString());
+      EXPECT_EQ("1.0", app.FindKey("version")->GetString());
+      const auto& event = app.FindKey("event")->GetList()[0];
+      EXPECT_EQ(0, event.FindKey("eventresult")->GetInt());
+      EXPECT_EQ(3, event.FindKey("eventtype")->GetInt());
+      EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+    interceptor->Reset();
+  }
+
+  {
+    // Test a valid |previouversion| and |next_version| = base::Version("0")
+    // are serialized correctly under <event...> for uninstall.
+    Component component(*update_context, "abc");
+    component.crx_component_ = CrxComponent();
+    component.Uninstall(base::Version("1.2.3.4"), 0);
+    component.AppendEvent(component.MakeEventUninstalled());
+
+    EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>()));
+    ping_manager_->SendPing(component, MakePingCallback());
+    RunThreads();
+
+    EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString();
+    const auto msg = interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(msg);
+      ASSERT_TRUE(root);
+      const auto* request = root->FindKey("request");
+      const auto& app = request->FindKey("app")->GetList()[0];
+      EXPECT_EQ("abc", app.FindKey("appid")->GetString());
+      EXPECT_EQ("1.2.3.4", app.FindKey("version")->GetString());
+      const auto& event = app.FindKey("event")->GetList()[0];
+      EXPECT_EQ(1, event.FindKey("eventresult")->GetInt());
+      EXPECT_EQ(4, event.FindKey("eventtype")->GetInt());
+      EXPECT_EQ("1.2.3.4", event.FindKey("previousversion")->GetString());
+      EXPECT_EQ("0", event.FindKey("nextversion")->GetString());
+    interceptor->Reset();
+  }
+
+  {
+    // Test the download metrics.
+    Component component(*update_context, "abc");
+    component.crx_component_ = CrxComponent();
+    component.crx_component_->version = base::Version("1.0");
+    component.state_ = std::make_unique<Component::StateUpdated>(&component);
+    component.previous_version_ = base::Version("1.0");
+    component.next_version_ = base::Version("2.0");
+    component.AppendEvent(component.MakeEventUpdateComplete());
+
+    CrxDownloader::DownloadMetrics download_metrics;
+    download_metrics.url = GURL("http://host1/path1");
+    download_metrics.downloader = CrxDownloader::DownloadMetrics::kUrlFetcher;
+    download_metrics.error = -1;
+    download_metrics.downloaded_bytes = 123;
+    download_metrics.total_bytes = 456;
+    download_metrics.download_time_ms = 987;
+    component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics));
+
+    download_metrics = CrxDownloader::DownloadMetrics();
+    download_metrics.url = GURL("http://host2/path2");
+    download_metrics.downloader = CrxDownloader::DownloadMetrics::kBits;
+    download_metrics.error = 0;
+    download_metrics.downloaded_bytes = 1230;
+    download_metrics.total_bytes = 4560;
+    download_metrics.download_time_ms = 9870;
+    component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics));
+
+    download_metrics = CrxDownloader::DownloadMetrics();
+    download_metrics.url = GURL("http://host3/path3");
+    download_metrics.downloader = CrxDownloader::DownloadMetrics::kBits;
+    download_metrics.error = 0;
+    download_metrics.downloaded_bytes = kProtocolMaxInt;
+    download_metrics.total_bytes = kProtocolMaxInt - 1;
+    download_metrics.download_time_ms = kProtocolMaxInt - 2;
+    component.AppendEvent(component.MakeEventDownloadMetrics(download_metrics));
+
+    EXPECT_TRUE(interceptor->ExpectRequest(std::make_unique<AnyMatch>()));
+    ping_manager_->SendPing(component, MakePingCallback());
+    RunThreads();
+
+    EXPECT_EQ(1, interceptor->GetCount()) << interceptor->GetRequestsAsString();
+    const auto msg = interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(msg);
+      ASSERT_TRUE(root);
+      const auto* request = root->FindKey("request");
+      const auto& app = request->FindKey("app")->GetList()[0];
+      EXPECT_EQ("abc", app.FindKey("appid")->GetString());
+      EXPECT_EQ("1.0", app.FindKey("version")->GetString());
+      EXPECT_EQ(4u, app.FindKey("event")->GetList().size());
+      {
+        const auto& event = app.FindKey("event")->GetList()[0];
+        EXPECT_EQ(1, event.FindKey("eventresult")->GetInt());
+        EXPECT_EQ(3, event.FindKey("eventtype")->GetInt());
+        EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString());
+        EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+      }
+      {
+        const auto& event = app.FindKey("event")->GetList()[1];
+        EXPECT_EQ(0, event.FindKey("eventresult")->GetInt());
+        EXPECT_EQ(14, event.FindKey("eventtype")->GetInt());
+        EXPECT_EQ(987, event.FindKey("download_time_ms")->GetDouble());
+        EXPECT_EQ(123, event.FindKey("downloaded")->GetDouble());
+        EXPECT_EQ("direct", event.FindKey("downloader")->GetString());
+        EXPECT_EQ(-1, event.FindKey("errorcode")->GetInt());
+        EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString());
+        EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+        EXPECT_EQ(456, event.FindKey("total")->GetDouble());
+        EXPECT_EQ("http://host1/path1", event.FindKey("url")->GetString());
+      }
+      {
+        const auto& event = app.FindKey("event")->GetList()[2];
+        EXPECT_EQ(1, event.FindKey("eventresult")->GetInt());
+        EXPECT_EQ(14, event.FindKey("eventtype")->GetInt());
+        EXPECT_EQ(9870, event.FindKey("download_time_ms")->GetDouble());
+        EXPECT_EQ(1230, event.FindKey("downloaded")->GetDouble());
+        EXPECT_EQ("bits", event.FindKey("downloader")->GetString());
+        EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString());
+        EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+        EXPECT_EQ(4560, event.FindKey("total")->GetDouble());
+        EXPECT_EQ("http://host2/path2", event.FindKey("url")->GetString());
+      }
+      {
+        const auto& event = app.FindKey("event")->GetList()[3];
+        EXPECT_EQ(1, event.FindKey("eventresult")->GetInt());
+        EXPECT_EQ(14, event.FindKey("eventtype")->GetInt());
+        EXPECT_EQ(9007199254740990,
+                  event.FindKey("download_time_ms")->GetDouble());
+        EXPECT_EQ(9007199254740992, event.FindKey("downloaded")->GetDouble());
+        EXPECT_EQ("bits", event.FindKey("downloader")->GetString());
+        EXPECT_EQ("2.0", event.FindKey("nextversion")->GetString());
+        EXPECT_EQ("1.0", event.FindKey("previousversion")->GetString());
+        EXPECT_EQ(9007199254740991, event.FindKey("total")->GetDouble());
+        EXPECT_EQ("http://host3/path3", event.FindKey("url")->GetString());
+      }
+    interceptor->Reset();
+  }
+}
+
+// Tests that sending the ping fails when the component requires encryption but
+// the ping URL is unsecure.
+TEST_P(PingManagerTest, RequiresEncryption) {
+  config_->SetPingUrl(GURL("http:\\foo\bar"));
+
+  const auto update_context = MakeMockUpdateContext();
+
+  Component component(*update_context, "abc");
+  component.crx_component_ = CrxComponent();
+  component.crx_component_->version = base::Version("1.0");
+
+  // The default value for |requires_network_encryption| is true.
+  EXPECT_TRUE(component.crx_component_->requires_network_encryption);
+
+  component.state_ = std::make_unique<Component::StateUpdated>(&component);
+  component.previous_version_ = base::Version("1.0");
+  component.next_version_ = base::Version("2.0");
+  component.AppendEvent(component.MakeEventUpdateComplete());
+
+  ping_manager_->SendPing(component, MakePingCallback());
+  RunThreads();
+
+  EXPECT_EQ(-2, error_);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_definition.cc b/src/updater_components/update_client/protocol_definition.cc
new file mode 100644
index 0000000..2dd3cc6
--- /dev/null
+++ b/src/updater_components/update_client/protocol_definition.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2018 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 "components/update_client/protocol_definition.h"
+
+#include "base/values.h"
+
+namespace update_client {
+
+namespace protocol_request {
+
+OS::OS() = default;
+OS::OS(OS&&) = default;
+OS::~OS() = default;
+
+Updater::Updater() = default;
+Updater::Updater(const Updater&) = default;
+Updater::~Updater() = default;
+
+UpdateCheck::UpdateCheck() = default;
+UpdateCheck::~UpdateCheck() = default;
+
+Ping::Ping() = default;
+Ping::Ping(const Ping&) = default;
+Ping::~Ping() = default;
+
+App::App() = default;
+App::App(App&&) = default;
+App::~App() = default;
+
+Request::Request() = default;
+Request::Request(Request&&) = default;
+Request::~Request() = default;
+
+}  // namespace protocol_request
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_definition.h b/src/updater_components/update_client/protocol_definition.h
new file mode 100644
index 0000000..aaacc76
--- /dev/null
+++ b/src/updater_components/update_client/protocol_definition.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2018 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 COMPONENTS_UPDATE_CLIENT_PROTOCOL_DEFINITION_H_
+#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_DEFINITION_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/values.h"
+#include "build/build_config.h"
+
+namespace update_client {
+
+// The protocol versions so far are:
+// * Version 3.1: it changes how the run actions are serialized.
+// * Version 3.0: it is the version implemented by the desktop updaters.
+constexpr char kProtocolVersion[] = "3.1";
+
+// Due to implementation constraints of the JSON parser and serializer,
+// precision of integer numbers greater than 2^53 is lost.
+constexpr int64_t kProtocolMaxInt = 1LL << 53;
+
+namespace protocol_request {
+
+struct HW {
+  uint32_t physmemory = 0;  // Physical memory rounded down to the closest GB.
+};
+
+struct OS {
+  OS();
+  OS(OS&&);
+  ~OS();
+
+  std::string platform;
+  std::string version;
+  std::string service_pack;
+  std::string arch;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OS);
+};
+
+struct Updater {
+  Updater();
+  Updater(const Updater&);
+  ~Updater();
+
+  std::string name;
+  std::string version;
+  bool is_machine = false;
+  bool autoupdate_check_enabled = false;
+  base::Optional<int> last_started;
+  base::Optional<int> last_checked;
+  int update_policy = 0;
+};
+
+struct UpdateCheck {
+  UpdateCheck();
+  ~UpdateCheck();
+
+  bool is_update_disabled = false;
+};
+
+// didrun element. The element is named "ping" for legacy reasons.
+struct Ping {
+  Ping();
+  Ping(const Ping&);
+  ~Ping();
+
+  // Preferred user count metrics ("ad" and "rd").
+  base::Optional<int> date_last_active;
+  base::Optional<int> date_last_roll_call;
+
+  // Legacy user count metrics ("a" and "r").
+  base::Optional<int> days_since_last_active_ping;
+  int days_since_last_roll_call = 0;
+
+  std::string ping_freshness;
+};
+
+struct App {
+  App();
+  App(App&&);
+  ~App();
+
+  std::string app_id;
+  std::string version;
+  base::flat_map<std::string, std::string> installer_attributes;
+  std::string lang;
+  std::string brand_code;
+  std::string install_source;
+  std::string install_location;
+  std::string fingerprint;
+
+  std::string cohort;       // Opaque string.
+  std::string cohort_hint;  // Server may use to move the app to a new cohort.
+  std::string cohort_name;  // Human-readable interpretation of the cohort.
+
+  base::Optional<bool> enabled;
+  base::Optional<std::vector<int>> disabled_reasons;
+
+  // Optional update check.
+  base::Optional<UpdateCheck> update_check;
+
+  // Optional 'did run' ping.
+  base::Optional<Ping> ping;
+
+  // Progress/result pings.
+  base::Optional<std::vector<base::Value>> events;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(App);
+};
+
+struct Request {
+  Request();
+  Request(Request&&);
+  ~Request();
+
+  std::string protocol_version;
+
+  // Unique identifier for this session, used to correlate multiple requests
+  // associated with a single update operation.
+  std::string session_id;
+
+  // Unique identifier for this request, used to associate the same request
+  // received multiple times on the server.
+  std::string request_id;
+
+  std::string updatername;
+  std::string updaterversion;
+  std::string prodversion;
+  std::string lang;
+  std::string updaterchannel;
+  std::string prodchannel;
+  std::string operating_system;
+  std::string arch;
+  std::string nacl_arch;
+
+#if defined(OS_WIN)
+  bool is_wow64 = false;
+#endif
+
+  // Provides a hint for what download urls should be returned by server.
+  // This data member is controlled by group policy settings.
+  // The only group policy value supported so far is |cacheable|.
+  std::string dlpref;
+
+  // True if this machine is part of a managed enterprise domain.
+  base::Optional<bool> domain_joined;
+
+  base::flat_map<std::string, std::string> additional_attributes;
+
+  HW hw;
+
+  OS os;
+
+  base::Optional<Updater> updater;
+
+  std::vector<App> apps;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+}  // namespace protocol_request
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PROTOCOL_DEFINITION_H_
diff --git a/src/updater_components/update_client/protocol_handler.cc b/src/updater_components/update_client/protocol_handler.cc
new file mode 100644
index 0000000..aa547b4
--- /dev/null
+++ b/src/updater_components/update_client/protocol_handler.cc
@@ -0,0 +1,21 @@
+// Copyright 2018 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 "components/update_client/protocol_handler.h"
+#include "components/update_client/protocol_parser_json.h"
+#include "components/update_client/protocol_serializer_json.h"
+
+namespace update_client {
+
+std::unique_ptr<ProtocolParser> ProtocolHandlerFactoryJSON::CreateParser()
+    const {
+  return std::make_unique<ProtocolParserJSON>();
+}
+
+std::unique_ptr<ProtocolSerializer>
+ProtocolHandlerFactoryJSON::CreateSerializer() const {
+  return std::make_unique<ProtocolSerializerJSON>();
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_handler.h b/src/updater_components/update_client/protocol_handler.h
new file mode 100644
index 0000000..57c8cfd
--- /dev/null
+++ b/src/updater_components/update_client/protocol_handler.h
@@ -0,0 +1,39 @@
+// Copyright 2018 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 COMPONENTS_UPDATE_CLIENT_PROTOCOL_HANDLER_H_
+#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+namespace update_client {
+
+class ProtocolParser;
+class ProtocolSerializer;
+
+class ProtocolHandlerFactory {
+ public:
+  virtual ~ProtocolHandlerFactory() = default;
+  virtual std::unique_ptr<ProtocolParser> CreateParser() const = 0;
+  virtual std::unique_ptr<ProtocolSerializer> CreateSerializer() const = 0;
+
+ protected:
+  ProtocolHandlerFactory() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerFactory);
+};
+
+class ProtocolHandlerFactoryJSON final : public ProtocolHandlerFactory {
+ public:
+  // Overrides for ProtocolHandlerFactory.
+  std::unique_ptr<ProtocolParser> CreateParser() const override;
+  std::unique_ptr<ProtocolSerializer> CreateSerializer() const override;
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PROTOCOL_HANDLER_H_
diff --git a/src/updater_components/update_client/protocol_parser.cc b/src/updater_components/update_client/protocol_parser.cc
new file mode 100644
index 0000000..7f280c5
--- /dev/null
+++ b/src/updater_components/update_client/protocol_parser.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/protocol_parser.h"
+#include "base/strings/stringprintf.h"
+
+namespace update_client {
+
+const char ProtocolParser::Result::kCohort[] = "cohort";
+const char ProtocolParser::Result::kCohortHint[] = "cohorthint";
+const char ProtocolParser::Result::kCohortName[] = "cohortname";
+
+ProtocolParser::ProtocolParser() = default;
+ProtocolParser::~ProtocolParser() = default;
+
+ProtocolParser::Results::Results() = default;
+ProtocolParser::Results::Results(const Results& other) = default;
+ProtocolParser::Results::~Results() = default;
+
+ProtocolParser::Result::Result() = default;
+ProtocolParser::Result::Result(const Result& other) = default;
+ProtocolParser::Result::~Result() = default;
+
+ProtocolParser::Result::Manifest::Manifest() = default;
+ProtocolParser::Result::Manifest::Manifest(const Manifest& other) = default;
+ProtocolParser::Result::Manifest::~Manifest() = default;
+
+ProtocolParser::Result::Manifest::Package::Package() = default;
+ProtocolParser::Result::Manifest::Package::Package(const Package& other) =
+    default;
+ProtocolParser::Result::Manifest::Package::~Package() = default;
+
+void ProtocolParser::ParseError(const char* details, ...) {
+  va_list args;
+  va_start(args, details);
+
+  if (!errors_.empty()) {
+    errors_ += "\r\n";
+  }
+
+  base::StringAppendV(&errors_, details, args);
+  va_end(args);
+}
+
+bool ProtocolParser::Parse(const std::string& response) {
+  results_.daystart_elapsed_seconds = kNoDaystart;
+  results_.daystart_elapsed_days = kNoDaystart;
+  results_.list.clear();
+  errors_.clear();
+
+  return DoParse(response, &results_);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_parser.h b/src/updater_components/update_client/protocol_parser.h
new file mode 100644
index 0000000..471fd6d
--- /dev/null
+++ b/src/updater_components/update_client/protocol_parser.h
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_H_
+#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_H_
+
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+class ProtocolParser {
+ public:
+  // The result of parsing one |app| entity in an update check response.
+  struct Result {
+    struct Manifest {
+      struct Package {
+        Package();
+        Package(const Package& other);
+        ~Package();
+
+        // |fingerprint| is optional. It identifies the package, preferably
+        // with a modified sha256 hash of the package in hex format.
+        std::string fingerprint;
+
+        // Attributes for the full update.
+        std::string name;
+        std::string hash_sha256;
+        int64_t size = 0;
+
+        // Attributes for the differential update.
+        std::string namediff;
+        std::string hashdiff_sha256;
+        int64_t sizediff = 0;
+      };
+
+      Manifest();
+      Manifest(const Manifest& other);
+      ~Manifest();
+
+      std::string version;
+      std::string browser_min_version;
+      std::vector<Package> packages;
+    };
+
+    Result();
+    Result(const Result& other);
+    ~Result();
+
+    std::string extension_id;
+
+    // The updatecheck response status.
+    std::string status;
+
+    // The list of fallback urls, for full and diff updates respectively.
+    // These urls are base urls; they don't include the filename.
+    std::vector<GURL> crx_urls;
+    std::vector<GURL> crx_diffurls;
+
+    Manifest manifest;
+
+    // The server has instructed the client to set its [key] to [value] for each
+    // key-value pair in this string.
+    std::map<std::string, std::string> cohort_attrs;
+
+    // The following are the only allowed keys in |cohort_attrs|.
+    static const char kCohort[];
+    static const char kCohortHint[];
+    static const char kCohortName[];
+
+    // Contains the run action returned by the server as part of an update
+    // check response.
+    std::string action_run;
+  };
+
+  static const int kNoDaystart = -1;
+  struct Results {
+    Results();
+    Results(const Results& other);
+    ~Results();
+
+    // This will be >= 0, or kNoDaystart if the <daystart> tag was not present.
+    int daystart_elapsed_seconds = kNoDaystart;
+
+    // This will be >= 0, or kNoDaystart if the <daystart> tag was not present.
+    int daystart_elapsed_days = kNoDaystart;
+    std::vector<Result> list;
+  };
+
+  static std::unique_ptr<ProtocolParser> Create();
+
+  virtual ~ProtocolParser();
+
+  // Parses an update response string into Result data. Returns a bool
+  // indicating success or failure. On success, the results are available by
+  // calling results(). In case of success, only results corresponding to
+  // the update check status |ok| or |noupdate| are included.
+  // The details for any failures are available by calling errors().
+  bool Parse(const std::string& response);
+
+  const Results& results() const { return results_; }
+  const std::string& errors() const { return errors_; }
+
+ protected:
+  ProtocolParser();
+
+  // Appends parse error details to |errors_| string.
+  void ParseError(const char* details, ...);
+
+ private:
+  virtual bool DoParse(const std::string& response, Results* results) = 0;
+
+  Results results_;
+  std::string errors_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtocolParser);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_H_
diff --git a/src/updater_components/update_client/protocol_parser_json.cc b/src/updater_components/update_client/protocol_parser_json.cc
new file mode 100644
index 0000000..eb022e4
--- /dev/null
+++ b/src/updater_components/update_client/protocol_parser_json.cc
@@ -0,0 +1,337 @@
+// Copyright 2018 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 "components/update_client/protocol_parser_json.h"
+
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "components/update_client/protocol_definition.h"
+
+namespace update_client {
+
+namespace {
+
+bool ParseManifest(const base::Value& manifest_node,
+                   ProtocolParser::Result* result,
+                   std::string* error) {
+  if (!manifest_node.is_dict()) {
+    *error = "'manifest' is not a dictionary.";
+  }
+  const auto* version = manifest_node.FindKey("version");
+  if (!version || !version->is_string()) {
+    *error = "Missing version for manifest.";
+    return false;
+  }
+
+  result->manifest.version = version->GetString();
+  if (!base::Version(result->manifest.version).IsValid()) {
+    *error =
+        base::StrCat({"Invalid version: '", result->manifest.version, "'."});
+    return false;
+  }
+
+  // Get the optional minimum browser version.
+  const auto* browser_min_version = manifest_node.FindKey("prodversionmin");
+  if (browser_min_version && browser_min_version->is_string()) {
+    result->manifest.browser_min_version = browser_min_version->GetString();
+    if (!base::Version(result->manifest.browser_min_version).IsValid()) {
+      *error = base::StrCat({"Invalid prodversionmin: '",
+                             result->manifest.browser_min_version, "'."});
+      return false;
+    }
+  }
+
+  const auto* packages_node = manifest_node.FindKey("packages");
+  if (!packages_node || !packages_node->is_dict()) {
+    *error = "Missing packages in manifest or 'packages' is not a dictionary.";
+    return false;
+  }
+  const auto* package_node = packages_node->FindKey("package");
+  if (!package_node || !package_node->is_list()) {
+    *error = "Missing package in packages.";
+    return false;
+  }
+
+  for (const auto& package : package_node->GetList()) {
+    if (!package.is_dict()) {
+      *error = "'package' is not a dictionary.";
+      return false;
+    }
+    ProtocolParser::Result::Manifest::Package p;
+    const auto* name = package.FindKey("name");
+    if (!name || !name->is_string()) {
+      *error = "Missing name for package.";
+      return false;
+    }
+    p.name = name->GetString();
+
+    const auto* namediff = package.FindKey("namediff");
+    if (namediff && namediff->is_string())
+      p.namediff = namediff->GetString();
+
+    const auto* fingerprint = package.FindKey("fp");
+    if (fingerprint && fingerprint->is_string())
+      p.fingerprint = fingerprint->GetString();
+
+    const auto* hash_sha256 = package.FindKey("hash_sha256");
+    if (hash_sha256 && hash_sha256->is_string())
+      p.hash_sha256 = hash_sha256->GetString();
+
+    const auto* size = package.FindKey("size");
+    if (size && (size->is_int() || size->is_double())) {
+      const auto val = size->GetDouble();
+      if (0 <= val && val < kProtocolMaxInt)
+        p.size = size->GetDouble();
+    }
+
+    const auto* hashdiff_sha256 = package.FindKey("hashdiff_sha256");
+    if (hashdiff_sha256 && hashdiff_sha256->is_string())
+      p.hashdiff_sha256 = hashdiff_sha256->GetString();
+
+    const auto* sizediff = package.FindKey("sizediff");
+    if (sizediff && (sizediff->is_int() || sizediff->is_double())) {
+      const auto val = sizediff->GetDouble();
+      if (0 <= val && val < kProtocolMaxInt)
+        p.sizediff = sizediff->GetDouble();
+    }
+
+    result->manifest.packages.push_back(std::move(p));
+  }
+
+  return true;
+}
+
+void ParseActions(const base::Value& actions_node,
+                  ProtocolParser::Result* result) {
+  if (!actions_node.is_dict())
+    return;
+
+  const auto* action_node = actions_node.FindKey("action");
+  if (!action_node || !action_node->is_list())
+    return;
+
+  const auto& action_list = action_node->GetList();
+  if (action_list.empty() || !action_list[0].is_dict())
+    return;
+
+  const auto* run = action_list[0].FindKey("run");
+  if (run && run->is_string())
+    result->action_run = run->GetString();
+}
+
+bool ParseUrls(const base::Value& urls_node,
+               ProtocolParser::Result* result,
+               std::string* error) {
+  if (!urls_node.is_dict()) {
+    *error = "'urls' is not a dictionary.";
+    return false;
+  }
+  const auto* url_node = urls_node.FindKey("url");
+  if (!url_node || !url_node->is_list()) {
+    *error = "Missing url on urls.";
+    return false;
+  }
+
+  for (const auto& url : url_node->GetList()) {
+    if (!url.is_dict())
+      continue;
+    const auto* codebase = url.FindKey("codebase");
+    if (codebase && codebase->is_string()) {
+      GURL crx_url(codebase->GetString());
+      if (crx_url.is_valid())
+        result->crx_urls.push_back(std::move(crx_url));
+    }
+    const auto* codebasediff = url.FindKey("codebasediff");
+    if (codebasediff && codebasediff->is_string()) {
+      GURL crx_diffurl(codebasediff->GetString());
+      if (crx_diffurl.is_valid())
+        result->crx_diffurls.push_back(std::move(crx_diffurl));
+    }
+  }
+
+  // Expect at least one url for full update.
+  if (result->crx_urls.empty()) {
+    *error = "Missing valid url for full update.";
+    return false;
+  }
+
+  return true;
+}
+
+bool ParseUpdateCheck(const base::Value& updatecheck_node,
+                      ProtocolParser::Result* result,
+                      std::string* error) {
+  if (!updatecheck_node.is_dict()) {
+    *error = "'updatecheck' is not a dictionary.";
+    return false;
+  }
+  const auto* status = updatecheck_node.FindKey("status");
+  if (!status || !status->is_string()) {
+    *error = "Missing status on updatecheck node";
+    return false;
+  }
+
+  result->status = status->GetString();
+  if (result->status == "noupdate") {
+    const auto* actions_node = updatecheck_node.FindKey("actions");
+    if (actions_node)
+      ParseActions(*actions_node, result);
+    return true;
+  }
+
+  if (result->status == "ok") {
+    const auto* actions_node = updatecheck_node.FindKey("actions");
+    if (actions_node)
+      ParseActions(*actions_node, result);
+
+    const auto* urls_node = updatecheck_node.FindKey("urls");
+    if (!urls_node) {
+      *error = "Missing urls on updatecheck.";
+      return false;
+    }
+
+    if (!ParseUrls(*urls_node, result, error))
+      return false;
+
+    const auto* manifest_node = updatecheck_node.FindKey("manifest");
+    if (!manifest_node) {
+      *error = "Missing manifest on updatecheck.";
+      return false;
+    }
+    return ParseManifest(*manifest_node, result, error);
+  }
+
+  // Return the |updatecheck| element status as a parsing error.
+  *error = result->status;
+  return false;
+}
+
+bool ParseApp(const base::Value& app_node,
+              ProtocolParser::Result* result,
+              std::string* error) {
+  if (!app_node.is_dict()) {
+    *error = "'app' is not a dictionary.";
+    return false;
+  }
+  for (const auto* cohort_key :
+       {ProtocolParser::Result::kCohort, ProtocolParser::Result::kCohortHint,
+        ProtocolParser::Result::kCohortName}) {
+    const auto* cohort_value = app_node.FindKey(cohort_key);
+    if (cohort_value && cohort_value->is_string())
+      result->cohort_attrs[cohort_key] = cohort_value->GetString();
+  }
+  const auto* appid = app_node.FindKey("appid");
+  if (appid && appid->is_string())
+    result->extension_id = appid->GetString();
+  if (result->extension_id.empty()) {
+    *error = "Missing appid on app node";
+    return false;
+  }
+
+  // Read the |status| attribute for the app.
+  // If the status is one of the defined app status error literals, then return
+  // it in the result as if it were an updatecheck status, then stop parsing,
+  // and return success.
+  const auto* status = app_node.FindKey("status");
+  if (status && status->is_string()) {
+    result->status = status->GetString();
+    if (result->status == "restricted" ||
+        result->status == "error-unknownApplication" ||
+        result->status == "error-invalidAppId")
+      return true;
+
+    // If the status was not handled above and the status is not "ok", then
+    // this must be a status literal that that the parser does not know about.
+    if (!result->status.empty() && result->status != "ok") {
+      *error = "Unknown app status";
+      return false;
+    }
+  }
+
+  DCHECK(result->status.empty() || result->status == "ok");
+  const auto* updatecheck_node = app_node.FindKey("updatecheck");
+  if (!updatecheck_node) {
+    *error = "Missing updatecheck on app.";
+    return false;
+  }
+
+  return ParseUpdateCheck(*updatecheck_node, result, error);
+}
+
+}  // namespace
+
+bool ProtocolParserJSON::DoParse(const std::string& response_json,
+                                 Results* results) {
+  DCHECK(results);
+
+  if (response_json.empty()) {
+    ParseError("Empty JSON.");
+    return false;
+  }
+
+  // The JSON response contains a prefix to prevent XSSI.
+  constexpr char kJSONPrefix[] = ")]}'";
+  if (!base::StartsWith(response_json, kJSONPrefix,
+                        base::CompareCase::SENSITIVE)) {
+    ParseError("Missing secure JSON prefix.");
+    return false;
+  }
+  const auto doc = base::JSONReader::Read(
+      {response_json.begin() + std::char_traits<char>::length(kJSONPrefix),
+       response_json.end()});
+  if (!doc) {
+    ParseError("JSON read error.");
+    return false;
+  }
+  if (!doc->is_dict()) {
+    ParseError("JSON document is not a dictionary.");
+    return false;
+  }
+  const auto* response_node = doc->FindKey("response");
+  if (!response_node || !response_node->is_dict()) {
+    ParseError("Missing 'response' element or 'response' is not a dictionary.");
+    return false;
+  }
+  const auto* protocol = response_node->FindKey("protocol");
+  if (!protocol || !protocol->is_string()) {
+    ParseError("Missing/non-string protocol.");
+    return false;
+  }
+  if (protocol->GetString() != kProtocolVersion) {
+    ParseError("Incorrect protocol. (expected '%s', found '%s')",
+               kProtocolVersion, protocol->GetString().c_str());
+    return false;
+  }
+
+  const auto* daystart_node = response_node->FindKey("daystart");
+  if (daystart_node && daystart_node->is_dict()) {
+    const auto* elapsed_seconds = daystart_node->FindKey("elapsed_seconds");
+    if (elapsed_seconds && elapsed_seconds->is_int())
+      results->daystart_elapsed_seconds = elapsed_seconds->GetInt();
+    const auto* elapsed_days = daystart_node->FindKey("elapsed_days");
+    if (elapsed_days && elapsed_days->is_int())
+      results->daystart_elapsed_days = elapsed_days->GetInt();
+  }
+
+  const auto* app_node = response_node->FindKey("app");
+  if (app_node && app_node->is_list()) {
+    for (const auto& app : app_node->GetList()) {
+      Result result;
+      std::string error;
+      if (ParseApp(app, &result, &error))
+        results->list.push_back(result);
+      else
+        ParseError("%s", error.c_str());
+    }
+  }
+
+  return true;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_parser_json.h b/src/updater_components/update_client/protocol_parser_json.h
new file mode 100644
index 0000000..71f96f4
--- /dev/null
+++ b/src/updater_components/update_client/protocol_parser_json.h
@@ -0,0 +1,30 @@
+// Copyright 2018 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 COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_JSON_H_
+#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_JSON_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/update_client/protocol_parser.h"
+
+namespace update_client {
+
+// Parses responses for the update protocol version 3.
+// (https://github.com/google/omaha/blob/wiki/ServerProtocolV3.md)
+class ProtocolParserJSON final : public ProtocolParser {
+ public:
+  ProtocolParserJSON() = default;
+
+ private:
+  // Overrides for ProtocolParser.
+  bool DoParse(const std::string& response_json, Results* results) override;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtocolParserJSON);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PROTOCOL_PARSER_JSON_H_
diff --git a/src/updater_components/update_client/protocol_parser_json_unittest.cc b/src/updater_components/update_client/protocol_parser_json_unittest.cc
new file mode 100644
index 0000000..5bf114a
--- /dev/null
+++ b/src/updater_components/update_client/protocol_parser_json_unittest.cc
@@ -0,0 +1,506 @@
+// Copyright 2018 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 "components/update_client/protocol_parser_json.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+const char* kJSONValid = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"},
+                    {"codebasediff":"http://diff.example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+const char* kJSONHash = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx",
+                              "hash_sha256":"1234",
+                              "hashdiff_sha256":"5678"}]}}
+     }
+    }
+   ]
+  }})";
+
+const char* kJSONInvalidSizes = R"()]}'
+ {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"name":"1","size":1234},
+                             {"name":"2","size":9007199254740991},
+                             {"name":"3","size":-1234},
+                             {"name":"4"},
+                             {"name":"5","size":"-a"},
+                             {"name":"6","size":-123467890123456789},
+                             {"name":"7","size":123467890123456789},
+                             {"name":"8","sizediff":1234},
+                             {"name":"9","sizediff":9007199254740991},
+                             {"name":"10","sizediff":-1234},
+                             {"name":"11","sizediff":"-a"},
+                             {"name":"12","sizediff":-123467890123456789},
+                             {"name":"13","sizediff":123467890123456789}]}}
+      }
+     }
+    ]
+   }})";
+
+const char* kJSONInvalidMissingCodebase = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebasediff":"http://diff.example.com"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"namediff":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+const char* kJSONInvalidMissingManifest = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://localhost/download/"}]}
+     }
+    }
+   ]
+  }})";
+
+const char* kJSONMissingAppId = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://localhost/download/"}]}
+     }
+    }
+   ]
+  }})";
+
+const char* kJSONInvalidCodebase = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"example.com/extension_1.2.3.4.crx",
+                     "version":"1.2.3.4"}]}
+     }
+    }
+   ]
+  }})";
+
+const char* kJSONMissingVersion = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://localhost/download/"}]},
+     "manifest":{
+      "packages":{"package":[{"name":"jebgalgnebhfojomionfpkfelancnnkf.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+const char* kJSONInvalidVersion = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://localhost/download/"}]},
+     "manifest":{
+      "version":"1.2.3.a",
+      "packages":{"package":[{"name":"jebgalgnebhfojomionfpkfelancnnkf.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+// Includes a <daystart> tag.
+const char* kJSONWithDaystart = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "daystart":{"elapsed_seconds":456},
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"},
+                    {"codebasediff":"http://diff.example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+// Indicates no updates available.
+const char* kJSONNoUpdate = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "status":"ok",
+     "updatecheck":{
+     "status":"noupdate"
+     }
+    }
+   ]
+  }})";
+
+// Includes two app objects, one app with an error.
+const char* kJSONTwoAppsOneError = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "daystart":{"elapsed_seconds":456},
+   "app":[
+    {"appid":"aaaaaaaa",
+     "status":"error-unknownApplication",
+     "updatecheck":{"status":"error-internal"}
+    },
+    {"appid":"bbbbbbbb",
+     "status":"ok",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+// Includes two <app> tags, both of which set the cohort.
+const char* kJSONTwoAppsSetCohort = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "daystart":{"elapsed_seconds":456},
+   "app":[
+    {"appid":"aaaaaaaa",
+     "cohort":"1:2q3/",
+     "updatecheck":{"status":"noupdate"}
+    },
+    {"appid":"bbbbbbbb",
+     "cohort":"1:33z@0.33",
+     "cohortname":"cname",
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+// Includes a run action for an update check with status='ok'.
+const char* kJSONUpdateCheckStatusOkWithRunAction = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "updatecheck":{
+     "status":"ok",
+     "actions":{"action":[{"run":"this"}]},
+     "urls":{"url":[{"codebase":"http://example.com/"},
+                    {"codebasediff":"http://diff.example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }})";
+
+// Includes a run action for an update check with status='noupdate'.
+const char* kJSONUpdateCheckStatusNoUpdateWithRunAction = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "updatecheck":{
+     "status":"noupdate",
+     "actions":{"action":[{"run":"this"}]}
+     }
+    }
+   ]
+  }})";
+
+// Includes a run action for an update check with status='error'.
+const char* kJSONUpdateCheckStatusErrorWithRunAction = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "updatecheck":{
+     "status":"error-osnotsupported",
+     "actions":{"action":[{"run":"this"}]}
+     }
+    }
+   ]
+  }})";
+
+// Includes four app objects with status different than 'ok'.
+const char* kJSONAppsStatusError = R"()]}'
+  {"response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"aaaaaaaa",
+     "status":"error-unknownApplication",
+     "updatecheck":{"status":"error-internal"}
+    },
+    {"appid":"bbbbbbbb",
+     "status":"restricted",
+     "updatecheck":{"status":"error-internal"}
+    },
+    {"appid":"cccccccc",
+     "status":"error-invalidAppId",
+     "updatecheck":{"status":"error-internal"}
+    },
+    {"appid":"dddddddd",
+     "status":"foobar",
+     "updatecheck":{"status":"error-internal"}
+    }
+   ]
+  }})";
+
+TEST(UpdateClientProtocolParserJSONTest, Parse) {
+  const auto parser = std::make_unique<ProtocolParserJSON>();
+
+  // Test parsing of a number of invalid JSON cases
+  EXPECT_FALSE(parser->Parse(std::string()));
+  EXPECT_FALSE(parser->errors().empty());
+
+  EXPECT_TRUE(parser->Parse(kJSONMissingAppId));
+  EXPECT_TRUE(parser->results().list.empty());
+  EXPECT_FALSE(parser->errors().empty());
+
+  EXPECT_TRUE(parser->Parse(kJSONInvalidCodebase));
+  EXPECT_TRUE(parser->results().list.empty());
+  EXPECT_FALSE(parser->errors().empty());
+
+  EXPECT_TRUE(parser->Parse(kJSONMissingVersion));
+  EXPECT_TRUE(parser->results().list.empty());
+  EXPECT_FALSE(parser->errors().empty());
+
+  EXPECT_TRUE(parser->Parse(kJSONInvalidVersion));
+  EXPECT_TRUE(parser->results().list.empty());
+  EXPECT_FALSE(parser->errors().empty());
+
+  EXPECT_TRUE(parser->Parse(kJSONInvalidMissingCodebase));
+  EXPECT_TRUE(parser->results().list.empty());
+  EXPECT_FALSE(parser->errors().empty());
+
+  EXPECT_TRUE(parser->Parse(kJSONInvalidMissingManifest));
+  EXPECT_TRUE(parser->results().list.empty());
+  EXPECT_FALSE(parser->errors().empty());
+
+  {
+    // Parse some valid XML, and check that all params came out as expected.
+    EXPECT_TRUE(parser->Parse(kJSONValid));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_EQ(1u, parser->results().list.size());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_STREQ("ok", first_result->status.c_str());
+    EXPECT_EQ(1u, first_result->crx_urls.size());
+    EXPECT_EQ(GURL("http://example.com/"), first_result->crx_urls[0]);
+    EXPECT_EQ(GURL("http://diff.example.com/"), first_result->crx_diffurls[0]);
+    EXPECT_EQ("1.2.3.4", first_result->manifest.version);
+    EXPECT_EQ("2.0.143.0", first_result->manifest.browser_min_version);
+    EXPECT_EQ(1u, first_result->manifest.packages.size());
+    EXPECT_EQ("extension_1_2_3_4.crx", first_result->manifest.packages[0].name);
+  }
+  {
+    // Parse xml with hash value.
+    EXPECT_TRUE(parser->Parse(kJSONHash));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_FALSE(parser->results().list.empty());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_FALSE(first_result->manifest.packages.empty());
+    EXPECT_EQ("1234", first_result->manifest.packages[0].hash_sha256);
+    EXPECT_EQ("5678", first_result->manifest.packages[0].hashdiff_sha256);
+  }
+  {
+    // Parse xml with package size value.
+    EXPECT_TRUE(parser->Parse(kJSONInvalidSizes));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_FALSE(parser->results().list.empty());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_FALSE(first_result->manifest.packages.empty());
+    EXPECT_EQ(1234, first_result->manifest.packages[0].size);
+    EXPECT_EQ(9007199254740991, first_result->manifest.packages[1].size);
+    EXPECT_EQ(0, first_result->manifest.packages[2].size);
+    EXPECT_EQ(0, first_result->manifest.packages[3].size);
+    EXPECT_EQ(0, first_result->manifest.packages[4].size);
+    EXPECT_EQ(0, first_result->manifest.packages[5].size);
+    EXPECT_EQ(0, first_result->manifest.packages[6].size);
+    EXPECT_EQ(1234, first_result->manifest.packages[7].sizediff);
+    EXPECT_EQ(9007199254740991, first_result->manifest.packages[8].sizediff);
+    EXPECT_EQ(0, first_result->manifest.packages[9].sizediff);
+    EXPECT_EQ(0, first_result->manifest.packages[10].sizediff);
+    EXPECT_EQ(0, first_result->manifest.packages[11].sizediff);
+    EXPECT_EQ(0, first_result->manifest.packages[12].sizediff);
+  }
+  {
+    // Parse xml with a <daystart> element.
+    EXPECT_TRUE(parser->Parse(kJSONWithDaystart));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_FALSE(parser->results().list.empty());
+    EXPECT_EQ(parser->results().daystart_elapsed_seconds, 456);
+  }
+  {
+    // Parse a no-update response.
+    EXPECT_TRUE(parser->Parse(kJSONNoUpdate));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_FALSE(parser->results().list.empty());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_STREQ("noupdate", first_result->status.c_str());
+    EXPECT_EQ(first_result->extension_id, "12345");
+    EXPECT_EQ(first_result->manifest.version, "");
+  }
+  {
+    // Parse xml with one error and one success <app> tag.
+    EXPECT_TRUE(parser->Parse(kJSONTwoAppsOneError));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_EQ(2u, parser->results().list.size());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_EQ(first_result->extension_id, "aaaaaaaa");
+    EXPECT_STREQ("error-unknownApplication", first_result->status.c_str());
+    EXPECT_TRUE(first_result->manifest.version.empty());
+    const auto* second_result = &parser->results().list[1];
+    EXPECT_EQ(second_result->extension_id, "bbbbbbbb");
+    EXPECT_STREQ("ok", second_result->status.c_str());
+    EXPECT_EQ("1.2.3.4", second_result->manifest.version);
+  }
+  {
+    // Parse xml with two apps setting the cohort info.
+    EXPECT_TRUE(parser->Parse(kJSONTwoAppsSetCohort));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_EQ(2u, parser->results().list.size());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_EQ(first_result->extension_id, "aaaaaaaa");
+    EXPECT_NE(first_result->cohort_attrs.find("cohort"),
+              first_result->cohort_attrs.end());
+    EXPECT_EQ(first_result->cohort_attrs.find("cohort")->second, "1:2q3/");
+    EXPECT_EQ(first_result->cohort_attrs.find("cohortname"),
+              first_result->cohort_attrs.end());
+    EXPECT_EQ(first_result->cohort_attrs.find("cohorthint"),
+              first_result->cohort_attrs.end());
+    const auto* second_result = &parser->results().list[1];
+    EXPECT_EQ(second_result->extension_id, "bbbbbbbb");
+    EXPECT_NE(second_result->cohort_attrs.find("cohort"),
+              second_result->cohort_attrs.end());
+    EXPECT_EQ(second_result->cohort_attrs.find("cohort")->second, "1:33z@0.33");
+    EXPECT_NE(second_result->cohort_attrs.find("cohortname"),
+              second_result->cohort_attrs.end());
+    EXPECT_EQ(second_result->cohort_attrs.find("cohortname")->second, "cname");
+    EXPECT_EQ(second_result->cohort_attrs.find("cohorthint"),
+              second_result->cohort_attrs.end());
+  }
+  {
+    EXPECT_TRUE(parser->Parse(kJSONUpdateCheckStatusOkWithRunAction));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_FALSE(parser->results().list.empty());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_STREQ("ok", first_result->status.c_str());
+    EXPECT_EQ(first_result->extension_id, "12345");
+    EXPECT_STREQ("this", first_result->action_run.c_str());
+  }
+  {
+    EXPECT_TRUE(parser->Parse(kJSONUpdateCheckStatusNoUpdateWithRunAction));
+    EXPECT_TRUE(parser->errors().empty());
+    EXPECT_FALSE(parser->results().list.empty());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_STREQ("noupdate", first_result->status.c_str());
+    EXPECT_EQ(first_result->extension_id, "12345");
+    EXPECT_STREQ("this", first_result->action_run.c_str());
+  }
+  {
+    EXPECT_TRUE(parser->Parse(kJSONUpdateCheckStatusErrorWithRunAction));
+    EXPECT_FALSE(parser->errors().empty());
+    EXPECT_TRUE(parser->results().list.empty());
+  }
+  {
+    EXPECT_TRUE(parser->Parse(kJSONAppsStatusError));
+    EXPECT_STREQ("Unknown app status", parser->errors().c_str());
+    EXPECT_EQ(3u, parser->results().list.size());
+    const auto* first_result = &parser->results().list[0];
+    EXPECT_EQ(first_result->extension_id, "aaaaaaaa");
+    EXPECT_STREQ("error-unknownApplication", first_result->status.c_str());
+    EXPECT_TRUE(first_result->manifest.version.empty());
+    const auto* second_result = &parser->results().list[1];
+    EXPECT_EQ(second_result->extension_id, "bbbbbbbb");
+    EXPECT_STREQ("restricted", second_result->status.c_str());
+    EXPECT_TRUE(second_result->manifest.version.empty());
+    const auto* third_result = &parser->results().list[2];
+    EXPECT_EQ(third_result->extension_id, "cccccccc");
+    EXPECT_STREQ("error-invalidAppId", third_result->status.c_str());
+    EXPECT_TRUE(third_result->manifest.version.empty());
+  }
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer.cc b/src/updater_components/update_client/protocol_serializer.cc
new file mode 100644
index 0000000..5d106c5
--- /dev/null
+++ b/src/updater_components/update_client/protocol_serializer.cc
@@ -0,0 +1,246 @@
+// Copyright 2018 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 "components/update_client/protocol_serializer.h"
+
+#include <utility>
+
+#include "base/containers/flat_map.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/system/sys_info.h"
+#include "base/version.h"
+#include "build/build_config.h"
+#include "components/update_client/activity_data_service.h"
+#include "components/update_client/persisted_data.h"
+#include "components/update_client/update_query_params.h"
+#include "components/update_client/updater_state.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace update_client {
+
+namespace {
+
+// Returns the amount of physical memory in GB, rounded to the nearest GB.
+int GetPhysicalMemoryGB() {
+  const double kOneGB = 1024 * 1024 * 1024;
+  const int64_t phys_mem = base::SysInfo::AmountOfPhysicalMemory();
+  return static_cast<int>(std::floor(0.5 + phys_mem / kOneGB));
+}
+
+std::string GetOSVersion() {
+#if defined(OS_WIN)
+  const auto ver = base::win::OSInfo::GetInstance()->version_number();
+  return base::StringPrintf("%d.%d.%d.%d", ver.major, ver.minor, ver.build,
+                            ver.patch);
+#else
+  return base::SysInfo().OperatingSystemVersion();
+#endif
+}
+
+std::string GetServicePack() {
+#if defined(OS_WIN)
+  return base::win::OSInfo::GetInstance()->service_pack_str();
+#else
+  return {};
+#endif
+}
+
+}  // namespace
+
+base::flat_map<std::string, std::string> BuildUpdateCheckExtraRequestHeaders(
+    const std::string& prod_id,
+    const base::Version& browser_version,
+    const std::vector<std::string>& ids,
+    bool is_foreground) {
+  // This number of extension ids results in an HTTP header length of about 1KB.
+  constexpr size_t maxIdsCount = 30;
+  const std::vector<std::string>& app_ids =
+      ids.size() <= maxIdsCount
+          ? ids
+          : std::vector<std::string>(ids.cbegin(), ids.cbegin() + maxIdsCount);
+  return {
+      {"X-Goog-Update-Updater",
+       base::StrCat({prod_id, "-", browser_version.GetString()})},
+      {"X-Goog-Update-Interactivity", is_foreground ? "fg" : "bg"},
+      {"X-Goog-Update-AppId", base::JoinString(app_ids, ",")},
+  };
+}
+
+protocol_request::Request MakeProtocolRequest(
+    const std::string& session_id,
+    const std::string& prod_id,
+    const std::string& browser_version,
+    const std::string& lang,
+    const std::string& channel,
+    const std::string& os_long_name,
+    const std::string& download_preference,
+    const base::flat_map<std::string, std::string>& additional_attributes,
+    const std::map<std::string, std::string>* updater_state_attributes,
+    std::vector<protocol_request::App> apps) {
+  protocol_request::Request request;
+  request.protocol_version = kProtocolVersion;
+
+  // Session id and request id.
+  DCHECK(!session_id.empty());
+  DCHECK(base::StartsWith(session_id, "{", base::CompareCase::SENSITIVE));
+  DCHECK(base::EndsWith(session_id, "}", base::CompareCase::SENSITIVE));
+  request.session_id = session_id;
+  request.request_id = base::StrCat({"{", base::GenerateGUID(), "}"});
+
+  request.updatername = prod_id;
+  request.updaterversion = browser_version;
+  request.prodversion = browser_version;
+  request.lang = lang;
+  request.updaterchannel = channel;
+  request.prodchannel = channel;
+  request.operating_system = UpdateQueryParams::GetOS();
+  request.arch = UpdateQueryParams::GetArch();
+  request.nacl_arch = UpdateQueryParams::GetNaclArch();
+  request.dlpref = download_preference;
+  request.additional_attributes = additional_attributes;
+
+#if defined(OS_WIN)
+  if (base::win::OSInfo::GetInstance()->wow64_status() ==
+      base::win::OSInfo::WOW64_ENABLED)
+    request.is_wow64 = true;
+#endif
+
+  if (updater_state_attributes &&
+      updater_state_attributes->count(UpdaterState::kIsEnterpriseManaged)) {
+    request.domain_joined =
+        updater_state_attributes->at(UpdaterState::kIsEnterpriseManaged) == "1";
+  }
+
+  // HW platform information.
+  request.hw.physmemory = GetPhysicalMemoryGB();
+
+  // OS version and platform information.
+  request.os.platform = os_long_name;
+  request.os.version = GetOSVersion();
+  request.os.service_pack = GetServicePack();
+  request.os.arch = base::SysInfo().OperatingSystemArchitecture();
+
+  if (updater_state_attributes) {
+    request.updater = base::make_optional<protocol_request::Updater>();
+    auto it = updater_state_attributes->find("name");
+    if (it != updater_state_attributes->end())
+      request.updater->name = it->second;
+    it = updater_state_attributes->find("version");
+    if (it != updater_state_attributes->end())
+      request.updater->version = it->second;
+    it = updater_state_attributes->find("ismachine");
+    if (it != updater_state_attributes->end()) {
+      DCHECK(it->second == "0" || it->second == "1");
+      request.updater->is_machine = it->second != "0";
+    }
+    it = updater_state_attributes->find("autoupdatecheckenabled");
+    if (it != updater_state_attributes->end()) {
+      DCHECK(it->second == "0" || it->second == "1");
+      request.updater->autoupdate_check_enabled = it->second != "0";
+    }
+    it = updater_state_attributes->find("laststarted");
+    if (it != updater_state_attributes->end()) {
+      int last_started = 0;
+      if (base::StringToInt(it->second, &last_started))
+        request.updater->last_started = last_started;
+    }
+    it = updater_state_attributes->find("lastchecked");
+    if (it != updater_state_attributes->end()) {
+      int last_checked = 0;
+      if (base::StringToInt(it->second, &last_checked))
+        request.updater->last_checked = last_checked;
+    }
+    it = updater_state_attributes->find("updatepolicy");
+    if (it != updater_state_attributes->end()) {
+      int update_policy = 0;
+      if (base::StringToInt(it->second, &update_policy))
+        request.updater->update_policy = update_policy;
+    }
+  }
+
+  request.apps = std::move(apps);
+  return request;
+}
+
+protocol_request::App MakeProtocolApp(
+    const std::string& app_id,
+    const base::Version& version,
+    base::Optional<std::vector<base::Value>> events) {
+  protocol_request::App app;
+  app.app_id = app_id;
+  app.version = version.GetString();
+  app.events = std::move(events);
+  return app;
+}
+
+protocol_request::App MakeProtocolApp(
+    const std::string& app_id,
+    const base::Version& version,
+    const std::string& brand_code,
+    const std::string& install_source,
+    const std::string& install_location,
+    const std::string& fingerprint,
+    const base::flat_map<std::string, std::string>& installer_attributes,
+    const std::string& cohort,
+    const std::string& cohort_hint,
+    const std::string& cohort_name,
+    const std::vector<int>& disabled_reasons,
+    base::Optional<protocol_request::UpdateCheck> update_check,
+    base::Optional<protocol_request::Ping> ping) {
+  auto app = MakeProtocolApp(app_id, version, base::nullopt);
+  app.brand_code = brand_code;
+  app.install_source = install_source;
+  app.install_location = install_location;
+  app.fingerprint = fingerprint;
+  app.installer_attributes = installer_attributes;
+  app.cohort = cohort;
+  app.cohort_hint = cohort_hint;
+  app.cohort_name = cohort_name;
+  app.enabled = disabled_reasons.empty();
+  app.disabled_reasons = disabled_reasons;
+  app.update_check = std::move(update_check);
+  app.ping = std::move(ping);
+  return app;
+}
+
+protocol_request::UpdateCheck MakeProtocolUpdateCheck(bool is_update_disabled) {
+  protocol_request::UpdateCheck update_check;
+  update_check.is_update_disabled = is_update_disabled;
+  return update_check;
+}
+
+protocol_request::Ping MakeProtocolPing(const std::string& app_id,
+                                        const PersistedData* metadata) {
+  DCHECK(metadata);
+  protocol_request::Ping ping;
+
+  if (metadata->GetActiveBit(app_id)) {
+    const int date_last_active = metadata->GetDateLastActive(app_id);
+    if (date_last_active != kDateUnknown) {
+      ping.date_last_active = date_last_active;
+    } else {
+      ping.days_since_last_active_ping =
+          metadata->GetDaysSinceLastActive(app_id);
+    }
+  }
+  const int date_last_roll_call = metadata->GetDateLastRollCall(app_id);
+  if (date_last_roll_call != kDateUnknown) {
+    ping.date_last_roll_call = date_last_roll_call;
+  } else {
+    ping.days_since_last_roll_call = metadata->GetDaysSinceLastRollCall(app_id);
+  }
+  ping.ping_freshness = metadata->GetPingFreshness(app_id);
+
+  return ping;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer.h b/src/updater_components/update_client/protocol_serializer.h
new file mode 100644
index 0000000..47c212a
--- /dev/null
+++ b/src/updater_components/update_client/protocol_serializer.h
@@ -0,0 +1,84 @@
+// Copyright 2018 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 COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_H_
+#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_H_
+
+#include <map>
+#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/protocol_definition.h"
+
+namespace base {
+class Version;
+}
+
+namespace update_client {
+
+class PersistedData;
+
+// Creates the values for the DDOS extra request headers sent with the update
+// check. These headers include "X-Goog-Update-Updater",
+// "X-Goog-Update-AppId", and  "X-Goog-Update-Interactivity".
+base::flat_map<std::string, std::string> BuildUpdateCheckExtraRequestHeaders(
+    const std::string& prod_id,
+    const base::Version& browser_version,
+    const std::vector<std::string>& ids_checked,
+    bool is_foreground);
+
+protocol_request::Request MakeProtocolRequest(
+    const std::string& session_id,
+    const std::string& prod_id,
+    const std::string& browser_version,
+    const std::string& lang,
+    const std::string& channel,
+    const std::string& os_long_name,
+    const std::string& download_preference,
+    const base::flat_map<std::string, std::string>& additional_attributes,
+    const std::map<std::string, std::string>* updater_state_attributes,
+    std::vector<protocol_request::App> apps);
+
+protocol_request::App MakeProtocolApp(
+    const std::string& app_id,
+    const base::Version& version,
+    base::Optional<std::vector<base::Value>> events);
+
+protocol_request::App MakeProtocolApp(
+    const std::string& app_id,
+    const base::Version& version,
+    const std::string& brand_code,
+    const std::string& install_source,
+    const std::string& install_location,
+    const std::string& fingerprint,
+    const base::flat_map<std::string, std::string>& installer_attributes,
+    const std::string& cohort,
+    const std::string& cohort_hint,
+    const std::string& cohort_name,
+    const std::vector<int>& disabled_reasons,
+    base::Optional<protocol_request::UpdateCheck> update_check,
+    base::Optional<protocol_request::Ping> ping);
+
+protocol_request::UpdateCheck MakeProtocolUpdateCheck(bool is_update_disabled);
+
+protocol_request::Ping MakeProtocolPing(const std::string& app_id,
+                                        const PersistedData* metadata);
+
+class ProtocolSerializer {
+ public:
+  virtual ~ProtocolSerializer() = default;
+  virtual std::string Serialize(
+      const protocol_request::Request& request) const = 0;
+
+ protected:
+  ProtocolSerializer() = default;
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_H_
diff --git a/src/updater_components/update_client/protocol_serializer_json.cc b/src/updater_components/update_client/protocol_serializer_json.cc
new file mode 100644
index 0000000..8e7aee8
--- /dev/null
+++ b/src/updater_components/update_client/protocol_serializer_json.cc
@@ -0,0 +1,183 @@
+// Copyright 2018 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 "components/update_client/protocol_serializer_json.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/json/json_writer.h"
+#include "base/values.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
+#include "components/update_client/updater_state.h"
+
+namespace update_client {
+
+using Value = base::Value;
+
+std::string ProtocolSerializerJSON::Serialize(
+    const protocol_request::Request& request) const {
+  Value root_node(Value::Type::DICTIONARY);
+  auto* request_node =
+      root_node.SetKey("request", Value(Value::Type::DICTIONARY));
+  request_node->SetKey("protocol", Value(request.protocol_version));
+  request_node->SetKey("dedup", Value("cr"));
+  request_node->SetKey("acceptformat", Value("crx2,crx3"));
+  if (!request.additional_attributes.empty()) {
+    for (const auto& attr : request.additional_attributes)
+      request_node->SetKey(attr.first, Value(attr.second));
+  }
+  request_node->SetKey("sessionid", Value(request.session_id));
+  request_node->SetKey("requestid", Value(request.request_id));
+  request_node->SetKey("@updater", Value(request.updatername));
+  request_node->SetKey("prodversion", Value(request.updaterversion));
+  request_node->SetKey("updaterversion", Value(request.prodversion));
+  request_node->SetKey("lang", Value(request.lang));
+  request_node->SetKey("@os", Value(request.operating_system));
+  request_node->SetKey("arch", Value(request.arch));
+  request_node->SetKey("nacl_arch", Value(request.nacl_arch));
+#if defined(OS_WIN)
+  if (request.is_wow64)
+    request_node->SetKey("wow64", Value(request.is_wow64));
+#endif  // OS_WIN
+  if (!request.updaterchannel.empty())
+    request_node->SetKey("updaterchannel", Value(request.updaterchannel));
+  if (!request.prodchannel.empty())
+    request_node->SetKey("prodchannel", Value(request.prodchannel));
+  if (!request.dlpref.empty())
+    request_node->SetKey("dlpref", Value(request.dlpref));
+  if (request.domain_joined) {
+    request_node->SetKey(UpdaterState::kIsEnterpriseManaged,
+                         Value(*request.domain_joined));
+  }
+
+  // HW platform information.
+  auto* hw_node = request_node->SetKey("hw", Value(Value::Type::DICTIONARY));
+  hw_node->SetKey("physmemory", Value(static_cast<int>(request.hw.physmemory)));
+
+  // OS version and platform information.
+  auto* os_node = request_node->SetKey("os", Value(Value::Type::DICTIONARY));
+  os_node->SetKey("platform", Value(request.os.platform));
+  os_node->SetKey("arch", Value(request.os.arch));
+  if (!request.os.version.empty())
+    os_node->SetKey("version", Value(request.os.version));
+  if (!request.os.service_pack.empty())
+    os_node->SetKey("sp", Value(request.os.service_pack));
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  if (request.updater) {
+    const auto& updater = *request.updater;
+    auto* updater_node =
+        request_node->SetKey("updater", Value(Value::Type::DICTIONARY));
+    updater_node->SetKey("name", Value(updater.name));
+    updater_node->SetKey("ismachine", Value(updater.is_machine));
+    updater_node->SetKey("autoupdatecheckenabled",
+                         Value(updater.autoupdate_check_enabled));
+    updater_node->SetKey("updatepolicy", Value(updater.update_policy));
+    if (!updater.version.empty())
+      updater_node->SetKey("version", Value(updater.version));
+    if (updater.last_checked)
+      updater_node->SetKey("lastchecked", Value(*updater.last_checked));
+    if (updater.last_started)
+      updater_node->SetKey("laststarted", Value(*updater.last_started));
+  }
+#endif
+
+  std::vector<Value> app_nodes;
+  for (const auto& app : request.apps) {
+    Value app_node(Value::Type::DICTIONARY);
+    app_node.SetKey("appid", Value(app.app_id));
+    app_node.SetKey("version", Value(app.version));
+    if (!app.brand_code.empty())
+      app_node.SetKey("brand", Value(app.brand_code));
+    if (!app.install_source.empty())
+      app_node.SetKey("installsource", Value(app.install_source));
+    if (!app.install_location.empty())
+      app_node.SetKey("installedby", Value(app.install_location));
+    if (!app.cohort.empty())
+      app_node.SetKey("cohort", Value(app.cohort));
+    if (!app.cohort_name.empty())
+      app_node.SetKey("cohortname", Value(app.cohort_name));
+    if (!app.cohort_hint.empty())
+      app_node.SetKey("cohorthint", Value(app.cohort_hint));
+    if (app.enabled)
+      app_node.SetKey("enabled", Value(*app.enabled));
+
+    if (app.disabled_reasons && !app.disabled_reasons->empty()) {
+      std::vector<Value> disabled_nodes;
+      for (const int disabled_reason : *app.disabled_reasons) {
+        Value disabled_node(Value::Type::DICTIONARY);
+        disabled_node.SetKey("reason", Value(disabled_reason));
+        disabled_nodes.push_back(std::move(disabled_node));
+      }
+      app_node.SetKey("disabled", Value(disabled_nodes));
+    }
+
+    for (const auto& attr : app.installer_attributes)
+      app_node.SetKey(attr.first, Value(attr.second));
+
+    if (app.update_check) {
+      auto* update_check_node =
+          app_node.SetKey("updatecheck", Value(Value::Type::DICTIONARY));
+      if (app.update_check->is_update_disabled)
+        update_check_node->SetKey("updatedisabled", Value(true));
+    }
+
+    if (app.ping) {
+      const auto& ping = *app.ping;
+      auto* ping_node = app_node.SetKey("ping", Value(Value::Type::DICTIONARY));
+      if (!ping.ping_freshness.empty())
+        ping_node->SetKey("ping_freshness", Value(ping.ping_freshness));
+
+      // Output "ad" or "a" only if the this app has been seen 'active'.
+      if (ping.date_last_active) {
+        ping_node->SetKey("ad", Value(*ping.date_last_active));
+      } else if (ping.days_since_last_active_ping) {
+        ping_node->SetKey("a", Value(*ping.days_since_last_active_ping));
+      }
+
+      // Output "rd" if valid or "r" as a last resort roll call metric.
+      if (ping.date_last_roll_call)
+        ping_node->SetKey("rd", Value(*ping.date_last_roll_call));
+      else
+        ping_node->SetKey("r", Value(ping.days_since_last_roll_call));
+    }
+
+    if (!app.fingerprint.empty()) {
+      std::vector<Value> package_nodes;
+      Value package(Value::Type::DICTIONARY);
+      package.SetKey("fp", Value(app.fingerprint));
+      package_nodes.push_back(std::move(package));
+      auto* packages_node =
+          app_node.SetKey("packages", Value(Value::Type::DICTIONARY));
+      packages_node->SetKey("package", Value(package_nodes));
+    }
+
+    if (app.events) {
+      std::vector<Value> event_nodes;
+      for (const auto& event : *app.events) {
+        DCHECK(event.is_dict());
+        DCHECK(!event.DictEmpty());
+        event_nodes.push_back(event.Clone());
+      }
+      app_node.SetKey("event", Value(event_nodes));
+    }
+
+    app_nodes.push_back(std::move(app_node));
+  }
+
+  if (!app_nodes.empty())
+    request_node->SetKey("app", Value(std::move(app_nodes)));
+
+  std::string msg;
+  return base::JSONWriter::WriteWithOptions(
+             root_node, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
+             &msg)
+             ? msg
+             : std::string();
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer_json.h b/src/updater_components/update_client/protocol_serializer_json.h
new file mode 100644
index 0000000..71f2a99
--- /dev/null
+++ b/src/updater_components/update_client/protocol_serializer_json.h
@@ -0,0 +1,28 @@
+// Copyright 2018 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 COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_JSON_H_
+#define COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_JSON_H_
+
+#include <string>
+
+#include "components/update_client/protocol_serializer.h"
+
+namespace update_client {
+
+class ProtocolSerializerJSON final : public ProtocolSerializer {
+ public:
+  ProtocolSerializerJSON() = default;
+
+  // Overrides for ProtocolSerializer.
+  std::string Serialize(
+      const protocol_request::Request& request) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProtocolSerializerJSON);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_PROTOCOL_SERIALIZER_JSON_H_
diff --git a/src/updater_components/update_client/protocol_serializer_json_unittest.cc b/src/updater_components/update_client/protocol_serializer_json_unittest.cc
new file mode 100644
index 0000000..4cf2666
--- /dev/null
+++ b/src/updater_components/update_client/protocol_serializer_json_unittest.cc
@@ -0,0 +1,128 @@
+// Copyright 2018 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 "components/update_client/protocol_serializer_json.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/optional.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "build/branding_buildflags.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/update_client/activity_data_service.h"
+#include "components/update_client/persisted_data.h"
+#include "components/update_client/protocol_definition.h"
+#include "components/update_client/protocol_serializer.h"
+#include "components/update_client/updater_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/re2/src/re2/re2.h"
+
+using base::Value;
+using std::string;
+
+namespace update_client {
+
+TEST(SerializeRequestJSON, Serialize) {
+  // When no updater state is provided, then check that the elements and
+  // attributes related to the updater state are not serialized.
+
+  std::vector<base::Value> events;
+  events.push_back(Value(Value::Type::DICTIONARY));
+  events.push_back(Value(Value::Type::DICTIONARY));
+  events[0].SetKey("a", Value(1));
+  events[0].SetKey("b", Value("2"));
+  events[1].SetKey("error", Value(0));
+
+  auto pref = std::make_unique<TestingPrefServiceSimple>();
+  PersistedData::RegisterPrefs(pref->registry());
+  auto metadata = std::make_unique<PersistedData>(pref.get(), nullptr);
+  std::vector<std::string> items = {"id1"};
+  metadata->SetDateLastRollCall(items, 1234);
+
+  std::vector<protocol_request::App> apps;
+  apps.push_back(MakeProtocolApp(
+      "id1", base::Version("1.0"), "brand1", "source1", "location1", "fp1",
+      {{"attr1", "1"}, {"attr2", "2"}}, "c1", "ch1", "cn1", {0, 1},
+      MakeProtocolUpdateCheck(true), MakeProtocolPing("id1", metadata.get())));
+  apps.push_back(
+      MakeProtocolApp("id2", base::Version("2.0"), std::move(events)));
+
+  const auto request = std::make_unique<ProtocolSerializerJSON>()->Serialize(
+      MakeProtocolRequest("{15160585-8ADE-4D3C-839B-1281A6035D1F}", "prod_id",
+                          "1.0", "lang", "channel", "OS", "cacheable",
+                          {{"extra", "params"}}, nullptr, std::move(apps)));
+  constexpr char regex[] =
+      R"({"request":{"@os":"\w+","@updater":"prod_id",)"
+      R"("acceptformat":"crx2,crx3",)"
+      R"("app":\[{"appid":"id1","attr1":"1","attr2":"2","brand":"brand1",)"
+      R"("cohort":"c1","cohorthint":"ch1","cohortname":"cn1",)"
+      R"("disabled":\[{"reason":0},{"reason":1}],"enabled":false,)"
+      R"("installedby":"location1","installsource":"source1",)"
+      R"("packages":{"package":\[{"fp":"fp1"}]},)"
+      R"("ping":{"ping_freshness":"{[-\w]{36}}","rd":1234},)"
+      R"("updatecheck":{"updatedisabled":true},"version":"1.0"},)"
+      R"({"appid":"id2","event":\[{"a":1,"b":"2"},{"error":0}],)"
+      R"("version":"2.0"}],"arch":"\w+","dedup":"cr","dlpref":"cacheable",)"
+      R"("extra":"params","hw":{"physmemory":\d+},"lang":"lang",)"
+      R"("nacl_arch":"[-\w]+","os":{"arch":"[_,-.\w]+","platform":"OS",)"
+      R"(("sp":"[\s\w]+",)?"version":"[-.\w]+"},"prodchannel":"channel",)"
+      R"("prodversion":"1.0","protocol":"3.1","requestid":"{[-\w]{36}}",)"
+      R"("sessionid":"{[-\w]{36}}","updaterchannel":"channel",)"
+      R"("updaterversion":"1.0"(,"wow64":true)?}})";
+  EXPECT_TRUE(RE2::FullMatch(request, regex)) << request;
+}
+
+TEST(SerializeRequestJSON, DownloadPreference) {
+  // Verifies that an empty |download_preference| is not serialized.
+  const auto serializer = std::make_unique<ProtocolSerializerJSON>();
+  auto request = serializer->Serialize(
+      MakeProtocolRequest("{15160585-8ADE-4D3C-839B-1281A6035D1F}", "", "", "",
+                          "", "", "", {}, nullptr, {}));
+  EXPECT_FALSE(RE2::PartialMatch(request, R"("dlpref":)")) << request;
+
+  // Verifies that |download_preference| is serialized.
+  request = serializer->Serialize(
+      MakeProtocolRequest("{15160585-8ADE-4D3C-839B-1281A6035D1F}", "", "", "",
+                          "", "", "cacheable", {}, nullptr, {}));
+  EXPECT_TRUE(RE2::PartialMatch(request, R"("dlpref":"cacheable")")) << request;
+}
+
+// When present, updater state attributes are only serialized for Google builds,
+// except the |domainjoined| attribute, which is serialized in all cases.
+TEST(SerializeRequestJSON, UpdaterStateAttributes) {
+  const auto serializer = std::make_unique<ProtocolSerializerJSON>();
+  UpdaterState::Attributes attributes;
+  attributes["ismachine"] = "1";
+  attributes["domainjoined"] = "1";
+  attributes["name"] = "Omaha";
+  attributes["version"] = "1.2.3.4";
+  attributes["laststarted"] = "1";
+  attributes["lastchecked"] = "2";
+  attributes["autoupdatecheckenabled"] = "0";
+  attributes["updatepolicy"] = "-1";
+  const auto request = serializer->Serialize(MakeProtocolRequest(
+      "{15160585-8ADE-4D3C-839B-1281A6035D1F}", "prod_id", "1.0", "lang",
+      "channel", "OS", "cacheable", {{"extra", "params"}}, &attributes, {}));
+  constexpr char regex[] =
+      R"({"request":{"@os":"\w+","@updater":"prod_id",)"
+      R"("acceptformat":"crx2,crx3","arch":"\w+","dedup":"cr",)"
+      R"("dlpref":"cacheable","domainjoined":true,"extra":"params",)"
+      R"("hw":{"physmemory":\d+},"lang":"lang","nacl_arch":"[-\w]+",)"
+      R"("os":{"arch":"[,-.\w]+","platform":"OS",("sp":"[\s\w]+",)?)"
+      R"("version":"[-.\w]+"},"prodchannel":"channel","prodversion":"1.0",)"
+      R"("protocol":"3.1","requestid":"{[-\w]{36}}","sessionid":"{[-\w]{36}}",)"
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+      R"("updater":{"autoupdatecheckenabled":false,"ismachine":true,)"
+      R"("lastchecked":2,"laststarted":1,"name":"Omaha","updatepolicy":-1,)"
+      R"("version":"1\.2\.3\.4"},)"
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+      R"("updaterchannel":"channel","updaterversion":"1.0"(,"wow64":true)?}})";
+  EXPECT_TRUE(RE2::FullMatch(request, regex)) << request;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/protocol_serializer_unittest.cc b/src/updater_components/update_client/protocol_serializer_unittest.cc
new file mode 100644
index 0000000..f0c8a14
--- /dev/null
+++ b/src/updater_components/update_client/protocol_serializer_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "base/version.h"
+#include "components/update_client/protocol_serializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace update_client {
+
+TEST(BuildProtocolRequest, BuildUpdateCheckExtraRequestHeaders) {
+  auto headers = BuildUpdateCheckExtraRequestHeaders(
+      "fake_prodid", base::Version("30.0"), {}, true);
+  EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]);
+  EXPECT_EQ("fg", headers["X-Goog-Update-Interactivity"]);
+  EXPECT_TRUE(headers["X-Goog-Update-AppId"].empty());
+
+  headers = BuildUpdateCheckExtraRequestHeaders(
+      "fake_prodid", base::Version("30.0"), {}, false);
+  EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]);
+  EXPECT_EQ("bg", headers["X-Goog-Update-Interactivity"]);
+  EXPECT_TRUE(headers["X-Goog-Update-AppId"].empty());
+
+  headers = BuildUpdateCheckExtraRequestHeaders(
+      "fake_prodid", base::Version("30.0"),
+      {"jebgalgnebhfojomionfpkfelancnnkf"}, true);
+  EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]);
+  EXPECT_EQ("fg", headers["X-Goog-Update-Interactivity"]);
+  EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", headers["X-Goog-Update-AppId"]);
+
+  headers = BuildUpdateCheckExtraRequestHeaders(
+      "fake_prodid", base::Version("30.0"),
+      {"jebgalgnebhfojomionfpkfelancnnkf", "ihfokbkgjpifbbojhneepfflplebdkc"},
+      true);
+  EXPECT_EQ("fake_prodid-30.0", headers["X-Goog-Update-Updater"]);
+  EXPECT_EQ("fg", headers["X-Goog-Update-Interactivity"]);
+  EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf,ihfokbkgjpifbbojhneepfflplebdkc",
+            headers["X-Goog-Update-AppId"]);
+
+  // Test that only 30 extension ids are joined in the headers.
+  headers = BuildUpdateCheckExtraRequestHeaders(
+      "fake_prodid", base::Version("30.0"),
+      std::vector<std::string>(40, "jebgalgnebhfojomionfpkfelancnnkf"), true);
+  EXPECT_EQ(base::JoinString(std::vector<std::string>(
+                                 30, "jebgalgnebhfojomionfpkfelancnnkf"),
+                             ","),
+            headers["X-Goog-Update-AppId"]);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/request_sender.cc b/src/updater_components/update_client/request_sender.cc
new file mode 100644
index 0000000..a68df98
--- /dev/null
+++ b/src/updater_components/update_client/request_sender.cc
@@ -0,0 +1,204 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/request_sender.h"
+
+#include <utility>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/client_update_protocol/ecdsa.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/network.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+
+namespace update_client {
+
+namespace {
+
+// This is an ECDSA prime256v1 named-curve key.
+constexpr int kKeyVersion = 9;
+const char kKeyPubBytesBase64[] =
+    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsVwVMmIJaWBjktSx9m1JrZWYBvMm"
+    "bsrGGQPhScDtao+DloD871YmEeunAaQvRMZgDh1nCaWkVG6wo75+yDbKDA==";
+
+}  // namespace
+
+RequestSender::RequestSender(scoped_refptr<Configurator> config)
+    : config_(config) {}
+
+RequestSender::~RequestSender() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void RequestSender::Send(
+    const std::vector<GURL>& urls,
+    const base::flat_map<std::string, std::string>& request_extra_headers,
+    const std::string& request_body,
+    bool use_signing,
+    RequestSenderCallback request_sender_callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  urls_ = urls;
+  request_extra_headers_ = request_extra_headers;
+  request_body_ = request_body;
+  use_signing_ = use_signing;
+  request_sender_callback_ = std::move(request_sender_callback);
+
+  if (urls_.empty()) {
+    return HandleSendError(static_cast<int>(ProtocolError::MISSING_URLS), 0);
+  }
+
+  cur_url_ = urls_.begin();
+
+  if (use_signing_) {
+    public_key_ = GetKey(kKeyPubBytesBase64);
+    if (public_key_.empty())
+      return HandleSendError(
+          static_cast<int>(ProtocolError::MISSING_PUBLIC_KEY), 0);
+  }
+
+  SendInternal();
+}
+
+void RequestSender::SendInternal() {
+  DCHECK(cur_url_ != urls_.end());
+  DCHECK(cur_url_->is_valid());
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  GURL url(*cur_url_);
+
+  if (use_signing_) {
+    DCHECK(!public_key_.empty());
+    signer_ = client_update_protocol::Ecdsa::Create(kKeyVersion, public_key_);
+    std::string request_query_string;
+    signer_->SignRequest(request_body_, &request_query_string);
+
+    url = BuildUpdateUrl(url, request_query_string);
+  }
+
+  network_fetcher_ = config_->GetNetworkFetcherFactory()->Create();
+  if (!network_fetcher_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&RequestSender::SendInternalComplete,
+                       base::Unretained(this),
+                       static_cast<int>(ProtocolError::URL_FETCHER_FAILED),
+                       std::string(), std::string(), 0));
+  }
+  network_fetcher_->PostRequest(
+      url, request_body_, request_extra_headers_,
+      base::BindOnce(&RequestSender::OnResponseStarted, base::Unretained(this)),
+      base::BindRepeating([](int64_t current) {}),
+      base::BindOnce(&RequestSender::OnNetworkFetcherComplete,
+                     base::Unretained(this), url));
+}
+
+void RequestSender::SendInternalComplete(int error,
+                                         const std::string& response_body,
+                                         const std::string& response_etag,
+                                         int retry_after_sec) {
+  if (!error) {
+    if (!use_signing_) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(request_sender_callback_), 0,
+                                    response_body, retry_after_sec));
+      return;
+    }
+
+    DCHECK(use_signing_);
+    DCHECK(signer_);
+    if (signer_->ValidateResponse(response_body, response_etag)) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(request_sender_callback_), 0,
+                                    response_body, retry_after_sec));
+      return;
+    }
+
+    error = static_cast<int>(ProtocolError::RESPONSE_NOT_TRUSTED);
+  }
+
+  DCHECK(error);
+
+  // A positive |retry_after_sec| is a hint from the server that the client
+  // should not send further request until the cooldown has expired.
+  if (retry_after_sec <= 0 && ++cur_url_ != urls_.end() &&
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&RequestSender::SendInternal,
+                                    base::Unretained(this)))) {
+    return;
+  }
+
+  HandleSendError(error, retry_after_sec);
+}
+
+void RequestSender::OnResponseStarted(const GURL& final_url,
+                                      int response_code,
+                                      int64_t content_length) {
+  response_code_ = response_code;
+}
+
+void RequestSender::OnNetworkFetcherComplete(
+    const GURL& original_url,
+    std::unique_ptr<std::string> response_body,
+    int net_error,
+    const std::string& header_etag,
+    int64_t xheader_retry_after_sec) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  VLOG(1) << "request completed from url: " << original_url.spec();
+
+  int error = -1;
+  if (response_body && response_code_ == 200) {
+    DCHECK_EQ(0, net_error);
+    error = 0;
+  } else if (response_code_ != -1) {
+    error = response_code_;
+  } else {
+    error = net_error;
+  }
+
+  int retry_after_sec = -1;
+  if (original_url.SchemeIsCryptographic() && error > 0)
+    retry_after_sec = base::saturated_cast<int>(xheader_retry_after_sec);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&RequestSender::SendInternalComplete,
+                                base::Unretained(this), error,
+                                response_body ? *response_body : std::string(),
+                                header_etag, retry_after_sec));
+}
+
+void RequestSender::HandleSendError(int error, int retry_after_sec) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(request_sender_callback_), error,
+                                std::string(), retry_after_sec));
+}
+
+std::string RequestSender::GetKey(const char* key_bytes_base64) {
+  std::string result;
+  return base::Base64Decode(std::string(key_bytes_base64), &result)
+             ? result
+             : std::string();
+}
+
+GURL RequestSender::BuildUpdateUrl(const GURL& url,
+                                   const std::string& query_params) {
+  const std::string query_string(
+      url.has_query() ? base::StringPrintf("%s&%s", url.query().c_str(),
+                                           query_params.c_str())
+                      : query_params);
+  GURL::Replacements replacements;
+  replacements.SetQueryStr(query_string);
+
+  return url.ReplaceComponents(replacements);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/request_sender.h b/src/updater_components/update_client/request_sender.h
new file mode 100644
index 0000000..7b9e21b
--- /dev/null
+++ b/src/updater_components/update_client/request_sender.h
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_
+#define COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#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 "url/gurl.h"
+
+namespace client_update_protocol {
+class Ecdsa;
+}
+
+namespace update_client {
+
+class Configurator;
+class NetworkFetcher;
+
+// Sends a request to one of the urls provided. The class implements a chain
+// of responsibility design pattern, where the urls are tried in the order they
+// are specified, until the request to one of them succeeds or all have failed.
+// CUP signing is optional.
+class RequestSender {
+ public:
+  // If |error| is 0, then the response is provided in the |response| parameter.
+  // |retry_after_sec| contains the value of the X-Retry-After response header,
+  // when the response was received from a cryptographically secure URL. The
+  // range for this value is [-1, 86400]. If |retry_after_sec| is -1 it means
+  // that the header could not be found, or trusted, or had an invalid value.
+  // The upper bound represents a delay of one day.
+  using RequestSenderCallback = base::OnceCallback<
+      void(int error, const std::string& response, int retry_after_sec)>;
+
+  explicit RequestSender(scoped_refptr<Configurator> config);
+  ~RequestSender();
+
+  // |use_signing| enables CUP signing of protocol messages exchanged using
+  // this class. |is_foreground| controls the presence and the value for the
+  // X-GoogleUpdate-Interactvity header serialized in the protocol request.
+  // If this optional parameter is set, the values of "fg" or "bg" are sent
+  // for true or false values of this parameter. Otherwise the header is not
+  // sent at all.
+  void Send(
+      const std::vector<GURL>& urls,
+      const base::flat_map<std::string, std::string>& request_extra_headers,
+      const std::string& request_body,
+      bool use_signing,
+      RequestSenderCallback request_sender_callback);
+
+ private:
+  // Combines the |url| and |query_params| parameters.
+  static GURL BuildUpdateUrl(const GURL& url, const std::string& query_params);
+
+  // Decodes and returns the public key used by CUP.
+  static std::string GetKey(const char* key_bytes_base64);
+
+  void OnResponseStarted(const GURL& final_url,
+                         int response_code,
+                         int64_t content_length);
+
+  void OnNetworkFetcherComplete(const GURL& original_url,
+                                std::unique_ptr<std::string> response_body,
+                                int net_error,
+                                const std::string& header_etag,
+                                int64_t xheader_retry_after_sec);
+
+  // Implements the error handling and url fallback mechanism.
+  void SendInternal();
+
+  // Called when SendInternal completes. |response_body| and |response_etag|
+  // contain the body and the etag associated with the HTTP response.
+  void SendInternalComplete(int error,
+                            const std::string& response_body,
+                            const std::string& response_etag,
+                            int retry_after_sec);
+
+  // Helper function to handle a non-continuable error in Send.
+  void HandleSendError(int error, int retry_after_sec);
+
+  base::ThreadChecker thread_checker_;
+
+  const scoped_refptr<Configurator> config_;
+
+  std::vector<GURL> urls_;
+  base::flat_map<std::string, std::string> request_extra_headers_;
+  std::string request_body_;
+  bool use_signing_ = false;  // True if CUP signing is used.
+  RequestSenderCallback request_sender_callback_;
+
+  std::string public_key_;
+  std::vector<GURL>::const_iterator cur_url_;
+  std::unique_ptr<NetworkFetcher> network_fetcher_;
+  std::unique_ptr<client_update_protocol::Ecdsa> signer_;
+
+  int response_code_ = -1;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestSender);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_
diff --git a/src/updater_components/update_client/request_sender_unittest.cc b/src/updater_components/update_client/request_sender_unittest.cc
new file mode 100644
index 0000000..cb0e09c
--- /dev/null
+++ b/src/updater_components/update_client/request_sender_unittest.cc
@@ -0,0 +1,261 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/request_sender.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string_util.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/update_client/net/url_loader_post_interceptor.h"
+#include "components/update_client/test_configurator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+namespace {
+
+const char kUrl1[] = "https://localhost2/path1";
+const char kUrl2[] = "https://localhost2/path2";
+
+// TODO(sorin): refactor as a utility function for unit tests.
+base::FilePath test_file(const char* file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("update_client")
+      .AppendASCII(file);
+}
+
+}  // namespace
+
+class RequestSenderTest : public testing::Test,
+                          public ::testing::WithParamInterface<bool> {
+ public:
+  RequestSenderTest();
+  ~RequestSenderTest() override;
+
+  // Overrides from testing::Test.
+  void SetUp() override;
+  void TearDown() override;
+
+  void RequestSenderComplete(int error,
+                             const std::string& response,
+                             int retry_after_sec);
+
+ protected:
+  void Quit();
+  void RunThreads();
+
+  base::test::TaskEnvironment task_environment_;
+
+  scoped_refptr<TestConfigurator> config_;
+  std::unique_ptr<RequestSender> request_sender_;
+
+  std::unique_ptr<URLLoaderPostInterceptor> post_interceptor_;
+
+  int error_ = 0;
+  std::string response_;
+
+ private:
+  base::OnceClosure quit_closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestSenderTest);
+};
+
+INSTANTIATE_TEST_SUITE_P(IsForeground, RequestSenderTest, ::testing::Bool());
+
+RequestSenderTest::RequestSenderTest()
+    : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}
+
+RequestSenderTest::~RequestSenderTest() {}
+
+void RequestSenderTest::SetUp() {
+  config_ = base::MakeRefCounted<TestConfigurator>();
+  request_sender_ = std::make_unique<RequestSender>(config_);
+
+  std::vector<GURL> urls;
+  urls.push_back(GURL(kUrl1));
+  urls.push_back(GURL(kUrl2));
+
+  post_interceptor_ = std::make_unique<URLLoaderPostInterceptor>(
+      urls, config_->test_url_loader_factory());
+  EXPECT_TRUE(post_interceptor_);
+}
+
+void RequestSenderTest::TearDown() {
+  request_sender_ = nullptr;
+
+  post_interceptor_.reset();
+
+  // Run the threads until they are idle to allow the clean up
+  // of the network interceptors on the IO thread.
+  task_environment_.RunUntilIdle();
+  config_ = nullptr;
+}
+
+void RequestSenderTest::RunThreads() {
+  base::RunLoop runloop;
+  quit_closure_ = runloop.QuitClosure();
+  runloop.Run();
+}
+
+void RequestSenderTest::Quit() {
+  if (!quit_closure_.is_null())
+    std::move(quit_closure_).Run();
+}
+
+void RequestSenderTest::RequestSenderComplete(int error,
+                                              const std::string& response,
+                                              int retry_after_sec) {
+  error_ = error;
+  response_ = response;
+
+  Quit();
+}
+
+// Tests that when a request to the first url succeeds, the subsequent urls are
+// not tried.
+TEST_P(RequestSenderTest, RequestSendSuccess) {
+  EXPECT_TRUE(
+      post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test"),
+                                       test_file("updatecheck_reply_1.json")));
+
+  const bool is_foreground = GetParam();
+  request_sender_->Send(
+      {GURL(kUrl1), GURL(kUrl2)},
+      {{"X-Goog-Update-Interactivity", is_foreground ? "fg" : "bg"}}, "test",
+      false,
+      base::BindOnce(&RequestSenderTest::RequestSenderComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  EXPECT_EQ(0, post_interceptor_->GetHitCountForURL(GURL(kUrl2)))
+      << post_interceptor_->GetRequestsAsString();
+
+  // Sanity check the request.
+  EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
+
+  // Check the response post conditions.
+  EXPECT_EQ(0, error_);
+  EXPECT_EQ(419ul, response_.size());
+
+  // Check the interactivity header value.
+  const auto extra_request_headers =
+      std::get<1>(post_interceptor_->GetRequests()[0]);
+  EXPECT_TRUE(extra_request_headers.HasHeader("X-Goog-Update-Interactivity"));
+  std::string header;
+  extra_request_headers.GetHeader("X-Goog-Update-Interactivity", &header);
+  EXPECT_STREQ(is_foreground ? "fg" : "bg", header.c_str());
+}
+
+// Tests that the request succeeds using the second url after the first url
+// has failed.
+TEST_F(RequestSenderTest, RequestSendSuccessWithFallback) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN));
+  EXPECT_TRUE(
+      post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test")));
+
+  request_sender_->Send(
+      {GURL(kUrl1), GURL(kUrl2)}, {}, "test", false,
+      base::BindOnce(&RequestSenderTest::RequestSenderComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(2, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(2, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl1)))
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl2)))
+      << post_interceptor_->GetRequestsAsString();
+
+  EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
+  EXPECT_STREQ("test", post_interceptor_->GetRequestBody(1).c_str());
+  EXPECT_EQ(0, error_);
+}
+
+// Tests that the request fails when both urls have failed.
+TEST_F(RequestSenderTest, RequestSendFailed) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN));
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN));
+
+  const std::vector<GURL> urls = {GURL(kUrl1), GURL(kUrl2)};
+  request_sender_ = std::make_unique<RequestSender>(config_);
+  request_sender_->Send(
+      urls, {}, "test", false,
+      base::BindOnce(&RequestSenderTest::RequestSenderComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(2, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(2, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl1)))
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl2)))
+      << post_interceptor_->GetRequestsAsString();
+
+  EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
+  EXPECT_STREQ("test", post_interceptor_->GetRequestBody(1).c_str());
+  EXPECT_EQ(403, error_);
+}
+
+// Tests that the request fails when no urls are provided.
+TEST_F(RequestSenderTest, RequestSendFailedNoUrls) {
+  std::vector<GURL> urls;
+  request_sender_ = std::make_unique<RequestSender>(config_);
+  request_sender_->Send(
+      urls, {}, "test", false,
+      base::BindOnce(&RequestSenderTest::RequestSenderComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(-10002, error_);
+}
+
+// Tests that a CUP request fails if the response is not signed.
+TEST_F(RequestSenderTest, RequestSendCupError) {
+  EXPECT_TRUE(
+      post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test"),
+                                       test_file("updatecheck_reply_1.json")));
+
+  const std::vector<GURL> urls = {GURL(kUrl1)};
+  request_sender_ = std::make_unique<RequestSender>(config_);
+  request_sender_->Send(
+      urls, {}, "test", true,
+      base::BindOnce(&RequestSenderTest::RequestSenderComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
+  EXPECT_EQ(-10000, error_);
+  EXPECT_TRUE(response_.empty());
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/task.h b/src/updater_components/update_client/task.h
new file mode 100644
index 0000000..e9b480e
--- /dev/null
+++ b/src/updater_components/update_client/task.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TASK_H_
+#define COMPONENTS_UPDATE_CLIENT_TASK_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace update_client {
+
+// Defines an abstraction for a unit of work done by the update client.
+// Each invocation of the update client API results in a task being created and
+// run. In most cases, a task corresponds to a set of CRXs, which are updated
+// together.
+class Task : public base::RefCounted<Task> {
+ public:
+  Task() = default;
+  virtual void Run() = 0;
+
+  // Does a best effort attempt to make a task release its resources and stop
+  // soon. It is possible that a running task may complete even if this
+  // method is called.
+  virtual void Cancel() = 0;
+
+  // Returns the ids corresponding to the CRXs associated with this update task.
+  virtual std::vector<std::string> GetIds() const = 0;
+
+ protected:
+  friend class base::RefCounted<Task>;
+  virtual ~Task() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Task);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_TASK_H_
diff --git a/src/updater_components/update_client/task_send_uninstall_ping.cc b/src/updater_components/update_client/task_send_uninstall_ping.cc
new file mode 100644
index 0000000..018bb1d
--- /dev/null
+++ b/src/updater_components/update_client/task_send_uninstall_ping.cc
@@ -0,0 +1,64 @@
+// 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 "components/update_client/task_send_uninstall_ping.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/version.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_engine.h"
+
+namespace update_client {
+
+TaskSendUninstallPing::TaskSendUninstallPing(
+    scoped_refptr<UpdateEngine> update_engine,
+    const std::string& id,
+    const base::Version& version,
+    int reason,
+    Callback callback)
+    : update_engine_(update_engine),
+      id_(id),
+      version_(version),
+      reason_(reason),
+      callback_(std::move(callback)) {}
+
+TaskSendUninstallPing::~TaskSendUninstallPing() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void TaskSendUninstallPing::Run() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (id_.empty()) {
+    TaskComplete(Error::INVALID_ARGUMENT);
+    return;
+  }
+
+  update_engine_->SendUninstallPing(
+      id_, version_, reason_,
+      base::BindOnce(&TaskSendUninstallPing::TaskComplete, this));
+}
+
+void TaskSendUninstallPing::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  TaskComplete(Error::UPDATE_CANCELED);
+}
+
+std::vector<std::string> TaskSendUninstallPing::GetIds() const {
+  return std::vector<std::string>{id_};
+}
+
+void TaskSendUninstallPing::TaskComplete(Error error) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback_), scoped_refptr<Task>(this), error));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/task_send_uninstall_ping.h b/src/updater_components/update_client/task_send_uninstall_ping.h
new file mode 100644
index 0000000..9367b3d
--- /dev/null
+++ b/src/updater_components/update_client/task_send_uninstall_ping.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TASK_SEND_UNINSTALL_PING_H_
+#define COMPONENTS_UPDATE_CLIENT_TASK_SEND_UNINSTALL_PING_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/task.h"
+#include "components/update_client/update_client.h"
+
+namespace base {
+class Version;
+}
+
+namespace update_client {
+
+class UpdateEngine;
+enum class Error;
+
+// Defines a specialized task for sending the uninstall ping.
+class TaskSendUninstallPing : public Task {
+ public:
+  using Callback =
+      base::OnceCallback<void(scoped_refptr<Task> task, Error error)>;
+
+  // |update_engine| is injected here to handle the task.
+  // |id| represents the CRX to send the ping for.
+  // |callback| is called to return the execution flow back to creator of
+  //    this task when the task is done.
+  TaskSendUninstallPing(scoped_refptr<UpdateEngine> update_engine,
+                        const std::string& id,
+                        const base::Version& version,
+                        int reason,
+                        Callback callback);
+
+  void Run() override;
+
+  void Cancel() override;
+
+  std::vector<std::string> GetIds() const override;
+
+ private:
+  ~TaskSendUninstallPing() override;
+
+  // Called when the task has completed either because the task has run or
+  // it has been canceled.
+  void TaskComplete(Error error);
+
+  base::ThreadChecker thread_checker_;
+  scoped_refptr<UpdateEngine> update_engine_;
+  const std::string id_;
+  const base::Version version_;
+  int reason_;
+  Callback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSendUninstallPing);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_TASK_SEND_UNINSTALL_PING_H_
diff --git a/src/updater_components/update_client/task_traits.h b/src/updater_components/update_client/task_traits.h
new file mode 100644
index 0000000..1f675e7
--- /dev/null
+++ b/src/updater_components/update_client/task_traits.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TASK_TRAITS_H_
+#define COMPONENTS_UPDATE_CLIENT_TASK_TRAITS_H_
+
+#include "base/task/task_traits.h"
+
+namespace update_client {
+
+constexpr base::TaskTraits kTaskTraits = {
+    base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+    base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
+
+constexpr base::TaskTraits kTaskTraitsBackgroundDownloader = {
+    base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+    base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
+
+// This task joins a process, hence .WithBaseSyncPrimitives().
+constexpr base::TaskTraits kTaskTraitsRunCommand = {
+    base::ThreadPool(), base::MayBlock(), base::WithBaseSyncPrimitives(),
+    base::TaskPriority::BEST_EFFORT,
+    base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_TASK_TRAITS_H_
diff --git a/src/updater_components/update_client/task_update.cc b/src/updater_components/update_client/task_update.cc
new file mode 100644
index 0000000..ab445b5
--- /dev/null
+++ b/src/updater_components/update_client/task_update.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "components/update_client/task_update.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_engine.h"
+
+namespace update_client {
+
+TaskUpdate::TaskUpdate(scoped_refptr<UpdateEngine> update_engine,
+                       bool is_foreground,
+                       const std::vector<std::string>& ids,
+                       UpdateClient::CrxDataCallback crx_data_callback,
+                       Callback callback)
+    : update_engine_(update_engine),
+      is_foreground_(is_foreground),
+      ids_(ids),
+      crx_data_callback_(std::move(crx_data_callback)),
+      callback_(std::move(callback)) {}
+
+TaskUpdate::~TaskUpdate() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void TaskUpdate::Run() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (ids_.empty()) {
+    TaskComplete(Error::INVALID_ARGUMENT);
+    return;
+  }
+
+  update_engine_->Update(is_foreground_, ids_, std::move(crx_data_callback_),
+                         base::BindOnce(&TaskUpdate::TaskComplete, this));
+}
+
+void TaskUpdate::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  TaskComplete(Error::UPDATE_CANCELED);
+}
+
+std::vector<std::string> TaskUpdate::GetIds() const {
+  return ids_;
+}
+
+void TaskUpdate::TaskComplete(Error error) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback_),
+                                scoped_refptr<TaskUpdate>(this), error));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/task_update.h b/src/updater_components/update_client/task_update.h
new file mode 100644
index 0000000..4df796b
--- /dev/null
+++ b/src/updater_components/update_client/task_update.h
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_
+#define COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/task.h"
+#include "components/update_client/update_client.h"
+
+namespace update_client {
+
+class UpdateEngine;
+enum class Error;
+
+// Defines a specialized task for updating a group of CRXs.
+class TaskUpdate : public Task {
+ public:
+  using Callback =
+      base::OnceCallback<void(scoped_refptr<Task> task, Error error)>;
+
+  // |update_engine| is injected here to handle the task.
+  // |is_foreground| is true when the update task is initiated by the user.
+  // |ids| represents the CRXs to be updated by this task.
+  // |crx_data_callback| is called to get update data for the these CRXs.
+  // |callback| is called to return the execution flow back to creator of
+  //    this task when the task is done.
+  TaskUpdate(scoped_refptr<UpdateEngine> update_engine,
+             bool is_foreground,
+             const std::vector<std::string>& ids,
+             UpdateClient::CrxDataCallback crx_data_callback,
+             Callback callback);
+
+  void Run() override;
+
+  void Cancel() override;
+
+  std::vector<std::string> GetIds() const override;
+
+ private:
+  ~TaskUpdate() override;
+
+  // Called when the task has completed either because the task has run or
+  // it has been canceled.
+  void TaskComplete(Error error);
+
+  base::ThreadChecker thread_checker_;
+  scoped_refptr<UpdateEngine> update_engine_;
+  const bool is_foreground_;
+  const std::vector<std::string> ids_;
+  UpdateClient::CrxDataCallback crx_data_callback_;
+  Callback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskUpdate);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_TASK_UPDATE_H_
diff --git a/src/updater_components/update_client/test_configurator.cc b/src/updater_components/update_client/test_configurator.cc
new file mode 100644
index 0000000..9365726
--- /dev/null
+++ b/src/updater_components/update_client/test_configurator.cc
@@ -0,0 +1,217 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/test_configurator.h"
+
+#include <utility>
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/version.h"
+#include "components/prefs/pref_service.h"
+#include "components/services/patch/in_process_file_patcher.h"
+#include "components/services/unzip/in_process_unzipper.h"
+#include "components/update_client/activity_data_service.h"
+#include "components/update_client/net/network_chromium.h"
+#include "components/update_client/patch/patch_impl.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/protocol_handler.h"
+#include "components/update_client/unzip/unzip_impl.h"
+#include "components/update_client/unzipper.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+namespace {
+
+std::vector<GURL> MakeDefaultUrls() {
+  std::vector<GURL> urls;
+  urls.push_back(GURL(POST_INTERCEPT_SCHEME
+                      "://" POST_INTERCEPT_HOSTNAME POST_INTERCEPT_PATH));
+  return urls;
+}
+
+}  // namespace
+
+TestConfigurator::TestConfigurator()
+    : brand_("TEST"),
+      initial_time_(0),
+      ondemand_time_(0),
+      enabled_cup_signing_(false),
+      enabled_component_updates_(true),
+      unzip_factory_(base::MakeRefCounted<update_client::UnzipChromiumFactory>(
+          base::BindRepeating(&unzip::LaunchInProcessUnzipper))),
+      patch_factory_(base::MakeRefCounted<update_client::PatchChromiumFactory>(
+          base::BindRepeating(&patch::LaunchInProcessFilePatcher))),
+      test_shared_loader_factory_(
+          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+              &test_url_loader_factory_)),
+      network_fetcher_factory_(
+          base::MakeRefCounted<NetworkFetcherChromiumFactory>(
+              test_shared_loader_factory_)) {}
+
+TestConfigurator::~TestConfigurator() {
+}
+
+int TestConfigurator::InitialDelay() const {
+  return initial_time_;
+}
+
+int TestConfigurator::NextCheckDelay() const {
+  return 1;
+}
+
+int TestConfigurator::OnDemandDelay() const {
+  return ondemand_time_;
+}
+
+int TestConfigurator::UpdateDelay() const {
+  return 1;
+}
+
+std::vector<GURL> TestConfigurator::UpdateUrl() const {
+  if (!update_check_url_.is_empty())
+    return std::vector<GURL>(1, update_check_url_);
+
+  return MakeDefaultUrls();
+}
+
+std::vector<GURL> TestConfigurator::PingUrl() const {
+  if (!ping_url_.is_empty())
+    return std::vector<GURL>(1, ping_url_);
+
+  return UpdateUrl();
+}
+
+std::string TestConfigurator::GetProdId() const {
+  return "fake_prodid";
+}
+
+base::Version TestConfigurator::GetBrowserVersion() const {
+  // Needs to be larger than the required version in tested component manifests.
+  return base::Version("30.0");
+}
+
+std::string TestConfigurator::GetChannel() const {
+  return "fake_channel_string";
+}
+
+std::string TestConfigurator::GetBrand() const {
+  return brand_;
+}
+
+std::string TestConfigurator::GetLang() const {
+  return "fake_lang";
+}
+
+std::string TestConfigurator::GetOSLongName() const {
+  return "Fake Operating System";
+}
+
+base::flat_map<std::string, std::string> TestConfigurator::ExtraRequestParams()
+    const {
+  return {{"extra", "foo"}};
+}
+
+std::string TestConfigurator::GetDownloadPreference() const {
+  return download_preference_;
+}
+
+scoped_refptr<NetworkFetcherFactory>
+TestConfigurator::GetNetworkFetcherFactory() {
+  return network_fetcher_factory_;
+}
+
+scoped_refptr<UnzipperFactory> TestConfigurator::GetUnzipperFactory() {
+  return unzip_factory_;
+}
+
+scoped_refptr<PatcherFactory> TestConfigurator::GetPatcherFactory() {
+  return patch_factory_;
+}
+
+bool TestConfigurator::EnabledDeltas() const {
+  return true;
+}
+
+bool TestConfigurator::EnabledComponentUpdates() const {
+  return enabled_component_updates_;
+}
+
+bool TestConfigurator::EnabledBackgroundDownloader() const {
+  return false;
+}
+
+bool TestConfigurator::EnabledCupSigning() const {
+  return enabled_cup_signing_;
+}
+
+void TestConfigurator::SetBrand(const std::string& brand) {
+  brand_ = brand;
+}
+
+void TestConfigurator::SetOnDemandTime(int seconds) {
+  ondemand_time_ = seconds;
+}
+
+void TestConfigurator::SetInitialDelay(int seconds) {
+  initial_time_ = seconds;
+}
+
+void TestConfigurator::SetEnabledCupSigning(bool enabled_cup_signing) {
+  enabled_cup_signing_ = enabled_cup_signing;
+}
+
+void TestConfigurator::SetEnabledComponentUpdates(
+    bool enabled_component_updates) {
+  enabled_component_updates_ = enabled_component_updates;
+}
+
+void TestConfigurator::SetDownloadPreference(
+    const std::string& download_preference) {
+  download_preference_ = download_preference;
+}
+
+void TestConfigurator::SetUpdateCheckUrl(const GURL& url) {
+  update_check_url_ = url;
+}
+
+void TestConfigurator::SetPingUrl(const GURL& url) {
+  ping_url_ = url;
+}
+
+void TestConfigurator::SetAppGuid(const std::string& app_guid) {
+  app_guid_ = app_guid;
+}
+
+PrefService* TestConfigurator::GetPrefService() const {
+  return nullptr;
+}
+
+ActivityDataService* TestConfigurator::GetActivityDataService() const {
+  return nullptr;
+}
+
+bool TestConfigurator::IsPerUserInstall() const {
+  return true;
+}
+
+std::vector<uint8_t> TestConfigurator::GetRunActionKeyHash() const {
+  return std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash));
+}
+
+std::string TestConfigurator::GetAppGuid() const {
+  return app_guid_;
+}
+
+std::unique_ptr<ProtocolHandlerFactory>
+TestConfigurator::GetProtocolHandlerFactory() const {
+  return std::make_unique<ProtocolHandlerFactoryJSON>();
+}
+
+RecoveryCRXElevator TestConfigurator::GetRecoveryCRXElevator() const {
+  return {};
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/test_configurator.h b/src/updater_components/update_client/test_configurator.h
new file mode 100644
index 0000000..6935347
--- /dev/null
+++ b/src/updater_components/update_client/test_configurator.h
@@ -0,0 +1,146 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TEST_CONFIGURATOR_H_
+#define COMPONENTS_UPDATE_CLIENT_TEST_CONFIGURATOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/configurator.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "url/gurl.h"
+
+class PrefService;
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace update_client {
+
+class ActivityDataService;
+class NetworkFetcherFactory;
+class PatchChromiumFactory;
+class ProtocolHandlerFactory;
+class UnzipChromiumFactory;
+
+#define POST_INTERCEPT_SCHEME "https"
+#define POST_INTERCEPT_HOSTNAME "localhost2"
+#define POST_INTERCEPT_PATH "/update2"
+
+// component 1 has extension id "jebgalgnebhfojomionfpkfelancnnkf", and
+// the RSA public key the following hash:
+const uint8_t jebg_hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec,
+                             0x8e, 0xd5, 0xfa, 0x54, 0xb0, 0xd2, 0xdd, 0xa5,
+                             0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4,
+                             0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
+// component 1 public key (base64 encoded):
+const char jebg_public_key[] =
+    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68bW8i/RzSaeXOcNLuBw0SP9+1bdo5ysLqH"
+    "qfLqZs6XyJWEyL0U6f1axPR6LwViku21kgdc6PI524eb8Cr+a/iXGgZ8SdvZTcfQ/g/ukwlblF"
+    "mtqYfDoVpz03U8rDQ9b6DxeJBF4r48TNlFORggrAiNR26qbf1i178Au12AzWtwIDAQAB";
+// component 2 has extension id "abagagagagagagagagagagagagagagag", and
+// the RSA public key the following hash:
+const uint8_t abag_hash[] = {0x01, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+                             0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+                             0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+                             0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x01};
+// component 3 has extension id "ihfokbkgjpifnbbojhneepfflplebdkc", and
+// the RSA public key the following hash:
+const uint8_t ihfo_hash[] = {0x87, 0x5e, 0xa1, 0xa6, 0x9f, 0x85, 0xd1, 0x1e,
+                             0x97, 0xd4, 0x4f, 0x55, 0xbf, 0xb4, 0x13, 0xa2,
+                             0xe7, 0xc5, 0xc8, 0xf5, 0x60, 0x19, 0x78, 0x1b,
+                             0x6d, 0xe9, 0x4c, 0xeb, 0x96, 0x05, 0x42, 0x17};
+
+// runaction_test_win.crx and its payload id: gjpmebpgbhcamgdgjcmnjfhggjpgcimm
+const uint8_t gjpm_hash[] = {0x69, 0xfc, 0x41, 0xf6, 0x17, 0x20, 0xc6, 0x36,
+                             0x92, 0xcd, 0x95, 0x76, 0x69, 0xf6, 0x28, 0xcc,
+                             0xbe, 0x98, 0x4b, 0x93, 0x17, 0xd6, 0x9c, 0xb3,
+                             0x64, 0x0c, 0x0d, 0x25, 0x61, 0xc5, 0x80, 0x1d};
+
+class TestConfigurator : public Configurator {
+ public:
+  TestConfigurator();
+
+  // Overrrides for 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<NetworkFetcherFactory> GetNetworkFetcherFactory() override;
+  scoped_refptr<UnzipperFactory> GetUnzipperFactory() override;
+  scoped_refptr<PatcherFactory> GetPatcherFactory() override;
+  bool EnabledDeltas() const override;
+  bool EnabledComponentUpdates() const override;
+  bool EnabledBackgroundDownloader() const override;
+  bool EnabledCupSigning() const override;
+  PrefService* GetPrefService() const override;
+  ActivityDataService* GetActivityDataService() const override;
+  bool IsPerUserInstall() const override;
+  std::vector<uint8_t> GetRunActionKeyHash() const override;
+  std::string GetAppGuid() const override;
+  std::unique_ptr<ProtocolHandlerFactory> GetProtocolHandlerFactory()
+      const override;
+  RecoveryCRXElevator GetRecoveryCRXElevator() const override;
+
+  void SetBrand(const std::string& brand);
+  void SetOnDemandTime(int seconds);
+  void SetInitialDelay(int seconds);
+  void SetDownloadPreference(const std::string& download_preference);
+  void SetEnabledCupSigning(bool use_cup_signing);
+  void SetEnabledComponentUpdates(bool enabled_component_updates);
+  void SetUpdateCheckUrl(const GURL& url);
+  void SetPingUrl(const GURL& url);
+  void SetAppGuid(const std::string& app_guid);
+  network::TestURLLoaderFactory* test_url_loader_factory() {
+    return &test_url_loader_factory_;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<TestConfigurator>;
+  ~TestConfigurator() override;
+
+  class TestPatchService;
+
+  std::string brand_;
+  int initial_time_;
+  int ondemand_time_;
+  std::string download_preference_;
+  bool enabled_cup_signing_;
+  bool enabled_component_updates_;
+  GURL update_check_url_;
+  GURL ping_url_;
+  std::string app_guid_;
+
+  scoped_refptr<update_client::UnzipChromiumFactory> unzip_factory_;
+  scoped_refptr<update_client::PatchChromiumFactory> patch_factory_;
+
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<NetworkFetcherFactory> network_fetcher_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestConfigurator);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_TEST_CONFIGURATOR_H_
diff --git a/src/updater_components/update_client/test_installer.cc b/src/updater_components/update_client/test_installer.cc
new file mode 100644
index 0000000..0285af0
--- /dev/null
+++ b/src/updater_components/update_client/test_installer.cc
@@ -0,0 +1,110 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/test_installer.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/values.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+TestInstaller::TestInstaller() : error_(0), install_count_(0) {
+}
+
+TestInstaller::~TestInstaller() {
+  // The unpack path is deleted unconditionally by the component state code,
+  // which is driving this installer. Therefore, the unpack path must not
+  // exist when this object is destroyed.
+  if (!unpack_path_.empty())
+    EXPECT_FALSE(base::DirectoryExists(unpack_path_));
+}
+
+void TestInstaller::OnUpdateError(int error) {
+  error_ = error;
+}
+
+void TestInstaller::Install(const base::FilePath& unpack_path,
+                            const std::string& /*public_key*/,
+                            Callback callback) {
+  ++install_count_;
+  unpack_path_ = unpack_path;
+
+  InstallComplete(std::move(callback), Result(InstallError::NONE));
+}
+
+void TestInstaller::InstallComplete(Callback callback,
+                                    const Result& result) const {
+  base::PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()},
+                 base::BindOnce(std::move(callback), result));
+}
+
+bool TestInstaller::GetInstalledFile(const std::string& file,
+                                     base::FilePath* installed_file) {
+  return false;
+}
+
+bool TestInstaller::Uninstall() {
+  return false;
+}
+
+ReadOnlyTestInstaller::ReadOnlyTestInstaller(const base::FilePath& install_dir)
+    : install_directory_(install_dir) {
+}
+
+ReadOnlyTestInstaller::~ReadOnlyTestInstaller() {
+}
+
+bool ReadOnlyTestInstaller::GetInstalledFile(const std::string& file,
+                                             base::FilePath* installed_file) {
+  *installed_file = install_directory_.AppendASCII(file);
+  return true;
+}
+
+VersionedTestInstaller::VersionedTestInstaller() {
+  base::CreateNewTempDirectory(FILE_PATH_LITERAL("TEST_"), &install_directory_);
+}
+
+VersionedTestInstaller::~VersionedTestInstaller() {
+  base::DeleteFile(install_directory_, true);
+}
+
+void VersionedTestInstaller::Install(const base::FilePath& unpack_path,
+                                     const std::string& public_key,
+                                     Callback callback) {
+  const auto manifest = update_client::ReadManifest(unpack_path);
+  std::string version_string;
+  manifest->GetStringASCII("version", &version_string);
+  const base::Version version(version_string);
+
+  const base::FilePath path =
+      install_directory_.AppendASCII(version.GetString());
+  base::CreateDirectory(path.DirName());
+  if (!base::Move(unpack_path, path)) {
+    InstallComplete(std::move(callback), Result(InstallError::GENERIC_ERROR));
+    return;
+  }
+  current_version_ = version;
+  ++install_count_;
+
+  InstallComplete(std::move(callback), Result(InstallError::NONE));
+}
+
+bool VersionedTestInstaller::GetInstalledFile(const std::string& file,
+                                              base::FilePath* installed_file) {
+  const base::FilePath path =
+      install_directory_.AppendASCII(current_version_.GetString());
+  *installed_file = path.Append(base::FilePath::FromUTF8Unsafe(file));
+  return true;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/test_installer.h b/src/updater_components/update_client/test_installer.h
new file mode 100644
index 0000000..a12baf9
--- /dev/null
+++ b/src/updater_components/update_client/test_installer.h
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
+#define COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "components/update_client/update_client.h"
+
+namespace update_client {
+
+// TODO(sorin): consider reducing the number of the installer mocks.
+// A TestInstaller is an installer that does nothing for installation except
+// increment a counter.
+class TestInstaller : public CrxInstaller {
+ public:
+  TestInstaller();
+
+  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;
+
+  int error() const { return error_; }
+
+  int install_count() const { return install_count_; }
+
+ protected:
+  ~TestInstaller() override;
+
+  void InstallComplete(Callback callback, const Result& result) const;
+
+  int error_;
+  int install_count_;
+
+ private:
+  // Contains the |unpack_path| argument of the Install call.
+  base::FilePath unpack_path_;
+};
+
+// A ReadOnlyTestInstaller is an installer that knows about files in an existing
+// directory. It will not write to the directory.
+class ReadOnlyTestInstaller : public TestInstaller {
+ public:
+  explicit ReadOnlyTestInstaller(const base::FilePath& installed_path);
+
+  bool GetInstalledFile(const std::string& file,
+                        base::FilePath* installed_file) override;
+
+ private:
+  ~ReadOnlyTestInstaller() override;
+
+  base::FilePath install_directory_;
+};
+
+// A VersionedTestInstaller is an installer that installs files into versioned
+// directories (e.g. somedir/25.23.89.141/<files>).
+class VersionedTestInstaller : public TestInstaller {
+ public:
+  VersionedTestInstaller();
+
+  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;
+
+ private:
+  ~VersionedTestInstaller() override;
+
+  base::FilePath install_directory_;
+  base::Version current_version_;
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_TEST_INSTALLER_H_
diff --git a/src/updater_components/update_client/unzip/unzip_impl.cc b/src/updater_components/update_client/unzip/unzip_impl.cc
new file mode 100644
index 0000000..1fa9058
--- /dev/null
+++ b/src/updater_components/update_client/unzip/unzip_impl.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 "components/update_client/unzip/unzip_impl.h"
+
+#include "components/services/unzip/public/cpp/unzip.h"
+
+namespace update_client {
+
+namespace {
+
+class UnzipperImpl : public Unzipper {
+ public:
+  explicit UnzipperImpl(UnzipChromiumFactory::Callback callback)
+      : callback_(std::move(callback)) {}
+
+  void Unzip(const base::FilePath& zip_file,
+             const base::FilePath& destination,
+             UnzipCompleteCallback callback) override {
+    unzip::Unzip(callback_.Run(), zip_file, destination, std::move(callback));
+  }
+
+ private:
+  const UnzipChromiumFactory::Callback callback_;
+};
+
+}  // namespace
+
+UnzipChromiumFactory::UnzipChromiumFactory(Callback callback)
+    : callback_(std::move(callback)) {}
+
+std::unique_ptr<Unzipper> UnzipChromiumFactory::Create() const {
+  return std::make_unique<UnzipperImpl>(callback_);
+}
+
+UnzipChromiumFactory::~UnzipChromiumFactory() = default;
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/unzip/unzip_impl.h b/src/updater_components/update_client/unzip/unzip_impl.h
new file mode 100644
index 0000000..bbf9156
--- /dev/null
+++ b/src/updater_components/update_client/unzip/unzip_impl.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 COMPONENTS_UPDATE_CLIENT_UNZIP_UNZIP_IMPL_H_
+#define COMPONENTS_UPDATE_CLIENT_UNZIP_UNZIP_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/services/unzip/public/mojom/unzipper.mojom.h"
+#include "components/update_client/unzipper.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+
+namespace update_client {
+
+class UnzipChromiumFactory : public UnzipperFactory {
+ public:
+  using Callback =
+      base::RepeatingCallback<mojo::PendingRemote<unzip::mojom::Unzipper>()>;
+  explicit UnzipChromiumFactory(Callback callback);
+
+  std::unique_ptr<Unzipper> Create() const override;
+
+ protected:
+  ~UnzipChromiumFactory() override;
+
+ private:
+  const Callback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnzipChromiumFactory);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UNZIP_UNZIP_IMPL_H_
diff --git a/src/updater_components/update_client/unzipper.h b/src/updater_components/update_client/unzipper.h
new file mode 100644
index 0000000..6aed58c
--- /dev/null
+++ b/src/updater_components/update_client/unzipper.h
@@ -0,0 +1,50 @@
+// 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 COMPONENTS_UPDATE_CLIENT_UNZIPPER_H_
+#define COMPONENTS_UPDATE_CLIENT_UNZIPPER_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace update_client {
+
+class Unzipper {
+ public:
+  using UnzipCompleteCallback = base::OnceCallback<void(bool success)>;
+
+  virtual ~Unzipper() = default;
+
+  virtual void Unzip(const base::FilePath& zip_file,
+                     const base::FilePath& destination,
+                     UnzipCompleteCallback callback) = 0;
+
+ protected:
+  Unzipper() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Unzipper);
+};
+
+class UnzipperFactory : public base::RefCountedThreadSafe<UnzipperFactory> {
+ public:
+  virtual std::unique_ptr<Unzipper> Create() const = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<UnzipperFactory>;
+  UnzipperFactory() = default;
+  virtual ~UnzipperFactory() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UnzipperFactory);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UNZIPPER_H_
diff --git a/src/updater_components/update_client/update_checker.cc b/src/updater_components/update_client/update_checker.cc
new file mode 100644
index 0000000..cfc2578
--- /dev/null
+++ b/src/updater_components/update_client/update_checker.cc
@@ -0,0 +1,301 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/update_checker.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/update_client/component.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/persisted_data.h"
+#include "components/update_client/protocol_definition.h"
+#include "components/update_client/protocol_handler.h"
+#include "components/update_client/protocol_serializer.h"
+#include "components/update_client/request_sender.h"
+#include "components/update_client/task_traits.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/updater_state.h"
+#include "components/update_client/utils.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+namespace {
+
+// Returns a sanitized version of the brand or an empty string otherwise.
+std::string SanitizeBrand(const std::string& brand) {
+  return IsValidBrand(brand) ? brand : std::string("");
+}
+
+// Returns true if at least one item requires network encryption.
+bool IsEncryptionRequired(const IdToComponentPtrMap& components) {
+  for (const auto& item : components) {
+    const auto& component = item.second;
+    if (component->crx_component() &&
+        component->crx_component()->requires_network_encryption)
+      return true;
+  }
+  return false;
+}
+
+// Filters invalid attributes from |installer_attributes|.
+using InstallerAttributesFlatMap = base::flat_map<std::string, std::string>;
+InstallerAttributesFlatMap SanitizeInstallerAttributes(
+    const InstallerAttributes& installer_attributes) {
+  InstallerAttributesFlatMap sanitized_attrs;
+  for (const auto& attr : installer_attributes) {
+    if (IsValidInstallerAttribute(attr))
+      sanitized_attrs.insert(attr);
+  }
+  return sanitized_attrs;
+}
+
+class UpdateCheckerImpl : public UpdateChecker {
+ public:
+  UpdateCheckerImpl(scoped_refptr<Configurator> config,
+                    PersistedData* metadata);
+  ~UpdateCheckerImpl() override;
+
+  // Overrides for UpdateChecker.
+  void CheckForUpdates(
+      const std::string& session_id,
+      const std::vector<std::string>& ids_checked,
+      const IdToComponentPtrMap& components,
+      const base::flat_map<std::string, std::string>& additional_attributes,
+      bool enabled_component_updates,
+      UpdateCheckCallback update_check_callback) override;
+
+ private:
+  void ReadUpdaterStateAttributes();
+  void CheckForUpdatesHelper(
+      const std::string& session_id,
+      const IdToComponentPtrMap& components,
+      const base::flat_map<std::string, std::string>& additional_attributes,
+      bool enabled_component_updates);
+  void OnRequestSenderComplete(int error,
+                               const std::string& response,
+                               int retry_after_sec);
+  void UpdateCheckSucceeded(const ProtocolParser::Results& results,
+                            int retry_after_sec);
+  void UpdateCheckFailed(ErrorCategory error_category,
+                         int error,
+                         int retry_after_sec);
+
+  base::ThreadChecker thread_checker_;
+
+  const scoped_refptr<Configurator> config_;
+  PersistedData* metadata_ = nullptr;
+  std::vector<std::string> ids_checked_;
+  UpdateCheckCallback update_check_callback_;
+  std::unique_ptr<UpdaterState::Attributes> updater_state_attributes_;
+  std::unique_ptr<RequestSender> request_sender_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateCheckerImpl);
+};
+
+UpdateCheckerImpl::UpdateCheckerImpl(scoped_refptr<Configurator> config,
+                                     PersistedData* metadata)
+    : config_(config), metadata_(metadata) {}
+
+UpdateCheckerImpl::~UpdateCheckerImpl() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void UpdateCheckerImpl::CheckForUpdates(
+    const std::string& session_id,
+    const std::vector<std::string>& ids_checked,
+    const IdToComponentPtrMap& components,
+    const base::flat_map<std::string, std::string>& additional_attributes,
+    bool enabled_component_updates,
+    UpdateCheckCallback update_check_callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  ids_checked_ = ids_checked;
+  update_check_callback_ = std::move(update_check_callback);
+
+  base::PostTaskAndReply(
+      FROM_HERE, kTaskTraits,
+      base::BindOnce(&UpdateCheckerImpl::ReadUpdaterStateAttributes,
+                     base::Unretained(this)),
+      base::BindOnce(&UpdateCheckerImpl::CheckForUpdatesHelper,
+                     base::Unretained(this), session_id, std::cref(components),
+                     additional_attributes, enabled_component_updates));
+}
+
+// This function runs on the blocking pool task runner.
+void UpdateCheckerImpl::ReadUpdaterStateAttributes() {
+#if defined(OS_WIN)
+  // On Windows, the Chrome and the updater install modes are matched by design.
+  updater_state_attributes_ =
+      UpdaterState::GetState(!config_->IsPerUserInstall());
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  // MacOS ignores this value in the current implementation but this may change.
+  updater_state_attributes_ = UpdaterState::GetState(false);
+#else
+// Other platforms don't have updaters.
+#endif  // OS_WIN
+}
+
+void UpdateCheckerImpl::CheckForUpdatesHelper(
+    const std::string& session_id,
+    const IdToComponentPtrMap& components,
+    const base::flat_map<std::string, std::string>& additional_attributes,
+    bool enabled_component_updates) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto urls(config_->UpdateUrl());
+  if (IsEncryptionRequired(components))
+    RemoveUnsecureUrls(&urls);
+
+  // Components in this update check are either all foreground, or all
+  // background since this member is inherited from the component's update
+  // context. Pick the state of the first component to use in the update check.
+  DCHECK(!components.empty());
+  const bool is_foreground = components.at(ids_checked_[0])->is_foreground();
+  DCHECK(
+      std::all_of(components.cbegin(), components.cend(),
+                  [is_foreground](IdToComponentPtrMap::const_reference& elem) {
+                    return is_foreground == elem.second->is_foreground();
+                  }));
+
+  std::vector<protocol_request::App> apps;
+  for (const auto& app_id : ids_checked_) {
+    DCHECK_EQ(1u, components.count(app_id));
+    const auto& component = components.at(app_id);
+    DCHECK_EQ(component->id(), app_id);
+    const auto& crx_component = component->crx_component();
+    DCHECK(crx_component);
+
+    std::string install_source;
+    if (!crx_component->install_source.empty())
+      install_source = crx_component->install_source;
+    else if (component->is_foreground())
+      install_source = "ondemand";
+
+    const bool is_update_disabled =
+        crx_component->supports_group_policy_enable_component_updates &&
+        !enabled_component_updates;
+
+    apps.push_back(MakeProtocolApp(
+        app_id, crx_component->version, SanitizeBrand(config_->GetBrand()),
+        install_source, crx_component->install_location,
+        crx_component->fingerprint,
+        SanitizeInstallerAttributes(crx_component->installer_attributes),
+        metadata_->GetCohort(app_id), metadata_->GetCohortName(app_id),
+        metadata_->GetCohortHint(app_id), crx_component->disabled_reasons,
+        MakeProtocolUpdateCheck(is_update_disabled),
+        MakeProtocolPing(app_id, metadata_)));
+  }
+
+  const auto request = MakeProtocolRequest(
+      session_id, config_->GetProdId(),
+      config_->GetBrowserVersion().GetString(), config_->GetLang(),
+      config_->GetChannel(), config_->GetOSLongName(),
+      config_->GetDownloadPreference(), additional_attributes,
+      updater_state_attributes_.get(), std::move(apps));
+
+  request_sender_ = std::make_unique<RequestSender>(config_);
+  request_sender_->Send(
+      urls,
+      BuildUpdateCheckExtraRequestHeaders(config_->GetProdId(),
+                                          config_->GetBrowserVersion(),
+                                          ids_checked_, is_foreground),
+      config_->GetProtocolHandlerFactory()->CreateSerializer()->Serialize(
+          request),
+      config_->EnabledCupSigning(),
+      base::BindOnce(&UpdateCheckerImpl::OnRequestSenderComplete,
+                     base::Unretained(this)));
+}
+
+void UpdateCheckerImpl::OnRequestSenderComplete(
+    int error,
+    const std::string& response,
+    int retry_after_sec) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (error) {
+    VLOG(1) << "RequestSender failed " << error;
+    UpdateCheckFailed(ErrorCategory::kUpdateCheck, error, retry_after_sec);
+    return;
+  }
+
+  auto parser = config_->GetProtocolHandlerFactory()->CreateParser();
+  if (!parser->Parse(response)) {
+    VLOG(1) << "Parse failed " << parser->errors();
+    UpdateCheckFailed(ErrorCategory::kUpdateCheck,
+                      static_cast<int>(ProtocolError::PARSE_FAILED),
+                      retry_after_sec);
+    return;
+  }
+
+  DCHECK_EQ(0, error);
+  UpdateCheckSucceeded(parser->results(), retry_after_sec);
+}
+
+void UpdateCheckerImpl::UpdateCheckSucceeded(
+    const ProtocolParser::Results& results,
+    int retry_after_sec) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const int daynum = results.daystart_elapsed_days;
+  if (daynum != ProtocolParser::kNoDaystart) {
+    metadata_->SetDateLastActive(ids_checked_, daynum);
+    metadata_->SetDateLastRollCall(ids_checked_, daynum);
+  }
+  for (const auto& result : results.list) {
+    auto entry = result.cohort_attrs.find(ProtocolParser::Result::kCohort);
+    if (entry != result.cohort_attrs.end())
+      metadata_->SetCohort(result.extension_id, entry->second);
+    entry = result.cohort_attrs.find(ProtocolParser::Result::kCohortName);
+    if (entry != result.cohort_attrs.end())
+      metadata_->SetCohortName(result.extension_id, entry->second);
+    entry = result.cohort_attrs.find(ProtocolParser::Result::kCohortHint);
+    if (entry != result.cohort_attrs.end())
+      metadata_->SetCohortHint(result.extension_id, entry->second);
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(update_check_callback_),
+                     base::make_optional<ProtocolParser::Results>(results),
+                     ErrorCategory::kNone, 0, retry_after_sec));
+}
+
+void UpdateCheckerImpl::UpdateCheckFailed(ErrorCategory error_category,
+                                          int error,
+                                          int retry_after_sec) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_NE(0, error);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(update_check_callback_), base::nullopt,
+                     error_category, error, retry_after_sec));
+}
+
+}  // namespace
+
+std::unique_ptr<UpdateChecker> UpdateChecker::Create(
+    scoped_refptr<Configurator> config,
+    PersistedData* persistent) {
+  return std::make_unique<UpdateCheckerImpl>(config, persistent);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/update_checker.h b/src/updater_components/update_client/update_checker.h
new file mode 100644
index 0000000..d5126e1
--- /dev/null
+++ b/src/updater_components/update_client/update_checker.h
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CHECKER_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_CHECKER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "components/update_client/component.h"
+#include "components/update_client/protocol_parser.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+class Configurator;
+class PersistedData;
+
+class UpdateChecker {
+ public:
+  using UpdateCheckCallback = base::OnceCallback<void(
+      const base::Optional<ProtocolParser::Results>& results,
+      ErrorCategory error_category,
+      int error,
+      int retry_after_sec)>;
+
+  using Factory =
+      std::unique_ptr<UpdateChecker> (*)(scoped_refptr<Configurator> config,
+                                         PersistedData* persistent);
+
+  virtual ~UpdateChecker() = default;
+
+  // Initiates an update check for the components specified by their ids.
+  // |additional_attributes| provides a way to customize the <request> element.
+  // |is_foreground| controls the value of "X-Goog-Update-Interactivity"
+  // header which is sent with the update check.
+  // On completion, the state of |components| is mutated as required by the
+  // server response received.
+  virtual void CheckForUpdates(
+      const std::string& session_id,
+      const std::vector<std::string>& ids_to_check,
+      const IdToComponentPtrMap& components,
+      const base::flat_map<std::string, std::string>& additional_attributes,
+      bool enabled_component_updates,
+      UpdateCheckCallback update_check_callback) = 0;
+
+  static std::unique_ptr<UpdateChecker> Create(
+      scoped_refptr<Configurator> config,
+      PersistedData* persistent);
+
+ protected:
+  UpdateChecker() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpdateChecker);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATE_CHECKER_H_
diff --git a/src/updater_components/update_client/update_checker_unittest.cc b/src/updater_components/update_client/update_checker_unittest.cc
new file mode 100644
index 0000000..24fc086
--- /dev/null
+++ b/src/updater_components/update_client/update_checker_unittest.cc
@@ -0,0 +1,1221 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/update_checker.h"
+
+#include <map>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/version.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/update_client/activity_data_service.h"
+#include "components/update_client/component.h"
+#include "components/update_client/net/url_loader_post_interceptor.h"
+#include "components/update_client/persisted_data.h"
+#include "components/update_client/test_configurator.h"
+#include "components/update_client/update_engine.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using std::string;
+
+namespace update_client {
+
+namespace {
+
+base::FilePath test_file(const char* file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("update_client")
+      .AppendASCII(file);
+}
+
+const char kUpdateItemId[] = "jebgalgnebhfojomionfpkfelancnnkf";
+
+class ActivityDataServiceTest final : public ActivityDataService {
+ public:
+  bool GetActiveBit(const std::string& id) const override;
+  void ClearActiveBit(const std::string& id) override;
+  int GetDaysSinceLastActive(const std::string& id) const override;
+  int GetDaysSinceLastRollCall(const std::string& id) const override;
+
+  void SetActiveBit(const std::string& id, bool value);
+  void SetDaysSinceLastActive(const std::string& id, int daynum);
+  void SetDaysSinceLastRollCall(const std::string& id, int daynum);
+
+ private:
+  std::map<std::string, bool> actives_;
+  std::map<std::string, int> days_since_last_actives_;
+  std::map<std::string, int> days_since_last_rollcalls_;
+};
+
+bool ActivityDataServiceTest::GetActiveBit(const std::string& id) const {
+  const auto& it = actives_.find(id);
+  return it != actives_.end() ? it->second : false;
+}
+
+void ActivityDataServiceTest::ClearActiveBit(const std::string& id) {
+  SetActiveBit(id, false);
+}
+
+int ActivityDataServiceTest::GetDaysSinceLastActive(
+    const std::string& id) const {
+  const auto& it = days_since_last_actives_.find(id);
+  return it != days_since_last_actives_.end() ? it->second : -2;
+}
+
+int ActivityDataServiceTest::GetDaysSinceLastRollCall(
+    const std::string& id) const {
+  const auto& it = days_since_last_rollcalls_.find(id);
+  return it != days_since_last_rollcalls_.end() ? it->second : -2;
+}
+
+void ActivityDataServiceTest::SetActiveBit(const std::string& id, bool value) {
+  actives_[id] = value;
+}
+
+void ActivityDataServiceTest::SetDaysSinceLastActive(const std::string& id,
+                                                     int daynum) {
+  days_since_last_actives_[id] = daynum;
+}
+
+void ActivityDataServiceTest::SetDaysSinceLastRollCall(const std::string& id,
+                                                       int daynum) {
+  days_since_last_rollcalls_[id] = daynum;
+}
+
+}  // namespace
+
+class UpdateCheckerTest : public testing::TestWithParam<bool> {
+ public:
+  UpdateCheckerTest();
+  ~UpdateCheckerTest() override;
+
+  // Overrides from testing::Test.
+  void SetUp() override;
+  void TearDown() override;
+
+  void UpdateCheckComplete(
+      const base::Optional<ProtocolParser::Results>& results,
+      ErrorCategory error_category,
+      int error,
+      int retry_after_sec);
+
+ protected:
+  void Quit();
+  void RunThreads();
+
+  std::unique_ptr<Component> MakeComponent() const;
+
+  scoped_refptr<TestConfigurator> config_;
+  std::unique_ptr<ActivityDataServiceTest> activity_data_service_;
+  std::unique_ptr<TestingPrefServiceSimple> pref_;
+  std::unique_ptr<PersistedData> metadata_;
+
+  std::unique_ptr<UpdateChecker> update_checker_;
+
+  std::unique_ptr<URLLoaderPostInterceptor> post_interceptor_;
+
+  base::Optional<ProtocolParser::Results> results_;
+  ErrorCategory error_category_ = ErrorCategory::kNone;
+  int error_ = 0;
+  int retry_after_sec_ = 0;
+
+  scoped_refptr<UpdateContext> update_context_;
+
+  bool is_foreground_ = false;
+
+ private:
+  scoped_refptr<UpdateContext> MakeMockUpdateContext() const;
+
+  base::test::TaskEnvironment task_environment_;
+  base::OnceClosure quit_closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateCheckerTest);
+};
+
+// This test is parameterized for |is_foreground|.
+INSTANTIATE_TEST_SUITE_P(Parameterized, UpdateCheckerTest, testing::Bool());
+
+UpdateCheckerTest::UpdateCheckerTest()
+    : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}
+
+UpdateCheckerTest::~UpdateCheckerTest() {
+}
+
+void UpdateCheckerTest::SetUp() {
+  is_foreground_ = GetParam();
+
+  config_ = base::MakeRefCounted<TestConfigurator>();
+
+  pref_ = std::make_unique<TestingPrefServiceSimple>();
+  activity_data_service_ = std::make_unique<ActivityDataServiceTest>();
+  PersistedData::RegisterPrefs(pref_->registry());
+  metadata_ = std::make_unique<PersistedData>(pref_.get(),
+                                              activity_data_service_.get());
+
+  post_interceptor_ = std::make_unique<URLLoaderPostInterceptor>(
+      config_->test_url_loader_factory());
+  EXPECT_TRUE(post_interceptor_);
+
+  update_checker_ = nullptr;
+
+  error_ = 0;
+  retry_after_sec_ = 0;
+  update_context_ = MakeMockUpdateContext();
+  update_context_->is_foreground = is_foreground_;
+}
+
+void UpdateCheckerTest::TearDown() {
+  update_checker_ = nullptr;
+
+  post_interceptor_.reset();
+
+  config_ = nullptr;
+
+  // The PostInterceptor requires the message loop to run to destruct correctly.
+  // TODO(sorin): This is fragile and should be fixed.
+  task_environment_.RunUntilIdle();
+}
+
+void UpdateCheckerTest::RunThreads() {
+  base::RunLoop runloop;
+  quit_closure_ = runloop.QuitClosure();
+  runloop.Run();
+}
+
+void UpdateCheckerTest::Quit() {
+  if (!quit_closure_.is_null())
+    std::move(quit_closure_).Run();
+}
+
+void UpdateCheckerTest::UpdateCheckComplete(
+    const base::Optional<ProtocolParser::Results>& results,
+    ErrorCategory error_category,
+    int error,
+    int retry_after_sec) {
+  results_ = results;
+  error_category_ = error_category;
+  error_ = error;
+  retry_after_sec_ = retry_after_sec;
+  Quit();
+}
+
+scoped_refptr<UpdateContext> UpdateCheckerTest::MakeMockUpdateContext() const {
+  return base::MakeRefCounted<UpdateContext>(
+      config_, false, std::vector<std::string>(),
+      UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
+      UpdateEngine::Callback(), nullptr);
+}
+
+std::unique_ptr<Component> UpdateCheckerTest::MakeComponent() const {
+  CrxComponent crx_component;
+  crx_component.name = "test_jebg";
+  crx_component.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+  crx_component.installer = nullptr;
+  crx_component.version = base::Version("0.9");
+  crx_component.fingerprint = "fp1";
+
+  auto component = std::make_unique<Component>(*update_context_, kUpdateItemId);
+  component->state_ = std::make_unique<Component::StateNew>(component.get());
+  component->crx_component_ = crx_component;
+
+  return component;
+}
+
+// This test is parameterized for |is_foreground|.
+TEST_P(UpdateCheckerTest, UpdateCheckSuccess) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_1.json")));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  auto& component = components[kUpdateItemId];
+  component->crx_component_->installer_attributes["ap"] = "some_ap";
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components,
+      {{"extra", "params"}, {"testrequest", "1"}}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  ASSERT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  // Sanity check the request.
+  const auto root =
+      base::JSONReader::Read(post_interceptor_->GetRequestBody(0));
+  ASSERT_TRUE(root);
+  const auto* request = root->FindKey("request");
+  ASSERT_TRUE(request);
+  EXPECT_TRUE(request->FindKey("@os"));
+  EXPECT_EQ("fake_prodid", request->FindKey("@updater")->GetString());
+  EXPECT_EQ("crx2,crx3", request->FindKey("acceptformat")->GetString());
+  EXPECT_TRUE(request->FindKey("arch"));
+  EXPECT_EQ("cr", request->FindKey("dedup")->GetString());
+  EXPECT_EQ("params", request->FindKey("extra")->GetString());
+  EXPECT_LT(0, request->FindPath({"hw", "physmemory"})->GetInt());
+  EXPECT_EQ("fake_lang", request->FindKey("lang")->GetString());
+  EXPECT_TRUE(request->FindKey("nacl_arch"));
+  EXPECT_EQ("fake_channel_string",
+            request->FindKey("prodchannel")->GetString());
+  EXPECT_EQ("30.0", request->FindKey("prodversion")->GetString());
+  EXPECT_EQ("3.1", request->FindKey("protocol")->GetString());
+  EXPECT_TRUE(request->FindKey("requestid"));
+  EXPECT_TRUE(request->FindKey("sessionid"));
+  EXPECT_EQ("1", request->FindKey("testrequest")->GetString());
+  EXPECT_EQ("fake_channel_string",
+            request->FindKey("updaterchannel")->GetString());
+  EXPECT_EQ("30.0", request->FindKey("updaterversion")->GetString());
+
+  // No "dlpref" is sent by default.
+  EXPECT_FALSE(request->FindKey("dlpref"));
+
+  EXPECT_TRUE(request->FindPath({"os", "arch"})->is_string());
+  EXPECT_EQ("Fake Operating System",
+            request->FindPath({"os", "platform"})->GetString());
+  EXPECT_TRUE(request->FindPath({"os", "version"})->is_string());
+
+  const auto& app = request->FindKey("app")->GetList()[0];
+  EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+  EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+  EXPECT_EQ("TEST", app.FindKey("brand")->GetString());
+  if (is_foreground_)
+    EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString());
+  EXPECT_EQ("some_ap", app.FindKey("ap")->GetString());
+  EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+  EXPECT_TRUE(app.FindKey("updatecheck"));
+  EXPECT_TRUE(app.FindKey("ping"));
+  EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt());
+  EXPECT_EQ("fp1", app.FindPath({"packages", "package"})
+                       ->GetList()[0]
+                       .FindKey("fp")
+                       ->GetString());
+
+#if defined(OS_WIN)
+  EXPECT_TRUE(request->FindKey("domainjoined"));
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  const auto* updater = request->FindKey("updater");
+  EXPECT_TRUE(updater);
+  EXPECT_EQ("Omaha", updater->FindKey("name")->GetString());
+  EXPECT_TRUE(updater->FindKey("autoupdatecheckenabled")->is_bool());
+  EXPECT_TRUE(updater->FindKey("ismachine")->is_bool());
+  EXPECT_TRUE(updater->FindKey("updatepolicy")->is_int());
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#endif  // OS_WIN
+
+  // Sanity check the arguments of the callback after parsing.
+  EXPECT_EQ(ErrorCategory::kNone, error_category_);
+  EXPECT_EQ(0, error_);
+  EXPECT_TRUE(results_);
+  EXPECT_EQ(1u, results_->list.size());
+  const auto& result = results_->list.front();
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", result.extension_id.c_str());
+  EXPECT_EQ("1.0", result.manifest.version);
+  EXPECT_EQ("11.0.1.0", result.manifest.browser_min_version);
+  EXPECT_EQ(1u, result.manifest.packages.size());
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf.crx",
+               result.manifest.packages.front().name.c_str());
+  EXPECT_EQ(1u, result.crx_urls.size());
+  EXPECT_EQ(GURL("http://localhost/download/"), result.crx_urls.front());
+  EXPECT_STREQ("this", result.action_run.c_str());
+
+  // Check the DDOS protection header values.
+  const auto extra_request_headers =
+      std::get<1>(post_interceptor_->GetRequests()[0]);
+  EXPECT_TRUE(extra_request_headers.HasHeader("X-Goog-Update-Interactivity"));
+  std::string header;
+  extra_request_headers.GetHeader("X-Goog-Update-Interactivity", &header);
+  EXPECT_STREQ(is_foreground_ ? "fg" : "bg", header.c_str());
+  extra_request_headers.GetHeader("X-Goog-Update-Updater", &header);
+  EXPECT_STREQ("fake_prodid-30.0", header.c_str());
+  extra_request_headers.GetHeader("X-Goog-Update-AppId", &header);
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", header.c_str());
+}
+
+// Tests that an invalid "ap" is not serialized.
+TEST_P(UpdateCheckerTest, UpdateCheckInvalidAp) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_1.json")));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  // Make "ap" too long.
+  auto& component = components[kUpdateItemId];
+  component->crx_component_->installer_attributes["ap"] = std::string(257, 'a');
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+
+  RunThreads();
+
+  const auto request = post_interceptor_->GetRequestBody(0);
+    const auto root = base::JSONReader::Read(request);
+    ASSERT_TRUE(root);
+    const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+    EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+    EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+    EXPECT_EQ("TEST", app.FindKey("brand")->GetString());
+    if (is_foreground_)
+      EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString());
+    EXPECT_FALSE(app.FindKey("ap"));
+    EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+    EXPECT_TRUE(app.FindKey("updatecheck"));
+    EXPECT_TRUE(app.FindKey("ping"));
+    EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt());
+    EXPECT_EQ("fp1", app.FindPath({"packages", "package"})
+                         ->GetList()[0]
+                         .FindKey("fp")
+                         ->GetString());
+}
+
+TEST_P(UpdateCheckerTest, UpdateCheckSuccessNoBrand) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_1.json")));
+
+  config_->SetBrand("TOOLONG");   // Sets an invalid brand code.
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+
+  RunThreads();
+
+  const auto request = post_interceptor_->GetRequestBody(0);
+
+    const auto root = base::JSONReader::Read(request);
+    ASSERT_TRUE(root);
+    const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+    EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+    EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+    EXPECT_FALSE(app.FindKey("brand"));
+    if (is_foreground_)
+      EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString());
+    EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+    EXPECT_TRUE(app.FindKey("updatecheck"));
+    EXPECT_TRUE(app.FindKey("ping"));
+    EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt());
+    EXPECT_EQ("fp1", app.FindPath({"packages", "package"})
+                         ->GetList()[0]
+                         .FindKey("fp")
+                         ->GetString());
+}
+
+// Simulates a 403 server response error.
+TEST_P(UpdateCheckerTest, UpdateCheckError) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"), net::HTTP_FORBIDDEN));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  EXPECT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
+  EXPECT_EQ(403, error_);
+  EXPECT_FALSE(results_);
+}
+
+TEST_P(UpdateCheckerTest, UpdateCheckDownloadPreference) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_1.json")));
+
+  config_->SetDownloadPreference(string("cacheable"));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components,
+      {{"extra", "params"}}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  // The request must contain dlpref="cacheable".
+  const auto request = post_interceptor_->GetRequestBody(0);
+    const auto root = base::JSONReader().Read(request);
+    ASSERT_TRUE(root);
+    EXPECT_EQ("cacheable",
+              root->FindKey("request")->FindKey("dlpref")->GetString());
+}
+
+// This test is checking that an update check signed with CUP fails, since there
+// is currently no entity that can respond with a valid signed response.
+// A proper CUP test requires network mocks, which are not available now.
+TEST_P(UpdateCheckerTest, UpdateCheckCupError) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_1.json")));
+
+  config_->SetEnabledCupSigning(true);
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  ASSERT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  // Sanity check the request.
+  const auto& request = post_interceptor_->GetRequestBody(0);
+    const auto root = base::JSONReader::Read(request);
+    ASSERT_TRUE(root);
+    const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+    EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+    EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+    EXPECT_EQ("TEST", app.FindKey("brand")->GetString());
+    if (is_foreground_)
+      EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString());
+    EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+    EXPECT_TRUE(app.FindKey("updatecheck"));
+    EXPECT_TRUE(app.FindKey("ping"));
+    EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt());
+    EXPECT_EQ("fp1", app.FindPath({"packages", "package"})
+                         ->GetList()[0]
+                         .FindKey("fp")
+                         ->GetString());
+
+  // Expect an error since the response is not trusted.
+  EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
+  EXPECT_EQ(-10000, error_);
+  EXPECT_FALSE(results_);
+}
+
+// Tests that the UpdateCheckers will not make an update check for a
+// component that requires encryption when the update check URL is unsecure.
+TEST_P(UpdateCheckerTest, UpdateCheckRequiresEncryptionError) {
+  config_->SetUpdateCheckUrl(GURL("http:\\foo\bar"));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  auto& component = components[kUpdateItemId];
+  component->crx_component_->requires_network_encryption = true;
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
+  EXPECT_EQ(-10002, error_);
+  EXPECT_FALSE(component->next_version_.IsValid());
+}
+
+// Tests that the PersistedData will get correctly update and reserialize
+// the elapsed_days value.
+TEST_P(UpdateCheckerTest, UpdateCheckLastRollCall) {
+  const char* filename = "updatecheck_reply_4.json";
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"), test_file(filename)));
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"), test_file(filename)));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  // Do two update-checks.
+  activity_data_service_->SetDaysSinceLastRollCall(kUpdateItemId, 5);
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components,
+      {{"extra", "params"}}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components,
+      {{"extra", "params"}}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(2, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  ASSERT_EQ(2, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+    const auto root1 =
+        base::JSONReader::Read(post_interceptor_->GetRequestBody(0));
+    ASSERT_TRUE(root1);
+    const auto& app1 = root1->FindKey("request")->FindKey("app")->GetList()[0];
+    EXPECT_EQ(5, app1.FindPath({"ping", "r"})->GetInt());
+    const auto root2 =
+        base::JSONReader::Read(post_interceptor_->GetRequestBody(1));
+    ASSERT_TRUE(root2);
+    const auto& app2 = root2->FindKey("request")->FindKey("app")->GetList()[0];
+    EXPECT_EQ(3383, app2.FindPath({"ping", "rd"})->GetInt());
+    EXPECT_TRUE(app2.FindPath({"ping", "ping_freshness"})->is_string());
+}
+
+TEST_P(UpdateCheckerTest, UpdateCheckLastActive) {
+  const char* filename = "updatecheck_reply_4.json";
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"), test_file(filename)));
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"), test_file(filename)));
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"), test_file(filename)));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  activity_data_service_->SetActiveBit(kUpdateItemId, true);
+  activity_data_service_->SetDaysSinceLastActive(kUpdateItemId, 10);
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components,
+      {{"extra", "params"}}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  // The active bit should be reset.
+  EXPECT_FALSE(metadata_->GetActiveBit(kUpdateItemId));
+
+  activity_data_service_->SetActiveBit(kUpdateItemId, true);
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components,
+      {{"extra", "params"}}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  // The active bit should be reset.
+  EXPECT_FALSE(metadata_->GetActiveBit(kUpdateItemId));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components,
+      {{"extra", "params"}}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_FALSE(metadata_->GetActiveBit(kUpdateItemId));
+
+  EXPECT_EQ(3, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  ASSERT_EQ(3, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+    {
+      const auto root =
+          base::JSONReader::Read(post_interceptor_->GetRequestBody(0));
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(10, app.FindPath({"ping", "a"})->GetInt());
+      EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt());
+    }
+    {
+      const auto root =
+          base::JSONReader::Read(post_interceptor_->GetRequestBody(1));
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(3383, app.FindPath({"ping", "ad"})->GetInt());
+      EXPECT_EQ(3383, app.FindPath({"ping", "rd"})->GetInt());
+      EXPECT_TRUE(app.FindPath({"ping", "ping_freshness"})->is_string());
+    }
+    {
+      const auto root =
+          base::JSONReader::Read(post_interceptor_->GetRequestBody(2));
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(3383, app.FindPath({"ping", "rd"})->GetInt());
+      EXPECT_TRUE(app.FindPath({"ping", "ping_freshness"})->is_string());
+    }
+}
+
+TEST_P(UpdateCheckerTest, UpdateCheckInstallSource) {
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  auto& component = components[kUpdateItemId];
+  auto crx_component = component->crx_component();
+
+  if (is_foreground_) {
+    {
+      auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+          config_->test_url_loader_factory());
+      EXPECT_TRUE(post_interceptor->ExpectRequest(
+          std::make_unique<PartialMatch>("updatecheck"),
+          test_file("updatecheck_reply_1.json")));
+      update_checker_->CheckForUpdates(
+          update_context_->session_id, {kUpdateItemId}, components, {}, false,
+          base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                         base::Unretained(this)));
+      RunThreads();
+      const auto& request = post_interceptor->GetRequestBody(0);
+        const auto root = base::JSONReader::Read(request);
+        ASSERT_TRUE(root);
+        const auto& app =
+            root->FindKey("request")->FindKey("app")->GetList()[0];
+        EXPECT_EQ("ondemand", app.FindKey("installsource")->GetString());
+        EXPECT_FALSE(app.FindKey("installedby"));
+    }
+    {
+      auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+          config_->test_url_loader_factory());
+      EXPECT_TRUE(post_interceptor->ExpectRequest(
+          std::make_unique<PartialMatch>("updatecheck"),
+          test_file("updatecheck_reply_1.json")));
+      crx_component->install_source = "sideload";
+      crx_component->install_location = "policy";
+      component->set_crx_component(*crx_component);
+      update_checker_->CheckForUpdates(
+          update_context_->session_id, {kUpdateItemId}, components, {}, false,
+          base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                         base::Unretained(this)));
+      RunThreads();
+      const auto& request = post_interceptor->GetRequestBody(0);
+        const auto root = base::JSONReader::Read(request);
+        ASSERT_TRUE(root);
+        const auto& app =
+            root->FindKey("request")->FindKey("app")->GetList()[0];
+        EXPECT_EQ("sideload", app.FindKey("installsource")->GetString());
+        EXPECT_EQ("policy", app.FindKey("installedby")->GetString());
+    }
+    return;
+  }
+
+  DCHECK(!is_foreground_);
+  {
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_FALSE(app.FindKey("installsource"));
+  }
+  {
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    crx_component->install_source = "webstore";
+    crx_component->install_location = "external";
+    component->set_crx_component(*crx_component);
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ("webstore", app.FindKey("installsource")->GetString());
+      EXPECT_EQ("external", app.FindKey("installedby")->GetString());
+  }
+}
+
+TEST_P(UpdateCheckerTest, ComponentDisabled) {
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  auto& component = components[kUpdateItemId];
+  auto crx_component = component->crx_component();
+
+  {
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+      EXPECT_FALSE(app.FindKey("disabled"));
+  }
+
+  {
+    crx_component->disabled_reasons = {};
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+      EXPECT_FALSE(app.FindKey("disabled"));
+  }
+
+  {
+    crx_component->disabled_reasons = {0};
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(false, app.FindKey("enabled")->GetBool());
+      const auto& disabled = app.FindKey("disabled")->GetList();
+      EXPECT_EQ(1u, disabled.size());
+      EXPECT_EQ(0, disabled[0].FindKey("reason")->GetInt());
+  }
+  {
+    crx_component->disabled_reasons = {1};
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(false, app.FindKey("enabled")->GetBool());
+      const auto& disabled = app.FindKey("disabled")->GetList();
+      EXPECT_EQ(1u, disabled.size());
+      EXPECT_EQ(1, disabled[0].FindKey("reason")->GetInt());
+  }
+
+  {
+    crx_component->disabled_reasons = {4, 8, 16};
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(false, app.FindKey("enabled")->GetBool());
+      const auto& disabled = app.FindKey("disabled")->GetList();
+      EXPECT_EQ(3u, disabled.size());
+      EXPECT_EQ(4, disabled[0].FindKey("reason")->GetInt());
+      EXPECT_EQ(8, disabled[1].FindKey("reason")->GetInt());
+      EXPECT_EQ(16, disabled[2].FindKey("reason")->GetInt());
+  }
+
+  {
+    crx_component->disabled_reasons = {0, 4, 8, 16};
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(false, app.FindKey("enabled")->GetBool());
+      const auto& disabled = app.FindKey("disabled")->GetList();
+      EXPECT_EQ(4u, disabled.size());
+      EXPECT_EQ(0, disabled[0].FindKey("reason")->GetInt());
+      EXPECT_EQ(4, disabled[1].FindKey("reason")->GetInt());
+      EXPECT_EQ(8, disabled[2].FindKey("reason")->GetInt());
+      EXPECT_EQ(16, disabled[3].FindKey("reason")->GetInt());
+  }
+}
+
+TEST_P(UpdateCheckerTest, UpdateCheckUpdateDisabled) {
+  config_->SetBrand("");
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  auto& component = components[kUpdateItemId];
+  auto crx_component = component->crx_component();
+
+  // Ignore this test parameter to keep the test simple.
+  update_context_->is_foreground = false;
+  {
+    // Tests the scenario where:
+    //  * the component does not support group policies.
+    //  * the component updates are disabled.
+    // Expects the group policy to be ignored and the update check to not
+    // include the "updatedisabled" attribute.
+    EXPECT_FALSE(crx_component->supports_group_policy_enable_component_updates);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+      EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+      EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+      EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty());
+  }
+  {
+    // Tests the scenario where:
+    //  * the component supports group policies.
+    //  * the component updates are disabled.
+    // Expects the update check to include the "updatedisabled" attribute.
+    crx_component->supports_group_policy_enable_component_updates = true;
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, false,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+      EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+      EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+      EXPECT_TRUE(app.FindPath({"updatecheck", "updatedisabled"})->GetBool());
+  }
+  {
+    // Tests the scenario where:
+    //  * the component does not support group policies.
+    //  * the component updates are enabled.
+    // Expects the update check to not include the "updatedisabled" attribute.
+    crx_component->supports_group_policy_enable_component_updates = false;
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, true,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+      EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+      EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+      EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty());
+  }
+  {
+    // Tests the scenario where:
+    //  * the component supports group policies.
+    //  * the component updates are enabled.
+    // Expects the update check to not include the "updatedisabled" attribute.
+    crx_component->supports_group_policy_enable_component_updates = true;
+    component->set_crx_component(*crx_component);
+    auto post_interceptor = std::make_unique<URLLoaderPostInterceptor>(
+        config_->test_url_loader_factory());
+    EXPECT_TRUE(post_interceptor->ExpectRequest(
+        std::make_unique<PartialMatch>("updatecheck"),
+        test_file("updatecheck_reply_1.json")));
+    update_checker_->CheckForUpdates(
+        update_context_->session_id, {kUpdateItemId}, components, {}, true,
+        base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                       base::Unretained(this)));
+    RunThreads();
+    const auto& request = post_interceptor->GetRequestBody(0);
+      const auto root = base::JSONReader::Read(request);
+      ASSERT_TRUE(root);
+      const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+      EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+      EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+      EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+      EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty());
+  }
+}
+
+TEST_P(UpdateCheckerTest, NoUpdateActionRun) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_noupdate.json")));
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  ASSERT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  // Sanity check the arguments of the callback after parsing.
+  EXPECT_EQ(ErrorCategory::kNone, error_category_);
+  EXPECT_EQ(0, error_);
+  EXPECT_TRUE(results_);
+  EXPECT_EQ(1u, results_->list.size());
+  const auto& result = results_->list.front();
+  EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", result.extension_id.c_str());
+  EXPECT_STREQ("noupdate", result.status.c_str());
+  EXPECT_STREQ("this", result.action_run.c_str());
+}
+
+TEST_P(UpdateCheckerTest, UpdatePauseResume) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_noupdate.json")));
+  post_interceptor_->url_job_request_ready_callback(base::BindOnce(
+      [](URLLoaderPostInterceptor* post_interceptor) {
+        post_interceptor->Resume();
+      },
+      post_interceptor_.get()));
+  post_interceptor_->Pause();
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  // Ignore this test parameter to keep the test simple.
+  update_context_->is_foreground = false;
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  const auto& request = post_interceptor_->GetRequestBody(0);
+    const auto root = base::JSONReader::Read(request);
+    ASSERT_TRUE(root);
+    const auto& app = root->FindKey("request")->FindKey("app")->GetList()[0];
+    EXPECT_EQ(kUpdateItemId, app.FindKey("appid")->GetString());
+    EXPECT_EQ("0.9", app.FindKey("version")->GetString());
+    EXPECT_EQ("TEST", app.FindKey("brand")->GetString());
+    EXPECT_EQ(true, app.FindKey("enabled")->GetBool());
+    EXPECT_TRUE(app.FindKey("updatecheck")->DictEmpty());
+    EXPECT_EQ(-2, app.FindPath({"ping", "r"})->GetInt());
+    EXPECT_EQ("fp1", app.FindKey("packages")
+                         ->FindKey("package")
+                         ->GetList()[0]
+                         .FindKey("fp")
+                         ->GetString());
+}
+
+// Tests that an update checker object and its underlying SimpleURLLoader can
+// be safely destroyed while it is paused.
+TEST_P(UpdateCheckerTest, UpdateResetUpdateChecker) {
+  base::RunLoop runloop;
+  auto quit_closure = runloop.QuitClosure();
+
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_1.json")));
+  post_interceptor_->url_job_request_ready_callback(base::BindOnce(
+      [](base::OnceClosure quit_closure) { std::move(quit_closure).Run(); },
+      std::move(quit_closure)));
+  post_interceptor_->Pause();
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  runloop.Run();
+}
+
+// The update response contains a protocol version which does not match the
+// expected protocol version.
+TEST_P(UpdateCheckerTest, ParseErrorProtocolVersionMismatch) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_parse_error.json")));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  ASSERT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
+  EXPECT_EQ(-10003, error_);
+  EXPECT_FALSE(results_);
+}
+
+// The update response contains a status |error-unknownApplication| for the
+// app. The response is succesfully parsed and a result is extracted to
+// indicate this status.
+TEST_P(UpdateCheckerTest, ParseErrorAppStatusErrorUnknownApplication) {
+  EXPECT_TRUE(post_interceptor_->ExpectRequest(
+      std::make_unique<PartialMatch>("updatecheck"),
+      test_file("updatecheck_reply_unknownapp.json")));
+
+  update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+  IdToComponentPtrMap components;
+  components[kUpdateItemId] = MakeComponent();
+
+  update_checker_->CheckForUpdates(
+      update_context_->session_id, {kUpdateItemId}, components, {}, true,
+      base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(1, post_interceptor_->GetHitCount())
+      << post_interceptor_->GetRequestsAsString();
+  ASSERT_EQ(1, post_interceptor_->GetCount())
+      << post_interceptor_->GetRequestsAsString();
+
+  EXPECT_EQ(ErrorCategory::kNone, error_category_);
+  EXPECT_EQ(0, error_);
+  EXPECT_TRUE(results_);
+  EXPECT_EQ(1u, results_->list.size());
+  const auto& result = results_->list.front();
+  EXPECT_STREQ("error-unknownApplication", result.status.c_str());
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/update_client.cc b/src/updater_components/update_client/update_client.cc
new file mode 100644
index 0000000..1a12e47
--- /dev/null
+++ b/src/updater_components/update_client/update_client.cc
@@ -0,0 +1,254 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/update_client.h"
+
+#include <algorithm>
+#include <queue>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/persisted_data.h"
+#include "components/update_client/ping_manager.h"
+#include "components/update_client/protocol_parser.h"
+#include "components/update_client/task_send_uninstall_ping.h"
+#include "components/update_client/task_update.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/update_client_internal.h"
+#include "components/update_client/update_engine.h"
+#include "components/update_client/utils.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+CrxUpdateItem::CrxUpdateItem() : state(ComponentState::kNew) {}
+CrxUpdateItem::~CrxUpdateItem() = default;
+CrxUpdateItem::CrxUpdateItem(const CrxUpdateItem& other) = default;
+
+CrxComponent::CrxComponent()
+    : allows_background_download(true),
+      requires_network_encryption(true),
+      crx_format_requirement(
+          crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF),
+      supports_group_policy_enable_component_updates(false) {}
+CrxComponent::CrxComponent(const CrxComponent& other) = default;
+CrxComponent::~CrxComponent() = default;
+
+// It is important that an instance of the UpdateClient binds an unretained
+// pointer to itself. Otherwise, a life time circular dependency between this
+// instance and its inner members prevents the destruction of this instance.
+// Using unretained references is allowed in this case since the life time of
+// the UpdateClient instance exceeds the life time of its inner members,
+// including any thread objects that might execute callbacks bound to it.
+UpdateClientImpl::UpdateClientImpl(
+    scoped_refptr<Configurator> config,
+    scoped_refptr<PingManager> ping_manager,
+    UpdateChecker::Factory update_checker_factory,
+    CrxDownloader::Factory crx_downloader_factory)
+    : is_stopped_(false),
+      config_(config),
+      ping_manager_(ping_manager),
+      update_engine_(base::MakeRefCounted<UpdateEngine>(
+          config,
+          update_checker_factory,
+          crx_downloader_factory,
+          ping_manager_.get(),
+          base::Bind(&UpdateClientImpl::NotifyObservers,
+                     base::Unretained(this)))) {}
+
+UpdateClientImpl::~UpdateClientImpl() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DCHECK(task_queue_.empty());
+  DCHECK(tasks_.empty());
+
+  config_ = nullptr;
+}
+
+void UpdateClientImpl::Install(const std::string& id,
+                               CrxDataCallback crx_data_callback,
+                               Callback callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (IsUpdating(id)) {
+    std::move(callback).Run(Error::UPDATE_IN_PROGRESS);
+    return;
+  }
+
+  std::vector<std::string> ids = {id};
+
+  // Install tasks are run concurrently and never queued up. They are always
+  // considered foreground tasks.
+  constexpr bool kIsForeground = true;
+  RunTask(base::MakeRefCounted<TaskUpdate>(
+      update_engine_.get(), kIsForeground, ids, std::move(crx_data_callback),
+      base::BindOnce(&UpdateClientImpl::OnTaskComplete, this,
+                     std::move(callback))));
+}
+
+void UpdateClientImpl::Update(const std::vector<std::string>& ids,
+                              CrxDataCallback crx_data_callback,
+                              bool is_foreground,
+                              Callback callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  auto task = base::MakeRefCounted<TaskUpdate>(
+      update_engine_.get(), is_foreground, ids, std::move(crx_data_callback),
+      base::BindOnce(&UpdateClientImpl::OnTaskComplete, this,
+                     std::move(callback)));
+
+  // If no other tasks are running at the moment, run this update task.
+  // Otherwise, queue the task up.
+  if (tasks_.empty()) {
+    RunTask(task);
+  } else {
+    task_queue_.push_back(task);
+  }
+}
+
+void UpdateClientImpl::RunTask(scoped_refptr<Task> task) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&Task::Run, base::Unretained(task.get())));
+  tasks_.insert(task);
+}
+
+void UpdateClientImpl::OnTaskComplete(Callback callback,
+                                      scoped_refptr<Task> task,
+                                      Error error) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(task);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), error));
+
+  // Remove the task from the set of the running tasks. Only tasks handled by
+  // the update engine can be in this data structure.
+  tasks_.erase(task);
+
+  if (is_stopped_)
+    return;
+
+  // Pick up a task from the queue if the queue has pending tasks and no other
+  // task is running.
+  if (tasks_.empty() && !task_queue_.empty()) {
+    auto task = task_queue_.front();
+    task_queue_.pop_front();
+    RunTask(task);
+  }
+}
+
+void UpdateClientImpl::AddObserver(Observer* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  observer_list_.AddObserver(observer);
+}
+
+void UpdateClientImpl::RemoveObserver(Observer* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  observer_list_.RemoveObserver(observer);
+}
+
+void UpdateClientImpl::NotifyObservers(Observer::Events event,
+                                       const std::string& id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  for (auto& observer : observer_list_)
+    observer.OnEvent(event, id);
+}
+
+bool UpdateClientImpl::GetCrxUpdateState(const std::string& id,
+                                         CrxUpdateItem* update_item) const {
+  return update_engine_->GetUpdateState(id, update_item);
+}
+
+bool UpdateClientImpl::IsUpdating(const std::string& id) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  for (const auto task : tasks_) {
+    const auto ids = task->GetIds();
+    if (base::Contains(ids, id)) {
+      return true;
+    }
+  }
+
+  for (const auto task : task_queue_) {
+    const auto ids = task->GetIds();
+    if (base::Contains(ids, id)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void UpdateClientImpl::Stop() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  is_stopped_ = true;
+
+  // In the current implementation it is sufficient to cancel the pending
+  // tasks only. The tasks that are run by the update engine will stop
+  // making progress naturally, as the main task runner stops running task
+  // actions. Upon the browser shutdown, the resources employed by the active
+  // tasks will leak, as the operating system kills the thread associated with
+  // the update engine task runner. Further refactoring may be needed in this
+  // area, to cancel the running tasks by canceling the current action update.
+  // This behavior would be expected, correct, and result in no resource leaks
+  // in all cases, in shutdown or not.
+  //
+  // Cancel the pending tasks. These tasks are safe to cancel and delete since
+  // they have not picked up by the update engine, and not shared with any
+  // task runner yet.
+  while (!task_queue_.empty()) {
+    auto task = task_queue_.front();
+    task_queue_.pop_front();
+    task->Cancel();
+  }
+}
+
+void UpdateClientImpl::SendUninstallPing(const std::string& id,
+                                         const base::Version& version,
+                                         int reason,
+                                         Callback callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  RunTask(base::MakeRefCounted<TaskSendUninstallPing>(
+      update_engine_.get(), id, version, reason,
+      base::BindOnce(&UpdateClientImpl::OnTaskComplete, base::Unretained(this),
+                     std::move(callback))));
+}
+
+scoped_refptr<UpdateClient> UpdateClientFactory(
+    scoped_refptr<Configurator> config) {
+  return base::MakeRefCounted<UpdateClientImpl>(
+      config, base::MakeRefCounted<PingManager>(config), &UpdateChecker::Create,
+      &CrxDownloader::Create);
+}
+
+void RegisterPrefs(PrefRegistrySimple* registry) {
+  PersistedData::RegisterPrefs(registry);
+}
+
+// This function has the exact same implementation as RegisterPrefs. We have
+// this implementation here to make the intention more clear that is local user
+// profile access is needed.
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  PersistedData::RegisterPrefs(registry);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/update_client.h b/src/updater_components/update_client/update_client.h
new file mode 100644
index 0000000..6d58c59
--- /dev/null
+++ b/src/updater_components/update_client/update_client.h
@@ -0,0 +1,417 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/version.h"
+#include "components/update_client/update_client_errors.h"
+
+// The UpdateClient class is a facade with a simple interface. The interface
+// exposes a few APIs to install a CRX or update a group of CRXs.
+//
+// The difference between a CRX install and a CRX update is relatively minor.
+// The terminology going forward will use the word "update" to cover both
+// install and update scenarios, except where details regarding the install
+// case are relevant.
+//
+// Handling an update consists of a series of actions such as sending an update
+// check to the server, followed by parsing the server response, identifying
+// the CRXs that require an update, downloading the differential update if
+// it is available, unpacking and patching the differential update, then
+// falling back to trying a similar set of actions using the full update.
+// At the end of this process, completion pings are sent to the server,
+// as needed, for the CRXs which had updates.
+//
+// As a general idea, this code handles the action steps needed to update
+// a group of components serially, one step at a time. However, concurrent
+// execution of calls to UpdateClient::Update is possible, therefore,
+// queuing of updates could happen in some cases. More below.
+//
+// The UpdateClient class features a subject-observer interface to observe
+// the CRX state changes during an update.
+//
+// The threading model for this code assumes that most of the code in the
+// public interface runs on a SingleThreadTaskRunner.
+// This task runner corresponds to the browser UI thread in many cases. There
+// are parts of the installer interface that run on blocking task runners, which
+// are usually threads in a thread pool.
+//
+// Using the UpdateClient is relatively easy. This assumes that the client
+// of this code has already implemented the observer interface as needed, and
+// can provide an installer, as described below.
+//
+//    std::unique_ptr<UpdateClient> update_client(UpdateClientFactory(...));
+//    update_client->AddObserver(&observer);
+//    std::vector<std::string> ids;
+//    ids.push_back(...));
+//    update_client->Update(ids, base::BindOnce(...), base::BindOnce(...));
+//
+// UpdateClient::Update takes two callbacks as parameters. First callback
+// allows the client of this code to provide an instance of CrxComponent
+// data structure that specifies additional parameters of the update.
+// CrxComponent has a CrxInstaller data member, which must be provided by the
+// callers of this class. The second callback indicates that this non-blocking
+// call has completed.
+//
+// There could be several ways of triggering updates for a CRX, user-initiated,
+// or timer-based. Since the execution of updates is concurrent, the parameters
+// for the update must be provided right before the update is handled.
+// Otherwise, the version of the CRX set in the CrxComponent may not be correct.
+//
+// The UpdateClient public interface includes two functions: Install and
+// Update. These functions correspond to installing one CRX immediately as a
+// foreground activity (Install), and updating a group of CRXs silently in the
+// background (Update). This distinction is important. Background updates are
+// queued up and their actions run serially, one at a time, for the purpose of
+// conserving local resources such as CPU, network, and I/O.
+// On the other hand, installs are never queued up but run concurrently, as
+// requested by the user.
+//
+// The update client introduces a runtime constraint regarding interleaving
+// updates and installs. If installs or updates for a given CRX are in progress,
+// then installs for the same CRX will fail with a specific error.
+//
+// Implementation details.
+//
+// The implementation details below are not relevant to callers of this
+// code. However, these design notes are relevant to the owners and maintainers
+// of this module.
+//
+// The design for the update client consists of a number of abstractions
+// such as: task, update engine, update context, and action.
+// The execution model for these abstractions is simple. They usually expose
+// a public, non-blocking Run function, and they invoke a callback when
+// the Run function has completed.
+//
+// A task is the unit of work for the UpdateClient. A task is associated
+// with a single call of the Update function. A task represents a group
+// of CRXs that are updated together.
+//
+// The UpdateClient is responsible for the queuing of tasks, if queuing is
+// needed.
+//
+// When the task runs, it calls the update engine to handle the updates for
+// the CRXs associated with the task. The UpdateEngine is the abstraction
+// responsible for breaking down the update in a set of discrete steps, which
+// are implemented as actions, and running the actions.
+//
+// The UpdateEngine maintains a set of UpdateContext instances. Each of
+// these instances maintains the update state for all the CRXs belonging to
+// a given task. The UpdateContext contains a queue of CRX ids.
+// The UpdateEngine will handle updates for the CRXs in the order they appear
+// in the queue, until the queue is empty.
+//
+// The update state for each CRX is maintained in a container of CrxUpdateItem*.
+// As actions run, each action updates the CRX state, represented by one of
+// these CrxUpdateItem* instances.
+//
+// Although the UpdateEngine can and will run update tasks concurrently, the
+// actions of a task are run sequentially.
+//
+// The Action is a polymorphic type. There is some code reuse for convenience,
+// implemented as a mixin. The polymorphic behavior of some of the actions
+// is achieved using a template method.
+//
+// State changes of a CRX could generate events, which are observed using a
+// subject-observer interface.
+//
+// The actions chain up. In some sense, the actions implement a state machine,
+// as the CRX undergoes a series of state transitions in the process of
+// being checked for updates and applying the update.
+
+class PrefRegistrySimple;
+
+namespace base {
+class FilePath;
+}
+
+namespace crx_file {
+enum class VerifierFormat;
+}
+
+namespace update_client {
+
+class Configurator;
+enum class Error;
+struct CrxUpdateItem;
+
+enum class ComponentState {
+  kNew,
+  kChecking,
+  kCanUpdate,
+  kDownloadingDiff,
+  kDownloading,
+  kDownloaded,
+  kUpdatingDiff,
+  kUpdating,
+  kUpdated,
+  kUpToDate,
+  kUpdateError,
+  kUninstalled,
+  kRun,
+  kLastStatus
+};
+
+// Defines an interface for a generic CRX installer.
+class CrxInstaller : public base::RefCountedThreadSafe<CrxInstaller> {
+ public:
+  // Contains the result of the Install operation.
+  struct Result {
+    explicit Result(int error, int extended_error = 0)
+        : error(error), extended_error(extended_error) {}
+    explicit Result(InstallError error, int extended_error = 0)
+        : error(static_cast<int>(error)), extended_error(extended_error) {}
+    int error = 0;  // 0 indicates that install has been successful.
+    int extended_error = 0;
+  };
+
+  using Callback = base::OnceCallback<void(const Result& result)>;
+
+  // Called on the main thread when there was a problem unpacking or
+  // verifying the CRX. |error| is a non-zero value which is only meaningful
+  // to the caller.
+  virtual void OnUpdateError(int error) = 0;
+
+  // Called by the update service when a CRX has been unpacked
+  // and it is ready to be installed. |unpack_path| contains the
+  // temporary directory with all the unpacked CRX files. |pubkey| contains the
+  // public key of the CRX in the PEM format, without the header and the footer.
+  // The caller must invoke the |callback| when the install flow has completed.
+  // This method may be called from a thread other than the main thread.
+  virtual void Install(const base::FilePath& unpack_path,
+                       const std::string& public_key,
+                       Callback callback) = 0;
+
+  // Sets |installed_file| to the full path to the installed |file|. |file| is
+  // the filename of the file in this CRX. Returns false if this is
+  // not possible (the file has been removed or modified, or its current
+  // location is unknown). Otherwise, it returns true.
+  virtual bool GetInstalledFile(const std::string& file,
+                                base::FilePath* installed_file) = 0;
+
+  // Called when a CRX has been unregistered and all versions should
+  // be uninstalled from disk. Returns true if uninstallation is supported,
+  // and false otherwise.
+  virtual bool Uninstall() = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<CrxInstaller>;
+
+  virtual ~CrxInstaller() {}
+};
+
+// A dictionary of installer-specific, arbitrary name-value pairs, which
+// may be used in the update checks requests.
+using InstallerAttributes = std::map<std::string, std::string>;
+
+struct CrxComponent {
+  CrxComponent();
+  CrxComponent(const CrxComponent& other);
+  ~CrxComponent();
+
+  // SHA256 hash of the CRX's public key.
+  std::vector<uint8_t> pk_hash;
+  scoped_refptr<CrxInstaller> installer;
+
+  // The current version if the CRX is updated. Otherwise, "0" or "0.0" if
+  // the CRX is installed.
+  base::Version version;
+
+  std::string fingerprint;  // Optional.
+  std::string name;         // Optional.
+  std::vector<std::string> handled_mime_types;
+
+  // Optional.
+  // Valid values for the name part of an attribute match
+  // ^[-_a-zA-Z0-9]{1,256}$ and valid values the value part of an attribute
+  // match ^[-.,;+_=a-zA-Z0-9]{0,256}$ .
+  InstallerAttributes installer_attributes;
+
+  // Specifies that the CRX can be background-downloaded in some cases.
+  // The default for this value is |true|.
+  bool allows_background_download;
+
+  // Specifies that the update checks and pings associated with this component
+  // require confidentiality. The default for this value is |true|. As a side
+  // note, the confidentiality of the downloads is enforced by the server,
+  // which only returns secure download URLs in this case.
+  bool requires_network_encryption;
+
+  // Specifies the strength of package validation required for the item.
+  crx_file::VerifierFormat crx_format_requirement;
+
+  // True if the component allows enabling or disabling updates by group policy.
+  // This member should be set to |false| for data, non-binary components, such
+  // as CRLSet, Supervised User Whitelists, STH Set, Origin Trials, and File
+  // Type Policies.
+  bool supports_group_policy_enable_component_updates;
+
+  // Reasons why this component/extension is disabled.
+  std::vector<int> disabled_reasons;
+
+  // Information about where the component/extension was installed from.
+  // For extension, this information is set from the update service, which
+  // gets the install source from the update URL.
+  std::string install_source;
+
+  // Information about where the component/extension was loaded from.
+  // For extensions, this information is inferred from the extension
+  // registry.
+  std::string install_location;
+};
+
+// Called when a non-blocking call of UpdateClient completes.
+using Callback = base::OnceCallback<void(Error error)>;
+
+// All methods are safe to call only from the browser's main thread. Once an
+// instance of this class is created, the reference to it must be released
+// only after the thread pools of the browser process have been destroyed and
+// the browser process has gone single-threaded.
+class UpdateClient : public base::RefCounted<UpdateClient> {
+ public:
+  using CrxDataCallback =
+      base::OnceCallback<std::vector<base::Optional<CrxComponent>>(
+          const std::vector<std::string>& ids)>;
+
+  // Defines an interface to observe the UpdateClient. It provides
+  // notifications when state changes occur for the service itself or for the
+  // registered CRXs.
+  class Observer {
+   public:
+    enum class Events {
+      // Sent before the update client does an update check.
+      COMPONENT_CHECKING_FOR_UPDATES = 1,
+
+      // Sent when there is a new version of a registered CRX. After
+      // the notification is sent the CRX will be downloaded unless the
+      // update client inserts a
+      COMPONENT_UPDATE_FOUND,
+
+      // Sent when a CRX is in the update queue but it can't be acted on
+      // right away, because the update client spaces out CRX updates due to a
+      // throttling policy.
+      COMPONENT_WAIT,
+
+      // Sent after the new CRX has been downloaded but before the install
+      // or the upgrade is attempted.
+      COMPONENT_UPDATE_READY,
+
+      // Sent when a CRX has been successfully updated.
+      COMPONENT_UPDATED,
+
+      // Sent when a CRX has not been updated because there was no update
+      // available for this component.
+      COMPONENT_NOT_UPDATED,
+
+      // Sent when an error ocurred during an update for any reason, including
+      // the update check itself failed, or the download of the update payload
+      // failed, or applying the update failed.
+      COMPONENT_UPDATE_ERROR,
+
+      // Sent when CRX bytes are being downloaded.
+      COMPONENT_UPDATE_DOWNLOADING,
+    };
+
+    virtual ~Observer() {}
+
+    // Called by the update client when a state change happens.
+    // If an |id| is specified, then the event is fired on behalf of the
+    // specific CRX. The implementors of this interface are
+    // expected to filter the relevant events based on the id of the CRX.
+    virtual void OnEvent(Events event, const std::string& id) = 0;
+  };
+
+  // Adds an observer for this class. An observer should not be added more
+  // than once. The caller retains the ownership of the observer object.
+  virtual void AddObserver(Observer* observer) = 0;
+
+  // Removes an observer. It is safe for an observer to be removed while
+  // the observers are being notified.
+  virtual void RemoveObserver(Observer* observer) = 0;
+
+  // Installs the specified CRX. Calls back on |callback| after the
+  // update has been handled. The |error| parameter of the |callback|
+  // contains an error code in the case of a run-time error, or 0 if the
+  // install has been handled successfully. Overlapping calls of this function
+  // are executed concurrently, as long as the id parameter is different,
+  // meaning that installs of different components are parallelized.
+  // The |Install| function is intended to be used for foreground installs of
+  // one CRX. These cases are usually associated with on-demand install
+  // scenarios, which are triggered by user actions. Installs are never
+  // queued up.
+  virtual void Install(const std::string& id,
+                       CrxDataCallback crx_data_callback,
+                       Callback callback) = 0;
+
+  // Updates the specified CRXs. Calls back on |crx_data_callback| before the
+  // update is attempted to give the caller the opportunity to provide the
+  // instances of CrxComponent to be used for this update. The |Update| function
+  // is intended to be used for background updates of several CRXs. Overlapping
+  // calls to this function result in a queuing behavior, and the execution
+  // of each call is serialized. In addition, updates are always queued up when
+  // installs are running. The |is_foreground| parameter must be set to true if
+  // the invocation of this function is a result of a user initiated update.
+  virtual void Update(const std::vector<std::string>& ids,
+                      CrxDataCallback crx_data_callback,
+                      bool is_foreground,
+                      Callback callback) = 0;
+
+  // Sends an uninstall ping for the CRX identified by |id| and |version|. The
+  // |reason| parameter is defined by the caller. The current implementation of
+  // this function only sends a best-effort, fire-and-forget ping. It has no
+  // other side effects regarding installs or updates done through an instance
+  // of this class.
+  virtual void SendUninstallPing(const std::string& id,
+                                 const base::Version& version,
+                                 int reason,
+                                 Callback callback) = 0;
+
+  // Returns status details about a CRX update. The function returns true in
+  // case of success and false in case of errors, such as |id| was
+  // invalid or not known.
+  virtual bool GetCrxUpdateState(const std::string& id,
+                                 CrxUpdateItem* update_item) const = 0;
+
+  // Returns true if the |id| is found in any running task.
+  virtual bool IsUpdating(const std::string& id) const = 0;
+
+  // Cancels the queued updates and makes a best effort to stop updates in
+  // progress as soon as possible. Some updates may not be stopped, in which
+  // case, the updates will run to completion. Calling this function has no
+  // effect if updates are not currently executed or queued up.
+  virtual void Stop() = 0;
+
+ protected:
+  friend class base::RefCounted<UpdateClient>;
+
+  virtual ~UpdateClient() {}
+};
+
+// Creates an instance of the update client.
+scoped_refptr<UpdateClient> UpdateClientFactory(
+    scoped_refptr<Configurator> config);
+
+// This must be called prior to the construction of any Configurator that
+// contains a PrefService.
+void RegisterPrefs(PrefRegistrySimple* registry);
+
+// This must be called prior to the construction of any Configurator that
+// needs access to local user profiles.
+// This function is mostly used for ExtensionUpdater, which requires update
+// info from user profiles.
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_H_
diff --git a/src/updater_components/update_client/update_client_errors.h b/src/updater_components/update_client/update_client_errors.h
new file mode 100644
index 0000000..3ee0509
--- /dev/null
+++ b/src/updater_components/update_client/update_client_errors.h
@@ -0,0 +1,118 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_ERRORS_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_ERRORS_H_
+
+namespace update_client {
+
+// Errors generated as a result of calling UpdateClient member functions.
+// These errors are not sent in pings.
+enum class Error {
+  NONE = 0,
+  UPDATE_IN_PROGRESS = 1,
+  UPDATE_CANCELED = 2,
+  RETRY_LATER = 3,
+  SERVICE_ERROR = 4,
+  UPDATE_CHECK_ERROR = 5,
+  CRX_NOT_FOUND = 6,
+  INVALID_ARGUMENT = 7,
+  MAX_VALUE,
+};
+
+// These errors are sent in pings. Add new values only to the bottom of
+// the enums below; the order must be kept stable.
+enum class ErrorCategory {
+  kNone = 0,
+  kDownload,
+  kUnpack,
+  kInstall,
+  kService,  // Runtime errors which occur in the service itself.
+  kUpdateCheck,
+};
+
+// These errors are returned with the |kNetworkError| error category. This
+// category could include other errors such as the errors defined by
+// the Chrome net stack.
+enum class CrxDownloaderError {
+  NONE = 0,
+  NO_URL = 10,
+  NO_HASH = 11,
+  BAD_HASH = 12,  // The downloaded file fails the hash verification.
+  // The Windows BITS queue contains to many update client jobs. The value is
+  // chosen so that it can be reported as a custom COM error on this platform.
+  BITS_TOO_MANY_JOBS = 0x0200,
+  GENERIC_ERROR = -1
+};
+
+// These errors are returned with the |kUnpack| error category and
+// indicate unpacker or patcher error.
+enum class UnpackerError {
+  kNone = 0,
+  kInvalidParams = 1,
+  kInvalidFile = 2,
+  kUnzipPathError = 3,
+  kUnzipFailed = 4,
+  // kNoManifest = 5,         // Deprecated. Never used.
+  kBadManifest = 6,
+  kBadExtension = 7,
+  // kInvalidId = 8,          // Deprecated. Combined with kInvalidFile.
+  // kInstallerError = 9,     // Deprecated. Don't use.
+  kIoError = 10,
+  kDeltaVerificationFailure = 11,
+  kDeltaBadCommands = 12,
+  kDeltaUnsupportedCommand = 13,
+  kDeltaOperationFailure = 14,
+  kDeltaPatchProcessFailure = 15,
+  kDeltaMissingExistingFile = 16,
+  // kFingerprintWriteFailed = 17,    // Deprecated. Don't use.
+};
+
+// These errors are returned with the |kService| error category and
+// are returned by the component installers.
+enum class InstallError {
+  NONE = 0,
+  FINGERPRINT_WRITE_FAILED = 2,
+  BAD_MANIFEST = 3,
+  GENERIC_ERROR = 9,  // Matches kInstallerError for compatibility.
+  MOVE_FILES_ERROR = 10,
+  SET_PERMISSIONS_FAILED = 11,
+  INVALID_VERSION = 12,
+  VERSION_NOT_UPGRADED = 13,
+  NO_DIR_COMPONENT_USER = 14,
+  CLEAN_INSTALL_DIR_FAILED = 15,
+  INSTALL_VERIFICATION_FAILED = 16,
+  CUSTOM_ERROR_BASE = 100,  // Specific installer errors go above this value.
+};
+
+// These errors are returned with the |kService| error category and
+// indicate critical or configuration errors in the update service.
+enum class ServiceError {
+  NONE = 0,
+  SERVICE_WAIT_FAILED = 1,
+  UPDATE_DISABLED = 2,
+};
+
+// These errors are related to serialization, deserialization, and parsing of
+// protocol requests.
+// The begin value for this enum is chosen not to conflict with network errors
+// defined by net/base/net_error_list.h. The callers don't have to handle this
+// error in any meaningful way, but this value may be reported in UMA stats,
+// therefore avoiding collisions with known network errors is desirable.
+enum class ProtocolError : int {
+  NONE = 0,
+  RESPONSE_NOT_TRUSTED = -10000,
+  MISSING_PUBLIC_KEY = -10001,
+  MISSING_URLS = -10002,
+  PARSE_FAILED = -10003,
+  UPDATE_RESPONSE_NOT_FOUND = -10004,
+  URL_FETCHER_FAILED = -10005,
+  UNKNOWN_APPLICATION = -10006,
+  RESTRICTED_APPLICATION = -10007,
+  INVALID_APPID = -10008,
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_ERRORS_H_
diff --git a/src/updater_components/update_client/update_client_internal.h b/src/updater_components/update_client/update_client_internal.h
new file mode 100644
index 0000000..2b6158c
--- /dev/null
+++ b/src/updater_components/update_client/update_client_internal.h
@@ -0,0 +1,92 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/containers/circular_deque.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "components/update_client/crx_downloader.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client.h"
+
+namespace update_client {
+
+class Configurator;
+class PingManager;
+class Task;
+class UpdateEngine;
+enum class Error;
+
+class UpdateClientImpl : public UpdateClient {
+ public:
+  UpdateClientImpl(scoped_refptr<Configurator> config,
+                   scoped_refptr<PingManager> ping_manager,
+                   UpdateChecker::Factory update_checker_factory,
+                   CrxDownloader::Factory crx_downloader_factory);
+
+  // Overrides for UpdateClient.
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+  void Install(const std::string& id,
+               CrxDataCallback crx_data_callback,
+               Callback callback) override;
+  void Update(const std::vector<std::string>& ids,
+              CrxDataCallback crx_data_callback,
+              bool is_foreground,
+              Callback callback) override;
+  bool GetCrxUpdateState(const std::string& id,
+                         CrxUpdateItem* update_item) const override;
+  bool IsUpdating(const std::string& id) const override;
+  void Stop() override;
+  void SendUninstallPing(const std::string& id,
+                         const base::Version& version,
+                         int reason,
+                         Callback callback) override;
+
+ private:
+  ~UpdateClientImpl() override;
+
+  void RunTask(scoped_refptr<Task> task);
+  void OnTaskComplete(Callback callback, scoped_refptr<Task> task, Error error);
+
+  void NotifyObservers(Observer::Events event, const std::string& id);
+
+  base::ThreadChecker thread_checker_;
+
+  // True if Stop method has been called.
+  bool is_stopped_ = false;
+
+  scoped_refptr<Configurator> config_;
+
+  // Contains the tasks that are pending. In the current implementation,
+  // only update tasks (background tasks) are queued up. These tasks are
+  // pending while they are in this queue. They have not been picked up yet
+  // by the update engine.
+  base::circular_deque<scoped_refptr<Task>> task_queue_;
+
+  // Contains all tasks in progress. These are the tasks that the update engine
+  // is executing at one moment. Install tasks are run concurrently, update
+  // tasks are always serialized, and update tasks are queued up if install
+  // tasks are running. In addition, concurrent install tasks for the same id
+  // are not allowed.
+  std::set<scoped_refptr<Task>> tasks_;
+  scoped_refptr<PingManager> ping_manager_;
+  scoped_refptr<UpdateEngine> update_engine_;
+  base::ObserverList<Observer>::Unchecked observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateClientImpl);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_INTERNAL_H_
diff --git a/src/updater_components/update_client/update_client_unittest.cc b/src/updater_components/update_client/update_client_unittest.cc
new file mode 100644
index 0000000..2a614f4
--- /dev/null
+++ b/src/updater_components/update_client/update_client_unittest.cc
@@ -0,0 +1,3933 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/test/scoped_path_override.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "build/build_config.h"
+#include "components/crx_file/crx_verifier.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/update_client/component_unpacker.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/network.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/persisted_data.h"
+#include "components/update_client/ping_manager.h"
+#include "components/update_client/protocol_handler.h"
+#include "components/update_client/test_configurator.h"
+#include "components/update_client/test_installer.h"
+#include "components/update_client/unzipper.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/update_client_internal.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+namespace {
+
+using base::FilePath;
+
+// Makes a copy of the file specified by |from_path| in a temporary directory
+// and returns the path of the copy. Returns true if successful. Cleans up if
+// there was an error creating the copy.
+bool MakeTestFile(const FilePath& from_path, FilePath* to_path) {
+  FilePath temp_dir;
+  bool result =
+      CreateNewTempDirectory(FILE_PATH_LITERAL("update_client"), &temp_dir);
+  if (!result)
+    return false;
+
+  FilePath temp_file;
+  result = CreateTemporaryFileInDir(temp_dir, &temp_file);
+  if (!result)
+    return false;
+
+  result = CopyFile(from_path, temp_file);
+  if (!result) {
+    DeleteFile(temp_file, false);
+    return false;
+  }
+
+  *to_path = temp_file;
+  return true;
+}
+
+using Events = UpdateClient::Observer::Events;
+
+class MockObserver : public UpdateClient::Observer {
+ public:
+  MOCK_METHOD2(OnEvent, void(Events event, const std::string&));
+};
+
+}  // namespace
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::AnyNumber;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Mock;
+using ::testing::Return;
+
+using std::string;
+
+class MockPingManagerImpl : public PingManager {
+ public:
+  struct PingData {
+    std::string id;
+    base::Version previous_version;
+    base::Version next_version;
+    ErrorCategory error_category = ErrorCategory::kNone;
+    int error_code = 0;
+    int extra_code1 = 0;
+    ErrorCategory diff_error_category = ErrorCategory::kNone;
+    int diff_error_code = 0;
+    bool diff_update_failed = false;
+  };
+
+  explicit MockPingManagerImpl(scoped_refptr<Configurator> config);
+
+  void SendPing(const Component& component, Callback callback) override;
+
+  const std::vector<PingData>& ping_data() const;
+
+  const std::vector<base::Value>& events() const;
+
+ protected:
+  ~MockPingManagerImpl() override;
+
+ private:
+  std::vector<PingData> ping_data_;
+  std::vector<base::Value> events_;
+  DISALLOW_COPY_AND_ASSIGN(MockPingManagerImpl);
+};
+
+MockPingManagerImpl::MockPingManagerImpl(scoped_refptr<Configurator> config)
+    : PingManager(config) {}
+
+MockPingManagerImpl::~MockPingManagerImpl() {}
+
+void MockPingManagerImpl::SendPing(const Component& component,
+                                   Callback callback) {
+  PingData ping_data;
+  ping_data.id = component.id_;
+  ping_data.previous_version = component.previous_version_;
+  ping_data.next_version = component.next_version_;
+  ping_data.error_category = component.error_category_;
+  ping_data.error_code = component.error_code_;
+  ping_data.extra_code1 = component.extra_code1_;
+  ping_data.diff_error_category = component.diff_error_category_;
+  ping_data.diff_error_code = component.diff_error_code_;
+  ping_data.diff_update_failed = component.diff_update_failed();
+  ping_data_.push_back(ping_data);
+
+  events_ = component.GetEvents();
+
+  std::move(callback).Run(0, "");
+}
+
+const std::vector<MockPingManagerImpl::PingData>&
+MockPingManagerImpl::ping_data() const {
+  return ping_data_;
+}
+
+const std::vector<base::Value>& MockPingManagerImpl::events() const {
+  return events_;
+}
+
+class UpdateClientTest : public testing::Test {
+ public:
+  UpdateClientTest();
+  ~UpdateClientTest() override;
+
+ protected:
+  void RunThreads();
+
+  // Returns the full path to a test file.
+  static base::FilePath TestFilePath(const char* file);
+
+  scoped_refptr<update_client::TestConfigurator> config() { return config_; }
+  update_client::PersistedData* metadata() { return metadata_.get(); }
+
+  base::OnceClosure quit_closure() { return runloop_.QuitClosure(); }
+
+ private:
+  static constexpr int kNumWorkerThreads_ = 2;
+
+  base::test::TaskEnvironment task_environment_;
+  base::RunLoop runloop_;
+
+  scoped_refptr<update_client::TestConfigurator> config_ =
+      base::MakeRefCounted<TestConfigurator>();
+  std::unique_ptr<TestingPrefServiceSimple> pref_ =
+      std::make_unique<TestingPrefServiceSimple>();
+  std::unique_ptr<update_client::PersistedData> metadata_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateClientTest);
+};
+
+constexpr int UpdateClientTest::kNumWorkerThreads_;
+
+UpdateClientTest::UpdateClientTest() {
+  PersistedData::RegisterPrefs(pref_->registry());
+  metadata_ = std::make_unique<PersistedData>(pref_.get(), nullptr);
+}
+
+UpdateClientTest::~UpdateClientTest() {
+}
+
+void UpdateClientTest::RunThreads() {
+  runloop_.Run();
+  task_environment_.RunUntilIdle();
+}
+
+base::FilePath UpdateClientTest::TestFilePath(const char* file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("update_client")
+      .AppendASCII(file);
+}
+
+// Tests the scenario where one update check is done for one CRX. The CRX
+// has no update.
+TEST_F(UpdateClientTest, OneCrxNoUpdate) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.9");
+      crx.installer = base::MakeRefCounted<TestInstaller>();
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      std::vector<base::Optional<CrxComponent>> component = {crx};
+      return component;
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(1u, ids_to_check.size());
+      const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+      EXPECT_EQ(id, ids_to_check.front());
+      EXPECT_EQ(1u, components.count(id));
+
+      auto& component = components.at(id);
+
+      EXPECT_TRUE(component->is_foreground());
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "noupdate";
+
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  InSequence seq;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), true,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the scenario where two CRXs are checked for updates. On CRX has
+// an update, the other CRX does not.
+TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx1;
+      crx1.name = "test_jebg";
+      crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx1.version = base::Version("0.9");
+      crx1.installer = base::MakeRefCounted<TestInstaller>();
+      crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      CrxComponent crx2;
+      crx2.name = "test_abag";
+      crx2.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash));
+      crx2.version = base::Version("2.2");
+      crx2.installer = base::MakeRefCounted<TestInstaller>();
+      crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      return {crx1, crx2};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+                         hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+                                      7c9b12cb7cc067667bde87'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+        <app appid='abagagagagagagagagagagagagagagag'>
+          <updatecheck status='noupdate'/>
+        </app>
+      </response>
+      */
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(2u, ids_to_check.size());
+
+      ProtocolParser::Results results;
+      {
+        const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+        package.hash_sha256 =
+            "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+        ProtocolParser::Result result;
+        result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf";
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+
+        EXPECT_FALSE(components.at(id)->is_foreground());
+      }
+
+      {
+        const std::string id = "abagagagagagagagagagagagagagagag";
+        EXPECT_EQ(id, ids_to_check[1]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "noupdate";
+        results.list.push_back(result);
+
+        EXPECT_FALSE(components.at(id)->is_foreground());
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      download_metrics.url = url;
+      download_metrics.downloader = DownloadMetrics::kNone;
+      download_metrics.error = 0;
+      download_metrics.downloaded_bytes = 1843;
+      download_metrics.total_bytes = 1843;
+      download_metrics.download_time_ms = 1000;
+
+      FilePath path;
+      EXPECT_TRUE(MakeTestFile(
+          TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+      Result result;
+      result.error = 0;
+      result.response = path;
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(1u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(0, ping_data[0].error_code);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "abagagagagagagagagagagagagagagag")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                  "abagagagagagagagagagagagagagagag")).Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
+                                        "abagagagagagagagagagagagagagagag"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the scenario where two CRXs are checked for updates. One CRX has
+// an update but the server ignores the second CRX and returns no response for
+// it. The second component gets an |UPDATE_RESPONSE_NOT_FOUND| error and
+// transitions to the error state.
+TEST_F(UpdateClientTest, TwoCrxUpdateFirstServerIgnoresSecond) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx1;
+      crx1.name = "test_jebg";
+      crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx1.version = base::Version("0.9");
+      crx1.installer = base::MakeRefCounted<TestInstaller>();
+      crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      CrxComponent crx2;
+      crx2.name = "test_abag";
+      crx2.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash));
+      crx2.version = base::Version("2.2");
+      crx2.installer = base::MakeRefCounted<TestInstaller>();
+      crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      return {crx1, crx2};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+                         hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+                                      7c9b12cb7cc067667bde87'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+      </response>
+      */
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(2u, ids_to_check.size());
+
+      ProtocolParser::Results results;
+      {
+        const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+        package.hash_sha256 =
+            "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+        ProtocolParser::Result result;
+        result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf";
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+
+        EXPECT_FALSE(components.at(id)->is_foreground());
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      download_metrics.url = url;
+      download_metrics.downloader = DownloadMetrics::kNone;
+      download_metrics.error = 0;
+      download_metrics.downloaded_bytes = 1843;
+      download_metrics.total_bytes = 1843;
+      download_metrics.download_time_ms = 1000;
+
+      FilePath path;
+      EXPECT_TRUE(MakeTestFile(
+          TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+      Result result;
+      result.error = 0;
+      result.response = path;
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(1u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(0, ping_data[0].error_code);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "abagagagagagagagagagagagagagagag"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "abagagagagagagagagagagagagagagag"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_EQ(5, static_cast<int>(item.error_category));
+          EXPECT_EQ(-10004, item.error_code);
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
+                                        "abagagagagagagagagagagagagagagag"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the update check for two CRXs scenario when the second CRX does not
+// provide a CrxComponent instance. In this case, the update is handled as
+// if only one component were provided as an argument to the |Update| call
+// with the exception that the second component still fires an event such as
+// |COMPONENT_UPDATE_ERROR|.
+TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentData) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.9");
+      crx.installer = base::MakeRefCounted<TestInstaller>();
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      return {crx, base::nullopt};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+                         hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+                                      7c9b12cb7cc067667bde87'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+      </response>
+      */
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(1u, ids_to_check.size());
+
+      ProtocolParser::Results results;
+      {
+        const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+        package.hash_sha256 =
+            "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+
+        EXPECT_FALSE(components.at(id)->is_foreground());
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      FilePath path;
+      Result result;
+      if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 1843;
+        download_metrics.total_bytes = 1843;
+        download_metrics.download_time_ms = 1000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(1u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(0, ping_data[0].error_code);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
+                                        "ihfokbkgjpifnbbojhneepfflplebdkc"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the update check for two CRXs scenario when no CrxComponent data is
+// provided for either component. In this case, no update check occurs, and
+// |COMPONENT_UPDATE_ERROR| event fires for both components.
+TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentDataAtAll) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      return {base::nullopt, base::nullopt};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      NOTREACHED();
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { NOTREACHED(); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      EXPECT_EQ(0u, MockPingManagerImpl::ping_data().size());
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
+                                        "ihfokbkgjpifnbbojhneepfflplebdkc"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the scenario where there is a download timeout for the first
+// CRX. The update for the first CRX fails. The update client waits before
+// attempting the update for the second CRX. This update succeeds.
+TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx1;
+      crx1.name = "test_jebg";
+      crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx1.version = base::Version("0.9");
+      crx1.installer = base::MakeRefCounted<TestInstaller>();
+      crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      CrxComponent crx2;
+      crx2.name = "test_ihfo";
+      crx2.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash));
+      crx2.version = base::Version("0.8");
+      crx2.installer = base::MakeRefCounted<TestInstaller>();
+      crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      return {crx1, crx2};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+                         hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+                                      7c9b12cb7cc067667bde87'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+        <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx'
+                         hash_sha256='813c59747e139a608b3b5fc49633affc6db574373f
+                                      309f156ea6d27229c0b3f9'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+      </response>
+      */
+
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(2u, ids_to_check.size());
+
+      ProtocolParser::Results results;
+      {
+        const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+        package.hash_sha256 =
+            "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      }
+
+      {
+        const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+        EXPECT_EQ(id, ids_to_check[1]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
+        package.hash_sha256 =
+            "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      FilePath path;
+      Result result;
+      if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = -118;
+        download_metrics.downloaded_bytes = 0;
+        download_metrics.total_bytes = 0;
+        download_metrics.download_time_ms = 1000;
+
+        // The result must not include a file path in the case of errors.
+        result.error = -118;
+      } else if (url.path() ==
+                 "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 53638;
+        download_metrics.total_bytes = 53638;
+        download_metrics.download_time_ms = 2000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(2u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(1, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(-118, ping_data[0].error_code);
+      EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id);
+      EXPECT_EQ(base::Version("0.8"), ping_data[1].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[1].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category));
+      EXPECT_EQ(0, ping_data[1].error_code);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_EQ(1, static_cast<int>(item.error_category));
+          EXPECT_EQ(-118, item.error_code);
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_WAIT,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
+                                        "ihfokbkgjpifnbbojhneepfflplebdkc"};
+
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the differential update scenario for one CRX.
+TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      static int num_calls = 0;
+
+      // Must use the same stateful installer object.
+      static scoped_refptr<CrxInstaller> installer =
+          base::MakeRefCounted<VersionedTestInstaller>();
+
+      ++num_calls;
+
+      CrxComponent crx;
+      crx.name = "test_ihfo";
+      crx.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash));
+      crx.installer = installer;
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      if (num_calls == 1) {
+        crx.version = base::Version("0.8");
+      } else if (num_calls == 2) {
+        crx.version = base::Version("1.0");
+      } else {
+        NOTREACHED();
+      }
+
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+
+      static int num_call = 0;
+      ++num_call;
+
+      ProtocolParser::Results results;
+
+      if (num_call == 1) {
+        /*
+        Mock the following response:
+        <?xml version='1.0' encoding='UTF-8'?>
+        <response protocol='3.1'>
+          <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+            <updatecheck status='ok'>
+              <urls>
+                <url codebase='http://localhost/download/'/>
+              </urls>
+              <manifest version='1.0' prodversionmin='11.0.1.0'>
+                <packages>
+                  <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx'
+                           hash_sha256='813c59747e139a608b3b5fc49633affc6db57437
+                                        3f309f156ea6d27229c0b3f9'/>
+                </packages>
+              </manifest>
+            </updatecheck>
+          </app>
+        </response>
+        */
+        const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
+        package.hash_sha256 =
+            "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      } else if (num_call == 2) {
+        /*
+        Mock the following response:
+        <?xml version='1.0' encoding='UTF-8'?>
+        <response protocol='3.1'>
+          <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+            <updatecheck status='ok'>
+              <urls>
+                <url codebase='http://localhost/download/'/>
+                <url codebasediff='http://localhost/download/'/>
+              </urls>
+              <manifest version='2.0' prodversionmin='11.0.1.0'>
+                <packages>
+                  <package name='ihfokbkgjpifnbbojhneepfflplebdkc_2.crx'
+                           namediff='ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx'
+                           hash_sha256='1af337fbd19c72db0f870753bcd7711c3ae9dcaa
+                                        0ecde26c262bad942b112990'
+                           fp='22'
+                           hashdiff_sha256='73c6e2d4f783fc4ca5481e89e0b8bfce7aec
+                                            8ead3686290c94792658ec06f2f2'/>
+                </packages>
+              </manifest>
+            </updatecheck>
+          </app>
+        </response>
+        */
+        const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx";
+        package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx";
+        package.hash_sha256 =
+            "1af337fbd19c72db0f870753bcd7711c3ae9dcaa0ecde26c262bad942b112990";
+        package.hashdiff_sha256 =
+            "73c6e2d4f783fc4ca5481e89e0b8bfce7aec8ead3686290c94792658ec06f2f2";
+        package.fingerprint = "22";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.crx_diffurls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "2.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      FilePath path;
+      Result result;
+      if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 53638;
+        download_metrics.total_bytes = 53638;
+        download_metrics.download_time_ms = 2000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else if (url.path() ==
+                 "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 2105;
+        download_metrics.total_bytes = 2105;
+        download_metrics.download_time_ms = 1000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(2u, ping_data.size());
+      EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.8"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(0, ping_data[0].error_code);
+      EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id);
+      EXPECT_EQ(base::Version("1.0"), ping_data[1].previous_version);
+      EXPECT_EQ(base::Version("2.0"), ping_data[1].next_version);
+      EXPECT_FALSE(ping_data[1].diff_update_failed);
+      EXPECT_EQ(0, static_cast<int>(ping_data[1].diff_error_category));
+      EXPECT_EQ(0, ping_data[1].diff_error_code);
+      EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category));
+      EXPECT_EQ(0, ping_data[1].error_code);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"ihfokbkgjpifnbbojhneepfflplebdkc"};
+  {
+    base::RunLoop runloop;
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+                          false,
+                          base::BindOnce(&CompletionCallbackMock::Callback,
+                                         runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    base::RunLoop runloop;
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+                          false,
+                          base::BindOnce(&CompletionCallbackMock::Callback,
+                                         runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the update scenario for one CRX where the CRX installer returns
+// an error. Tests that the |unpack_path| argument refers to a valid path
+// then |Install| is called, then tests that the |unpack| path is deleted
+// by the |update_client| code before the test ends.
+TEST_F(UpdateClientTest, OneCrxInstallError) {
+  class MockInstaller : public CrxInstaller {
+   public:
+    MOCK_METHOD1(OnUpdateError, void(int error));
+    MOCK_METHOD2(DoInstall,
+                 void(const base::FilePath& unpack_path,
+                      const Callback& callback));
+    MOCK_METHOD2(GetInstalledFile,
+                 bool(const std::string& file, base::FilePath* installed_file));
+    MOCK_METHOD0(Uninstall, bool());
+
+    void Install(const base::FilePath& unpack_path,
+                 const std::string& public_key,
+                 Callback callback) override {
+      DoInstall(unpack_path, std::move(callback));
+
+      unpack_path_ = unpack_path;
+      EXPECT_TRUE(base::DirectoryExists(unpack_path_));
+      base::PostTask(
+          FROM_HERE, {base::ThreadPool(), base::MayBlock()},
+          base::BindOnce(std::move(callback),
+                         CrxInstaller::Result(InstallError::GENERIC_ERROR)));
+    }
+
+   protected:
+    ~MockInstaller() override {
+      // The unpack path is deleted unconditionally by the component state code,
+      // which is driving this installer. Therefore, the unpack path must not
+      // exist when this object is destroyed.
+      if (!unpack_path_.empty())
+        EXPECT_FALSE(base::DirectoryExists(unpack_path_));
+    }
+
+   private:
+    // Contains the |unpack_path| argument of the Install call.
+    base::FilePath unpack_path_;
+  };
+
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      scoped_refptr<MockInstaller> installer =
+          base::MakeRefCounted<MockInstaller>();
+
+      EXPECT_CALL(*installer, OnUpdateError(_)).Times(0);
+      EXPECT_CALL(*installer, DoInstall(_, _)).Times(1);
+      EXPECT_CALL(*installer, GetInstalledFile(_, _)).Times(0);
+      EXPECT_CALL(*installer, Uninstall()).Times(0);
+
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.9");
+      crx.installer = installer;
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+                         hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+                                      7c9b12cb7cc067667bde87'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+      </response>
+      */
+      EXPECT_FALSE(session_id.empty());
+
+      const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+      EXPECT_EQ(id, ids_to_check[0]);
+      EXPECT_EQ(1u, components.count(id));
+
+      ProtocolParser::Result::Manifest::Package package;
+      package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+      package.hash_sha256 =
+          "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "ok";
+      result.crx_urls.push_back(GURL("http://localhost/download/"));
+      result.manifest.version = "1.0";
+      result.manifest.browser_min_version = "11.0.1.0";
+      result.manifest.packages.push_back(package);
+
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      download_metrics.url = url;
+      download_metrics.downloader = DownloadMetrics::kNone;
+      download_metrics.error = 0;
+      download_metrics.downloaded_bytes = 1843;
+      download_metrics.total_bytes = 1843;
+      download_metrics.download_time_ms = 1000;
+
+      FilePath path;
+      EXPECT_TRUE(MakeTestFile(
+          TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+      Result result;
+      result.error = 0;
+      result.response = path;
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(1u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(3, static_cast<int>(ping_data[0].error_category));  // kInstall.
+      EXPECT_EQ(9, ping_data[0].error_code);  // kInstallerError.
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the fallback from differential to full update scenario for one CRX.
+TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      static int num_calls = 0;
+
+      // Must use the same stateful installer object.
+      static scoped_refptr<CrxInstaller> installer =
+          base::MakeRefCounted<VersionedTestInstaller>();
+
+      ++num_calls;
+
+      CrxComponent crx;
+      crx.name = "test_ihfo";
+      crx.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash));
+      crx.installer = installer;
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      if (num_calls == 1) {
+        crx.version = base::Version("0.8");
+      } else if (num_calls == 2) {
+        crx.version = base::Version("1.0");
+      } else {
+        NOTREACHED();
+      }
+
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+
+      static int num_call = 0;
+      ++num_call;
+
+      ProtocolParser::Results results;
+
+      if (num_call == 1) {
+        /*
+        Mock the following response:
+        <?xml version='1.0' encoding='UTF-8'?>
+        <response protocol='3.1'>
+          <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+            <updatecheck status='ok'>
+              <urls>
+                <url codebase='http://localhost/download/'/>
+              </urls>
+              <manifest version='1.0' prodversionmin='11.0.1.0'>
+                <packages>
+                  <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx'
+                           hash_sha256='813c59747e139a608b3b5fc49633affc6db57437
+                                        3f309f156ea6d27229c0b3f9'
+                           fp='1'/>
+                </packages>
+              </manifest>
+            </updatecheck>
+          </app>
+        </response>
+        */
+        const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
+        package.hash_sha256 =
+            "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9";
+        package.fingerprint = "1";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      } else if (num_call == 2) {
+        /*
+        Mock the following response:
+        <?xml version='1.0' encoding='UTF-8'?>
+        <response protocol='3.1'>
+          <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+            <updatecheck status='ok'>
+              <urls>
+                <url codebase='http://localhost/download/'/>
+                <url codebasediff='http://localhost/download/'/>
+              </urls>
+              <manifest version='2.0' prodversionmin='11.0.1.0'>
+                <packages>
+                  <package name='ihfokbkgjpifnbbojhneepfflplebdkc_2.crx'
+                           namediff='ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx'
+                           hash_sha256='1af337fbd19c72db0f870753bcd7711c3ae9dcaa
+                                        0ecde26c262bad942b112990'
+                           fp='22'
+                           hashdiff_sha256='73c6e2d4f783fc4ca5481e89e0b8bfce7aec
+                                            8ead3686290c94792658ec06f2f2'/>
+                </packages>
+              </manifest>
+            </updatecheck>
+          </app>
+        </response>
+        */
+        const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx";
+        package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx";
+        package.hash_sha256 =
+            "1af337fbd19c72db0f870753bcd7711c3ae9dcaa0ecde26c262bad942b112990";
+        package.hashdiff_sha256 =
+            "73c6e2d4f783fc4ca5481e89e0b8bfce7aec8ead3686290c94792658ec06f2f2";
+        package.fingerprint = "22";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.crx_diffurls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "2.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      FilePath path;
+      Result result;
+      if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 53638;
+        download_metrics.total_bytes = 53638;
+        download_metrics.download_time_ms = 2000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else if (url.path() ==
+                 "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") {
+        // A download error is injected on this execution path.
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = -1;
+        download_metrics.downloaded_bytes = 0;
+        download_metrics.total_bytes = 2105;
+        download_metrics.download_time_ms = 1000;
+
+        // The response must not include a file path in the case of errors.
+        result.error = -1;
+      } else if (url.path() ==
+                 "/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 53855;
+        download_metrics.total_bytes = 53855;
+        download_metrics.download_time_ms = 1000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(2u, ping_data.size());
+      EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.8"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(0, ping_data[0].error_code);
+      EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id);
+      EXPECT_EQ(base::Version("1.0"), ping_data[1].previous_version);
+      EXPECT_EQ(base::Version("2.0"), ping_data[1].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category));
+      EXPECT_EQ(0, ping_data[1].error_code);
+      EXPECT_TRUE(ping_data[1].diff_update_failed);
+      EXPECT_EQ(1, static_cast<int>(ping_data[1].diff_error_category));
+      EXPECT_EQ(-1, ping_data[1].diff_error_code);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"ihfokbkgjpifnbbojhneepfflplebdkc"};
+
+  {
+    base::RunLoop runloop;
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+                          false,
+                          base::BindOnce(&CompletionCallbackMock::Callback,
+                                         runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    base::RunLoop runloop;
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+                          false,
+                          base::BindOnce(&CompletionCallbackMock::Callback,
+                                         runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the queuing of update checks. In this scenario, two update checks are
+// done for one CRX. The second update check call is queued up and will run
+// after the first check has completed. The CRX has no updates.
+TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.9");
+      crx.installer = base::MakeRefCounted<TestInstaller>();
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      static int num_call = 0;
+      ++num_call;
+
+      EXPECT_EQ(Error::NONE, error);
+
+      if (num_call == 2)
+        std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(1u, ids_to_check.size());
+      const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+      EXPECT_EQ(id, ids_to_check.front());
+      EXPECT_EQ(1u, components.count(id));
+
+      auto& component = components.at(id);
+
+      EXPECT_FALSE(component->is_foreground());
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "noupdate";
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  InSequence seq;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the install of one CRX.
+TEST_F(UpdateClientTest, OneCrxInstall) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.0");
+      crx.installer = base::MakeRefCounted<TestInstaller>();
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+                         hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+                                      7c9b12cb7cc067667bde87'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+      </response>
+      */
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(1u, ids_to_check.size());
+
+      const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+      EXPECT_EQ(id, ids_to_check[0]);
+      EXPECT_EQ(1u, components.count(id));
+
+      ProtocolParser::Result::Manifest::Package package;
+      package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+      package.hash_sha256 =
+          "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "ok";
+      result.crx_urls.push_back(GURL("http://localhost/download/"));
+      result.manifest.version = "1.0";
+      result.manifest.browser_min_version = "11.0.1.0";
+      result.manifest.packages.push_back(package);
+
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+
+      // Verify that calling Install sets ondemand.
+      EXPECT_TRUE(components.at(id)->is_foreground());
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      FilePath path;
+      Result result;
+      if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 1843;
+        download_metrics.total_bytes = 1843;
+        download_metrics.download_time_ms = 1000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(1u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.0"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(0, ping_data[0].error_code);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  InSequence seq;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(AtLeast(1));
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf")).Times(1);
+
+  update_client->AddObserver(&observer);
+
+  update_client->Install(
+      std::string("jebgalgnebhfojomionfpkfelancnnkf"),
+      base::BindOnce(&DataCallbackMock::Callback),
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the install of one CRX when no component data is provided. This
+// results in an install error.
+TEST_F(UpdateClientTest, OneCrxInstallNoCrxComponentData) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      return {base::nullopt};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      NOTREACHED();
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { NOTREACHED(); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      EXPECT_EQ(0u, MockPingManagerImpl::ping_data().size());
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  InSequence seq;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1)
+      .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+        // Tests that the state of the component when the CrxComponent data
+        // is not provided. In this case, the optional |item.component| instance
+        // is not present.
+        CrxUpdateItem item;
+        EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+        EXPECT_EQ(ComponentState::kUpdateError, item.state);
+        EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", item.id.c_str());
+        EXPECT_FALSE(item.component);
+        EXPECT_EQ(ErrorCategory::kService, item.error_category);
+        EXPECT_EQ(static_cast<int>(Error::CRX_NOT_FOUND), item.error_code);
+        EXPECT_EQ(0, item.extra_code1);
+      }));
+
+  update_client->AddObserver(&observer);
+
+  update_client->Install(
+      std::string("jebgalgnebhfojomionfpkfelancnnkf"),
+      base::BindOnce(&DataCallbackMock::Callback),
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests that overlapping installs of the same CRX result in an error.
+TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.0");
+      crx.installer = base::MakeRefCounted<TestInstaller>();
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      static int num_call = 0;
+      ++num_call;
+
+      EXPECT_LE(num_call, 2);
+
+      if (num_call == 1) {
+        EXPECT_EQ(Error::UPDATE_IN_PROGRESS, error);
+        return;
+      }
+      if (num_call == 2) {
+        EXPECT_EQ(Error::NONE, error);
+        std::move(quit_closure).Run();
+      }
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(1u, ids_to_check.size());
+      const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+      EXPECT_EQ(id, ids_to_check.front());
+      EXPECT_EQ(1u, components.count(id));
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "noupdate";
+
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+
+      // Verify that calling Install sets |is_foreground| for the component.
+      EXPECT_TRUE(components.at(id)->is_foreground());
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+
+  update_client->AddObserver(&observer);
+
+  update_client->Install(
+      std::string("jebgalgnebhfojomionfpkfelancnnkf"),
+      base::BindOnce(&DataCallbackMock::Callback),
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  update_client->Install(
+      std::string("jebgalgnebhfojomionfpkfelancnnkf"),
+      base::BindOnce(&DataCallbackMock::Callback),
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests that UpdateClient::Update returns Error::INVALID_ARGUMENT when
+// the |ids| parameter is empty.
+TEST_F(UpdateClientTest, EmptyIdList) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      return {};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      DCHECK_EQ(Error::INVALID_ARGUMENT, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      NOTREACHED();
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  const std::vector<std::string> empty_id_list;
+  update_client->Update(
+      empty_id_list, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+  RunThreads();
+}
+
+TEST_F(UpdateClientTest, SendUninstallPing) {
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return nullptr;
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      NOTREACHED();
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return nullptr;
+    }
+
+   private:
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+    ~MockCrxDownloader() override {}
+
+    void DoStartDownload(const GURL& url) override {}
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(1u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("1.2.3.4"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("0"), ping_data[0].next_version);
+      EXPECT_EQ(10, ping_data[0].extra_code1);
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  update_client->SendUninstallPing(
+      "jebgalgnebhfojomionfpkfelancnnkf", base::Version("1.2.3.4"), 10,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+}
+
+TEST_F(UpdateClientTest, RetryAfter) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.9");
+      crx.installer = base::MakeRefCounted<TestInstaller>();
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      static int num_call = 0;
+      ++num_call;
+
+      EXPECT_LE(num_call, 4);
+
+      if (num_call == 1) {
+        EXPECT_EQ(Error::NONE, error);
+      } else if (num_call == 2) {
+        // This request is throttled since the update engine received a
+        // positive |retry_after_sec| value in the update check response.
+        EXPECT_EQ(Error::RETRY_LATER, error);
+      } else if (num_call == 3) {
+        // This request is a foreground Install, which is never throttled.
+        // The update engine received a |retry_after_sec| value of 0, which
+        // resets the throttling.
+        EXPECT_EQ(Error::NONE, error);
+      } else if (num_call == 4) {
+        // This request succeeds since there is no throttling in effect.
+        EXPECT_EQ(Error::NONE, error);
+      }
+
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+
+      static int num_call = 0;
+      ++num_call;
+
+      EXPECT_LE(num_call, 3);
+
+      int retry_after_sec(0);
+      if (num_call == 1) {
+        // Throttle the next call.
+        retry_after_sec = 60 * 60;  // 1 hour.
+      }
+
+      EXPECT_EQ(1u, ids_to_check.size());
+      const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+      EXPECT_EQ(id, ids_to_check.front());
+      EXPECT_EQ(1u, components.count(id));
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "noupdate";
+
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, retry_after_sec));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+
+  InSequence seq;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
+  {
+    // The engine handles this Update call but responds with a valid
+    // |retry_after_sec|, which causes subsequent calls to fail.
+    base::RunLoop runloop;
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+                          false,
+                          base::BindOnce(&CompletionCallbackMock::Callback,
+                                         runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    // This call will result in a completion callback invoked with
+    // Error::ERROR_UPDATE_RETRY_LATER.
+    base::RunLoop runloop;
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+                          false,
+                          base::BindOnce(&CompletionCallbackMock::Callback,
+                                         runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    // The Install call is handled, and the throttling is reset due to
+    // the value of |retry_after_sec| in the completion callback.
+    base::RunLoop runloop;
+    update_client->Install(std::string("jebgalgnebhfojomionfpkfelancnnkf"),
+                           base::BindOnce(&DataCallbackMock::Callback),
+                           base::BindOnce(&CompletionCallbackMock::Callback,
+                                          runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    // This call succeeds.
+    base::RunLoop runloop;
+    update_client->Update(ids, base::BindOnce(&DataCallbackMock::Callback),
+                          false,
+                          base::BindOnce(&CompletionCallbackMock::Callback,
+                                         runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the update check for two CRXs scenario. The first component supports
+// the group policy to enable updates, and has its updates disabled. The second
+// component has an update. The server does not honor the "updatedisabled"
+// attribute and returns updates for both components. However, the update for
+// the first component is not applied and the client responds with a
+// (SERVICE_ERROR, UPDATE_DISABLED)
+TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx1;
+      crx1.name = "test_jebg";
+      crx1.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx1.version = base::Version("0.9");
+      crx1.installer = base::MakeRefCounted<TestInstaller>();
+      crx1.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      crx1.supports_group_policy_enable_component_updates = true;
+
+      CrxComponent crx2;
+      crx2.name = "test_ihfo";
+      crx2.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash));
+      crx2.version = base::Version("0.8");
+      crx2.installer = base::MakeRefCounted<TestInstaller>();
+      crx2.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+
+      return {crx1, crx2};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+                         hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+                                      7c9b12cb7cc067667bde87'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+        <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='ihfokbkgjpifnbbojhneepfflplebdkc_1.crx'
+                         hash_sha256='813c59747e139a608b3b5fc49633affc6db574373f
+                                      309f156ea6d27229c0b3f9'/>
+              </packages>
+            </manifest>
+          </updatecheck>
+        </app>
+      </response>
+      */
+
+      // UpdateClient reads the state of |enabled_component_updates| from the
+      // configurator instance, persists its value in the corresponding
+      // update context, and propagates it down to each of the update actions,
+      // and further down to the UpdateChecker instance.
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_FALSE(enabled_component_updates);
+      EXPECT_EQ(2u, ids_to_check.size());
+
+      ProtocolParser::Results results;
+      {
+        const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+        EXPECT_EQ(id, ids_to_check[0]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+        package.hash_sha256 =
+            "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      }
+
+      {
+        const std::string id = "ihfokbkgjpifnbbojhneepfflplebdkc";
+        EXPECT_EQ(id, ids_to_check[1]);
+        EXPECT_EQ(1u, components.count(id));
+
+        ProtocolParser::Result::Manifest::Package package;
+        package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx";
+        package.hash_sha256 =
+            "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9";
+
+        ProtocolParser::Result result;
+        result.extension_id = id;
+        result.status = "ok";
+        result.crx_urls.push_back(GURL("http://localhost/download/"));
+        result.manifest.version = "1.0";
+        result.manifest.browser_min_version = "11.0.1.0";
+        result.manifest.packages.push_back(package);
+        results.list.push_back(result);
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      FilePath path;
+      Result result;
+      if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 53638;
+        download_metrics.total_bytes = 53638;
+        download_metrics.download_time_ms = 2000;
+
+        EXPECT_TRUE(MakeTestFile(
+            TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+                                    base::Unretained(this)));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      const auto ping_data = MockPingManagerImpl::ping_data();
+      EXPECT_EQ(2u, ping_data.size());
+      EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+      EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+      EXPECT_EQ(4, static_cast<int>(ping_data[0].error_category));
+      EXPECT_EQ(2, ping_data[0].error_code);
+      EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_data[1].id);
+      EXPECT_EQ(base::Version("0.8"), ping_data[1].previous_version);
+      EXPECT_EQ(base::Version("1.0"), ping_data[1].next_version);
+      EXPECT_EQ(0, static_cast<int>(ping_data[1].error_category));
+      EXPECT_EQ(0, ping_data[1].error_code);
+    }
+  };
+
+  // Disables updates for the components declaring support for the group policy.
+  config()->SetEnabledComponentUpdates(false);
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(AtLeast(1));
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1);
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
+                                        "ihfokbkgjpifnbbojhneepfflplebdkc"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the scenario where the update check fails.
+TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+      crx.version = base::Version("0.9");
+      crx.installer = base::MakeRefCounted<TestInstaller>();
+      crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+      return {crx};
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::UPDATE_CHECK_ERROR, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(1u, ids_to_check.size());
+      const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+      EXPECT_EQ(id, ids_to_check.front());
+      EXPECT_EQ(1u, components.count(id));
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          base::BindOnce(std::move(update_check_callback), base::nullopt,
+                         ErrorCategory::kUpdateCheck, -1, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  InSequence seq;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1)
+      .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+        CrxUpdateItem item;
+        EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+        EXPECT_EQ(ComponentState::kUpdateError, item.state);
+        EXPECT_EQ(5, static_cast<int>(item.error_category));
+        EXPECT_EQ(-1, item.error_code);
+        EXPECT_EQ(0, item.extra_code1);
+      }));
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), false,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+// Tests the scenario where the server responds with different values for
+// application status.
+TEST_F(UpdateClientTest, OneCrxErrorUnknownApp) {
+  class DataCallbackMock {
+   public:
+    static std::vector<base::Optional<CrxComponent>> Callback(
+        const std::vector<std::string>& ids) {
+      std::vector<base::Optional<CrxComponent>> component;
+      {
+        CrxComponent crx;
+        crx.name = "test_jebg";
+        crx.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+        crx.version = base::Version("0.9");
+        crx.installer = base::MakeRefCounted<TestInstaller>();
+        crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+        component.push_back(crx);
+      }
+      {
+        CrxComponent crx;
+        crx.name = "test_abag";
+        crx.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash));
+        crx.version = base::Version("0.1");
+        crx.installer = base::MakeRefCounted<TestInstaller>();
+        crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+        component.push_back(crx);
+      }
+      {
+        CrxComponent crx;
+        crx.name = "test_ihfo";
+        crx.pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash));
+        crx.version = base::Version("0.2");
+        crx.installer = base::MakeRefCounted<TestInstaller>();
+        crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+        component.push_back(crx);
+      }
+      {
+        CrxComponent crx;
+        crx.name = "test_gjpm";
+        crx.pk_hash.assign(gjpm_hash, gjpm_hash + base::size(gjpm_hash));
+        crx.version = base::Version("0.3");
+        crx.installer = base::MakeRefCounted<TestInstaller>();
+        crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+        component.push_back(crx);
+      }
+      return component;
+    }
+  };
+
+  class CompletionCallbackMock {
+   public:
+    static void Callback(base::OnceClosure quit_closure, Error error) {
+      EXPECT_EQ(Error::NONE, error);
+      std::move(quit_closure).Run();
+    }
+  };
+
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(4u, ids_to_check.size());
+
+      const std::string update_response =
+          ")]}'"
+          R"({"response": {)"
+          R"( "protocol": "3.1",)"
+          R"( "app": [)"
+          R"({"appid": "jebgalgnebhfojomionfpkfelancnnkf",)"
+          R"( "status": "error-unknownApplication"},)"
+          R"({"appid": "abagagagagagagagagagagagagagagag",)"
+          R"( "status": "restricted"},)"
+          R"({"appid": "ihfokbkgjpifnbbojhneepfflplebdkc",)"
+          R"( "status": "error-invalidAppId"},)"
+          R"({"appid": "gjpmebpgbhcamgdgjcmnjfhggjpgcimm",)"
+          R"( "status": "error-foobarApp"})"
+          R"(]}})";
+
+      const auto parser = ProtocolHandlerFactoryJSON().CreateParser();
+      EXPECT_TRUE(parser->Parse(update_response));
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          base::BindOnce(std::move(update_check_callback), parser->results(),
+                         ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  MockObserver observer;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "jebgalgnebhfojomionfpkfelancnnkf"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_EQ(5, static_cast<int>(item.error_category));
+          EXPECT_EQ(-10006, item.error_code);  // UNKNOWN_APPPLICATION.
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "abagagagagagagagagagagagagagagag"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "abagagagagagagagagagagagagagagag"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_EQ(5, static_cast<int>(item.error_category));
+          EXPECT_EQ(-10007, item.error_code);  // RESTRICTED_APPLICATION.
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "ihfokbkgjpifnbbojhneepfflplebdkc"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_EQ(5, static_cast<int>(item.error_category));
+          EXPECT_EQ(-10008, item.error_code);  // INVALID_APPID.
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+  {
+    InSequence seq;
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                  "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"))
+        .Times(1);
+    EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+                                  "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"))
+        .Times(1)
+        .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+          CrxUpdateItem item;
+          EXPECT_TRUE(update_client->GetCrxUpdateState(id, &item));
+          EXPECT_EQ(ComponentState::kUpdateError, item.state);
+          EXPECT_EQ(5, static_cast<int>(item.error_category));
+          EXPECT_EQ(-10004, item.error_code);  // UPDATE_RESPONSE_NOT_FOUND.
+          EXPECT_EQ(0, item.extra_code1);
+        }));
+  }
+
+  update_client->AddObserver(&observer);
+
+  const std::vector<std::string> ids = {
+      "jebgalgnebhfojomionfpkfelancnnkf", "abagagagagagagagagagagagagagagag",
+      "ihfokbkgjpifnbbojhneepfflplebdkc", "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"};
+  update_client->Update(
+      ids, base::BindOnce(&DataCallbackMock::Callback), true,
+      base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+  RunThreads();
+
+  update_client->RemoveObserver(&observer);
+}
+
+#if defined(OS_WIN)  // ActionRun is only implemented on Windows.
+
+// Tests that a run action in invoked in the CRX install scenario.
+TEST_F(UpdateClientTest, ActionRun_Install) {
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='gjpmebpgbhcamgdgjcmnjfhggjpgcimm'>
+          <updatecheck status='ok'>
+            <urls>
+              <url codebase='http://localhost/download/'/>
+            </urls>
+            <manifest version='1.0' prodversionmin='11.0.1.0'>
+              <packages>
+                <package name='runaction_test_win.crx3'
+                         hash_sha256='89290a0d2ff21ca5b45e109c6cc859ab5fe294e19c102d54acd321429c372cea'/>
+              </packages>
+            </manifest>
+            <actions>"
+             <action run='ChromeRecovery.crx3'/>"
+            </actions>"
+          </updatecheck>
+        </app>
+      </response>
+      */
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_TRUE(enabled_component_updates);
+      EXPECT_EQ(1u, ids_to_check.size());
+
+      const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm";
+      EXPECT_EQ(id, ids_to_check[0]);
+      EXPECT_EQ(1u, components.count(id));
+
+      ProtocolParser::Result::Manifest::Package package;
+      package.name = "runaction_test_win.crx3";
+      package.hash_sha256 =
+          "89290a0d2ff21ca5b45e109c6cc859ab5fe294e19c102d54acd321429c372cea";
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "ok";
+      result.crx_urls.push_back(GURL("http://localhost/download/"));
+      result.manifest.version = "1.0";
+      result.manifest.browser_min_version = "11.0.1.0";
+      result.manifest.packages.push_back(package);
+      result.action_run = "ChromeRecovery.crx3";
+
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override {
+      DownloadMetrics download_metrics;
+      FilePath path;
+      Result result;
+      if (url.path() == "/download/runaction_test_win.crx3") {
+        download_metrics.url = url;
+        download_metrics.downloader = DownloadMetrics::kNone;
+        download_metrics.error = 0;
+        download_metrics.downloaded_bytes = 1843;
+        download_metrics.total_bytes = 1843;
+        download_metrics.download_time_ms = 1000;
+
+        EXPECT_TRUE(
+            MakeTestFile(TestFilePath("runaction_test_win.crx3"), &path));
+
+        result.error = 0;
+        result.response = path;
+      } else {
+        NOTREACHED();
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+                                    base::Unretained(this), true, result,
+                                    download_metrics));
+    }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      EXPECT_EQ(3u, events().size());
+
+      /*
+      "<event eventtype="14" eventresult="1" downloader="unknown" "
+      "url="http://localhost/download/runaction_test_win.crx3"
+      "downloaded=1843 "
+      "total=1843 download_time_ms="1000" previousversion="0.0" "
+      "nextversion="1.0"/>"
+      */
+      const auto& event0 = events()[0];
+      EXPECT_EQ(14, event0.FindKey("eventtype")->GetInt());
+      EXPECT_EQ(1, event0.FindKey("eventresult")->GetInt());
+      EXPECT_EQ("unknown", event0.FindKey("downloader")->GetString());
+      EXPECT_EQ("http://localhost/download/runaction_test_win.crx3",
+                event0.FindKey("url")->GetString());
+      EXPECT_EQ(1843, event0.FindKey("downloaded")->GetDouble());
+      EXPECT_EQ(1843, event0.FindKey("total")->GetDouble());
+      EXPECT_EQ(1000, event0.FindKey("download_time_ms")->GetDouble());
+      EXPECT_EQ("0.0", event0.FindKey("previousversion")->GetString());
+      EXPECT_EQ("1.0", event0.FindKey("nextversion")->GetString());
+
+      // "<event eventtype="42" eventresult="1" errorcode="1877345072"/>"
+      const auto& event1 = events()[1];
+      EXPECT_EQ(42, event1.FindKey("eventtype")->GetInt());
+      EXPECT_EQ(1, event1.FindKey("eventresult")->GetInt());
+      EXPECT_EQ(1877345072, event1.FindKey("errorcode")->GetInt());
+
+      // "<event eventtype=\"3\" eventresult=\"1\" previousversion=\"0.0\" "
+      // "nextversion=\"1.0\"/>",
+      const auto& event2 = events()[2];
+      EXPECT_EQ(3, event2.FindKey("eventtype")->GetInt());
+      EXPECT_EQ(1, event1.FindKey("eventresult")->GetInt());
+      EXPECT_EQ("0.0", event0.FindKey("previousversion")->GetString());
+      EXPECT_EQ("1.0", event0.FindKey("nextversion")->GetString());
+    }
+  };
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  // The action is a program which returns 1877345072 as a hardcoded value.
+  update_client->Install(
+      std::string("gjpmebpgbhcamgdgjcmnjfhggjpgcimm"),
+      base::BindOnce([](const std::vector<std::string>& ids) {
+        CrxComponent crx;
+        crx.name = "test_niea";
+        crx.pk_hash.assign(gjpm_hash, gjpm_hash + base::size(gjpm_hash));
+        crx.version = base::Version("0.0");
+        crx.installer = base::MakeRefCounted<VersionedTestInstaller>();
+        crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+        return std::vector<base::Optional<CrxComponent>>{crx};
+      }),
+      base::BindOnce(
+          [](base::OnceClosure quit_closure, Error error) {
+            EXPECT_EQ(Error::NONE, error);
+            std::move(quit_closure).Run();
+          },
+          quit_closure()));
+
+  RunThreads();
+}
+
+// Tests that a run action is invoked in an update scenario when there was
+// no update.
+TEST_F(UpdateClientTest, ActionRun_NoUpdate) {
+  class MockUpdateChecker : public UpdateChecker {
+   public:
+    static std::unique_ptr<UpdateChecker> Create(
+        scoped_refptr<Configurator> config,
+        PersistedData* metadata) {
+      return std::make_unique<MockUpdateChecker>();
+    }
+
+    void CheckForUpdates(
+        const std::string& session_id,
+        const std::vector<std::string>& ids_to_check,
+        const IdToComponentPtrMap& components,
+        const base::flat_map<std::string, std::string>& additional_attributes,
+        bool enabled_component_updates,
+        UpdateCheckCallback update_check_callback) override {
+      /*
+      Mock the following response:
+
+      <?xml version='1.0' encoding='UTF-8'?>
+      <response protocol='3.1'>
+        <app appid='gjpmebpgbhcamgdgjcmnjfhggjpgcimm'>
+          <updatecheck status='noupdate'>
+            <actions>"
+             <action run=ChromeRecovery.crx3'/>"
+            </actions>"
+          </updatecheck>
+        </app>
+      </response>
+      */
+      EXPECT_FALSE(session_id.empty());
+      EXPECT_EQ(1u, ids_to_check.size());
+      const std::string id = "gjpmebpgbhcamgdgjcmnjfhggjpgcimm";
+      EXPECT_EQ(id, ids_to_check[0]);
+      EXPECT_EQ(1u, components.count(id));
+
+      ProtocolParser::Result result;
+      result.extension_id = id;
+      result.status = "noupdate";
+      result.action_run = "ChromeRecovery.crx3";
+
+      ProtocolParser::Results results;
+      results.list.push_back(result);
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+                                    ErrorCategory::kNone, 0, 0));
+    }
+  };
+
+  class MockCrxDownloader : public CrxDownloader {
+   public:
+    static std::unique_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        scoped_refptr<NetworkFetcherFactory> network_fetcher_factory) {
+      return std::make_unique<MockCrxDownloader>();
+    }
+
+    MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+   private:
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class MockPingManager : public MockPingManagerImpl {
+   public:
+    explicit MockPingManager(scoped_refptr<Configurator> config)
+        : MockPingManagerImpl(config) {}
+
+   protected:
+    ~MockPingManager() override {
+      EXPECT_EQ(1u, events().size());
+
+      // "<event eventtype="42" eventresult="1" errorcode="1877345072"/>"
+      const auto& event = events()[0];
+      EXPECT_EQ(42, event.FindKey("eventtype")->GetInt());
+      EXPECT_EQ(1, event.FindKey("eventresult")->GetInt());
+      EXPECT_EQ(1877345072, event.FindKey("errorcode")->GetInt());
+    }
+  };
+
+  // Unpack the CRX to mock an existing install to be updated. The payload to
+  // run is going to be picked up from this directory.
+  base::FilePath unpack_path;
+  {
+    base::RunLoop runloop;
+    base::OnceClosure quit_closure = runloop.QuitClosure();
+
+    auto config = base::MakeRefCounted<TestConfigurator>();
+    auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>(
+        std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash)),
+        TestFilePath("runaction_test_win.crx3"), nullptr,
+        config->GetUnzipperFactory()->Create(),
+        config->GetPatcherFactory()->Create(),
+        crx_file::VerifierFormat::CRX2_OR_CRX3);
+
+    component_unpacker->Unpack(base::BindOnce(
+        [](base::FilePath* unpack_path, base::OnceClosure quit_closure,
+           const ComponentUnpacker::Result& result) {
+          EXPECT_EQ(UnpackerError::kNone, result.error);
+          EXPECT_EQ(0, result.extended_error);
+          *unpack_path = result.unpack_path;
+          std::move(quit_closure).Run();
+        },
+        &unpack_path, runloop.QuitClosure()));
+
+    runloop.Run();
+  }
+
+  EXPECT_FALSE(unpack_path.empty());
+  EXPECT_TRUE(base::DirectoryExists(unpack_path));
+  int64_t file_size = 0;
+  EXPECT_TRUE(base::GetFileSize(unpack_path.AppendASCII("ChromeRecovery.crx3"),
+                                &file_size));
+  EXPECT_EQ(44582, file_size);
+
+  base::ScopedTempDir unpack_path_owner;
+  EXPECT_TRUE(unpack_path_owner.Set(unpack_path));
+
+  scoped_refptr<UpdateClient> update_client =
+      base::MakeRefCounted<UpdateClientImpl>(
+          config(), base::MakeRefCounted<MockPingManager>(config()),
+          &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+  // The action is a program which returns 1877345072 as a hardcoded value.
+  const std::vector<std::string> ids = {"gjpmebpgbhcamgdgjcmnjfhggjpgcimm"};
+  update_client->Update(
+      ids,
+      base::BindOnce(
+          [](const base::FilePath& unpack_path,
+             const std::vector<std::string>& ids) {
+            CrxComponent crx;
+            crx.name = "test_niea";
+            crx.pk_hash.assign(gjpm_hash, gjpm_hash + base::size(gjpm_hash));
+            crx.version = base::Version("1.0");
+            crx.installer =
+                base::MakeRefCounted<ReadOnlyTestInstaller>(unpack_path);
+            crx.crx_format_requirement = crx_file::VerifierFormat::CRX2_OR_CRX3;
+            return std::vector<base::Optional<CrxComponent>>{crx};
+          },
+          unpack_path),
+      false,
+      base::BindOnce(
+          [](base::OnceClosure quit_closure, Error error) {
+            EXPECT_EQ(Error::NONE, error);
+            std::move(quit_closure).Run();
+          },
+          quit_closure()));
+
+  RunThreads();
+}
+
+#endif  // OS_WIN
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/update_engine.cc b/src/updater_components/update_client/update_engine.cc
new file mode 100644
index 0000000..a6d6920
--- /dev/null
+++ b/src/updater_components/update_client/update_engine.cc
@@ -0,0 +1,438 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/update_engine.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/stl_util.h"
+#include "base/strings/strcat.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/prefs/pref_service.h"
+#include "components/update_client/component.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/persisted_data.h"
+#include "components/update_client/protocol_parser.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client_errors.h"
+#include "components/update_client/utils.h"
+
+namespace update_client {
+
+UpdateContext::UpdateContext(
+    scoped_refptr<Configurator> config,
+    bool is_foreground,
+    const std::vector<std::string>& ids,
+    UpdateClient::CrxDataCallback crx_data_callback,
+    const UpdateEngine::NotifyObserversCallback& notify_observers_callback,
+    UpdateEngine::Callback callback,
+    CrxDownloader::Factory crx_downloader_factory)
+    : config(config),
+      is_foreground(is_foreground),
+      enabled_component_updates(config->EnabledComponentUpdates()),
+      ids(ids),
+      crx_data_callback(std::move(crx_data_callback)),
+      notify_observers_callback(notify_observers_callback),
+      callback(std::move(callback)),
+      crx_downloader_factory(crx_downloader_factory),
+      session_id(base::StrCat({"{", base::GenerateGUID(), "}"})) {
+  for (const auto& id : ids) {
+    components.insert(
+        std::make_pair(id, std::make_unique<Component>(*this, id)));
+  }
+}
+
+UpdateContext::~UpdateContext() {}
+
+UpdateEngine::UpdateEngine(
+    scoped_refptr<Configurator> config,
+    UpdateChecker::Factory update_checker_factory,
+    CrxDownloader::Factory crx_downloader_factory,
+    scoped_refptr<PingManager> ping_manager,
+    const NotifyObserversCallback& notify_observers_callback)
+    : config_(config),
+      update_checker_factory_(update_checker_factory),
+      crx_downloader_factory_(crx_downloader_factory),
+      ping_manager_(ping_manager),
+      metadata_(
+          std::make_unique<PersistedData>(config->GetPrefService(),
+                                          config->GetActivityDataService())),
+      notify_observers_callback_(notify_observers_callback) {}
+
+UpdateEngine::~UpdateEngine() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void UpdateEngine::Update(bool is_foreground,
+                          const std::vector<std::string>& ids,
+                          UpdateClient::CrxDataCallback crx_data_callback,
+                          Callback callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (ids.empty()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback), Error::INVALID_ARGUMENT));
+    return;
+  }
+
+  if (IsThrottled(is_foreground)) {
+    // TODO(xiaochu): remove this log after https://crbug.com/851151 is fixed.
+    VLOG(1) << "Background update is throttled for following components:";
+    for (const auto& id : ids) {
+      VLOG(1) << "id:" << id;
+    }
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), Error::RETRY_LATER));
+    return;
+  }
+
+  const auto update_context = base::MakeRefCounted<UpdateContext>(
+      config_, is_foreground, ids, std::move(crx_data_callback),
+      notify_observers_callback_, std::move(callback), crx_downloader_factory_);
+  DCHECK(!update_context->session_id.empty());
+
+  const auto result = update_contexts_.insert(
+      std::make_pair(update_context->session_id, update_context));
+  DCHECK(result.second);
+
+  // Calls out to get the corresponding CrxComponent data for the CRXs in this
+  // update context.
+  const auto crx_components =
+      std::move(update_context->crx_data_callback).Run(update_context->ids);
+  DCHECK_EQ(update_context->ids.size(), crx_components.size());
+
+  for (size_t i = 0; i != update_context->ids.size(); ++i) {
+    const auto& id = update_context->ids[i];
+
+    DCHECK(update_context->components[id]->state() == ComponentState::kNew);
+
+    const auto crx_component = crx_components[i];
+    if (crx_component) {
+      // This component can be checked for updates.
+      DCHECK_EQ(id, GetCrxComponentID(*crx_component));
+      auto& component = update_context->components[id];
+      component->set_crx_component(*crx_component);
+      component->set_previous_version(component->crx_component()->version);
+      component->set_previous_fp(component->crx_component()->fingerprint);
+      update_context->components_to_check_for_updates.push_back(id);
+    } else {
+      // |CrxDataCallback| did not return a CrxComponent instance for this
+      // component, which most likely, has been uninstalled. This component
+      // is going to be transitioned to an error state when the its |Handle|
+      // method is called later on by the engine.
+      update_context->component_queue.push(id);
+    }
+  }
+
+  if (update_context->components_to_check_for_updates.empty()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
+                                  base::Unretained(this), update_context));
+    return;
+  }
+
+  for (const auto& id : update_context->components_to_check_for_updates)
+    update_context->components[id]->Handle(
+        base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesStart,
+                       base::Unretained(this), update_context, id));
+}
+
+void UpdateEngine::ComponentCheckingForUpdatesStart(
+    scoped_refptr<UpdateContext> update_context,
+    const std::string& id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  DCHECK_EQ(1u, update_context->components.count(id));
+  DCHECK(update_context->components.at(id));
+
+  // Handle |kChecking| state.
+  auto& component = *update_context->components.at(id);
+  component.Handle(
+      base::BindOnce(&UpdateEngine::ComponentCheckingForUpdatesComplete,
+                     base::Unretained(this), update_context));
+
+  ++update_context->num_components_ready_to_check;
+  if (update_context->num_components_ready_to_check <
+      update_context->components_to_check_for_updates.size()) {
+    return;
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&UpdateEngine::DoUpdateCheck,
+                                base::Unretained(this), update_context));
+}
+
+void UpdateEngine::DoUpdateCheck(scoped_refptr<UpdateContext> update_context) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  update_context->update_checker =
+      update_checker_factory_(config_, metadata_.get());
+
+  update_context->update_checker->CheckForUpdates(
+      update_context->session_id,
+      update_context->components_to_check_for_updates,
+      update_context->components, config_->ExtraRequestParams(),
+      update_context->enabled_component_updates,
+      base::BindOnce(&UpdateEngine::UpdateCheckResultsAvailable,
+                     base::Unretained(this), update_context));
+}
+
+void UpdateEngine::UpdateCheckResultsAvailable(
+    scoped_refptr<UpdateContext> update_context,
+    const base::Optional<ProtocolParser::Results>& results,
+    ErrorCategory error_category,
+    int error,
+    int retry_after_sec) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  update_context->retry_after_sec = retry_after_sec;
+
+  // Only positive values for throttle_sec are effective. 0 means that no
+  // throttling occurs and it resets |throttle_updates_until_|.
+  // Negative values are not trusted and are ignored.
+  constexpr int kMaxRetryAfterSec = 24 * 60 * 60;  // 24 hours.
+  const int throttle_sec =
+      std::min(update_context->retry_after_sec, kMaxRetryAfterSec);
+  if (throttle_sec >= 0) {
+    throttle_updates_until_ =
+        throttle_sec ? base::TimeTicks::Now() +
+                           base::TimeDelta::FromSeconds(throttle_sec)
+                     : base::TimeTicks();
+  }
+
+  update_context->update_check_error = error;
+
+  if (error) {
+    DCHECK(!results);
+    for (const auto& id : update_context->components_to_check_for_updates) {
+      DCHECK_EQ(1u, update_context->components.count(id));
+      auto& component = update_context->components.at(id);
+      component->SetUpdateCheckResult(base::nullopt,
+                                      ErrorCategory::kUpdateCheck, error);
+    }
+    return;
+  }
+
+  DCHECK(results);
+  DCHECK_EQ(0, error);
+
+  std::map<std::string, ProtocolParser::Result> id_to_result;
+  for (const auto& result : results->list)
+    id_to_result[result.extension_id] = result;
+
+  for (const auto& id : update_context->components_to_check_for_updates) {
+    DCHECK_EQ(1u, update_context->components.count(id));
+    auto& component = update_context->components.at(id);
+    const auto& it = id_to_result.find(id);
+    if (it != id_to_result.end()) {
+      const auto result = it->second;
+      const auto error = [](const std::string& status) {
+        // First, handle app status literals which can be folded down as an
+        // updatecheck status
+        if (status == "error-unknownApplication")
+          return std::make_pair(ErrorCategory::kUpdateCheck,
+                                ProtocolError::UNKNOWN_APPLICATION);
+        if (status == "restricted")
+          return std::make_pair(ErrorCategory::kUpdateCheck,
+                                ProtocolError::RESTRICTED_APPLICATION);
+        if (status == "error-invalidAppId")
+          return std::make_pair(ErrorCategory::kUpdateCheck,
+                                ProtocolError::INVALID_APPID);
+        // If the parser has return a valid result and the status is not one of
+        // the literals above, then this must be a success an not a parse error.
+        return std::make_pair(ErrorCategory::kNone, ProtocolError::NONE);
+      }(result.status);
+      component->SetUpdateCheckResult(result, error.first,
+                                      static_cast<int>(error.second));
+    } else {
+      component->SetUpdateCheckResult(
+          base::nullopt, ErrorCategory::kUpdateCheck,
+          static_cast<int>(ProtocolError::UPDATE_RESPONSE_NOT_FOUND));
+    }
+  }
+}
+
+void UpdateEngine::ComponentCheckingForUpdatesComplete(
+    scoped_refptr<UpdateContext> update_context) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  ++update_context->num_components_checked;
+  if (update_context->num_components_checked <
+      update_context->components_to_check_for_updates.size()) {
+    return;
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&UpdateEngine::UpdateCheckComplete,
+                                base::Unretained(this), update_context));
+}
+
+void UpdateEngine::UpdateCheckComplete(
+    scoped_refptr<UpdateContext> update_context) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  for (const auto& id : update_context->components_to_check_for_updates)
+    update_context->component_queue.push(id);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
+                                base::Unretained(this), update_context));
+}
+
+void UpdateEngine::HandleComponent(
+    scoped_refptr<UpdateContext> update_context) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  auto& queue = update_context->component_queue;
+
+  if (queue.empty()) {
+    const Error error = update_context->update_check_error
+                            ? Error::UPDATE_CHECK_ERROR
+                            : Error::NONE;
+
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&UpdateEngine::UpdateComplete, base::Unretained(this),
+                       update_context, error));
+    return;
+  }
+
+  const auto& id = queue.front();
+  DCHECK_EQ(1u, update_context->components.count(id));
+  const auto& component = update_context->components.at(id);
+  DCHECK(component);
+
+  auto& next_update_delay = update_context->next_update_delay;
+  if (!next_update_delay.is_zero() && component->IsUpdateAvailable()) {
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&UpdateEngine::HandleComponent, base::Unretained(this),
+                       update_context),
+        next_update_delay);
+    next_update_delay = base::TimeDelta();
+
+    notify_observers_callback_.Run(
+        UpdateClient::Observer::Events::COMPONENT_WAIT, id);
+    return;
+  }
+
+  component->Handle(base::BindOnce(&UpdateEngine::HandleComponentComplete,
+                                   base::Unretained(this), update_context));
+}
+
+void UpdateEngine::HandleComponentComplete(
+    scoped_refptr<UpdateContext> update_context) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  auto& queue = update_context->component_queue;
+  DCHECK(!queue.empty());
+
+  const auto& id = queue.front();
+  DCHECK_EQ(1u, update_context->components.count(id));
+  const auto& component = update_context->components.at(id);
+  DCHECK(component);
+
+  if (component->IsHandled()) {
+    update_context->next_update_delay = component->GetUpdateDuration();
+
+    if (!component->events().empty()) {
+      ping_manager_->SendPing(*component,
+                              base::BindOnce([](int, const std::string&) {}));
+    }
+
+    queue.pop();
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
+                                base::Unretained(this), update_context));
+}
+
+void UpdateEngine::UpdateComplete(scoped_refptr<UpdateContext> update_context,
+                                  Error error) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(update_context);
+
+  const auto num_erased = update_contexts_.erase(update_context->session_id);
+  DCHECK_EQ(1u, num_erased);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(update_context->callback), error));
+}
+
+bool UpdateEngine::GetUpdateState(const std::string& id,
+                                  CrxUpdateItem* update_item) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  for (const auto& context : update_contexts_) {
+    const auto& components = context.second->components;
+    const auto it = components.find(id);
+    if (it != components.end()) {
+      *update_item = it->second->GetCrxUpdateItem();
+      return true;
+    }
+  }
+  return false;
+}
+
+bool UpdateEngine::IsThrottled(bool is_foreground) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (is_foreground || throttle_updates_until_.is_null())
+    return false;
+
+  const auto now(base::TimeTicks::Now());
+
+  // Throttle the calls in the interval (t - 1 day, t) to limit the effect of
+  // unset clocks or clock drift.
+  return throttle_updates_until_ - base::TimeDelta::FromDays(1) < now &&
+         now < throttle_updates_until_;
+}
+
+void UpdateEngine::SendUninstallPing(const std::string& id,
+                                     const base::Version& version,
+                                     int reason,
+                                     Callback callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  const auto update_context = base::MakeRefCounted<UpdateContext>(
+      config_, false, std::vector<std::string>{id},
+      UpdateClient::CrxDataCallback(), UpdateEngine::NotifyObserversCallback(),
+      std::move(callback), nullptr);
+  DCHECK(!update_context->session_id.empty());
+
+  const auto result = update_contexts_.insert(
+      std::make_pair(update_context->session_id, update_context));
+  DCHECK(result.second);
+
+  DCHECK(update_context);
+  DCHECK_EQ(1u, update_context->ids.size());
+  DCHECK_EQ(1u, update_context->components.count(id));
+  const auto& component = update_context->components.at(id);
+
+  component->Uninstall(version, reason);
+
+  update_context->component_queue.push(id);
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&UpdateEngine::HandleComponent,
+                                base::Unretained(this), update_context));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/update_engine.h b/src/updater_components/update_client/update_engine.h
new file mode 100644
index 0000000..9601873
--- /dev/null
+++ b/src/updater_components/update_client/update_engine.h
@@ -0,0 +1,201 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/queue.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "components/update_client/component.h"
+#include "components/update_client/crx_downloader.h"
+#include "components/update_client/crx_update_item.h"
+#include "components/update_client/ping_manager.h"
+#include "components/update_client/update_checker.h"
+#include "components/update_client/update_client.h"
+
+namespace base {
+class TimeTicks;
+}  // namespace base
+
+namespace update_client {
+
+class Configurator;
+struct UpdateContext;
+
+// Handles updates for a group of components. Updates for different groups
+// are run concurrently but within the same group of components, updates are
+// applied one at a time.
+class UpdateEngine : public base::RefCounted<UpdateEngine> {
+ public:
+  using Callback = base::OnceCallback<void(Error error)>;
+  using NotifyObserversCallback =
+      base::Callback<void(UpdateClient::Observer::Events event,
+                          const std::string& id)>;
+  using CrxDataCallback = UpdateClient::CrxDataCallback;
+
+  UpdateEngine(scoped_refptr<Configurator> config,
+               UpdateChecker::Factory update_checker_factory,
+               CrxDownloader::Factory crx_downloader_factory,
+               scoped_refptr<PingManager> ping_manager,
+               const NotifyObserversCallback& notify_observers_callback);
+
+  // Returns true and the state of the component identified by |id|, if the
+  // component is found in any update context. Returns false if the component
+  // is not found.
+  bool GetUpdateState(const std::string& id, CrxUpdateItem* update_state);
+
+  void Update(bool is_foreground,
+              const std::vector<std::string>& ids,
+              UpdateClient::CrxDataCallback crx_data_callback,
+              Callback update_callback);
+
+  void SendUninstallPing(const std::string& id,
+                         const base::Version& version,
+                         int reason,
+                         Callback update_callback);
+
+ private:
+  friend class base::RefCounted<UpdateEngine>;
+  ~UpdateEngine();
+
+  using UpdateContexts = std::map<std::string, scoped_refptr<UpdateContext>>;
+
+  void UpdateComplete(scoped_refptr<UpdateContext> update_context, Error error);
+
+  void ComponentCheckingForUpdatesStart(
+      scoped_refptr<UpdateContext> update_context,
+      const std::string& id);
+  void ComponentCheckingForUpdatesComplete(
+      scoped_refptr<UpdateContext> update_context);
+  void UpdateCheckComplete(scoped_refptr<UpdateContext> update_context);
+
+  void DoUpdateCheck(scoped_refptr<UpdateContext> update_context);
+  void UpdateCheckResultsAvailable(
+      scoped_refptr<UpdateContext> update_context,
+      const base::Optional<ProtocolParser::Results>& results,
+      ErrorCategory error_category,
+      int error,
+      int retry_after_sec);
+
+  void HandleComponent(scoped_refptr<UpdateContext> update_context);
+  void HandleComponentComplete(scoped_refptr<UpdateContext> update_context);
+
+  // Returns true if the update engine rejects this update call because it
+  // occurs too soon.
+  bool IsThrottled(bool is_foreground) const;
+
+  base::ThreadChecker thread_checker_;
+  scoped_refptr<Configurator> config_;
+  UpdateChecker::Factory update_checker_factory_;
+  CrxDownloader::Factory crx_downloader_factory_;
+  scoped_refptr<PingManager> ping_manager_;
+  std::unique_ptr<PersistedData> metadata_;
+
+  // Called when CRX state changes occur.
+  const NotifyObserversCallback notify_observers_callback_;
+
+  // Contains the contexts associated with each update in progress.
+  UpdateContexts update_contexts_;
+
+  // Implements a rate limiting mechanism for background update checks. Has the
+  // effect of rejecting the update call if the update call occurs before
+  // a certain time, which is negotiated with the server as part of the
+  // update protocol. See the comments for X-Retry-After header.
+  base::TimeTicks throttle_updates_until_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateEngine);
+};
+
+// Describes a group of components which are installed or updated together.
+struct UpdateContext : public base::RefCounted<UpdateContext> {
+  UpdateContext(
+      scoped_refptr<Configurator> config,
+      bool is_foreground,
+      const std::vector<std::string>& ids,
+      UpdateClient::CrxDataCallback crx_data_callback,
+      const UpdateEngine::NotifyObserversCallback& notify_observers_callback,
+      UpdateEngine::Callback callback,
+      CrxDownloader::Factory crx_downloader_factory);
+
+  scoped_refptr<Configurator> config;
+
+  // True if the component is updated as a result of user interaction.
+  bool is_foreground = false;
+
+  // True if the component updates are enabled in this context.
+  const bool enabled_component_updates;
+
+  // Contains the ids of all CRXs in this context in the order specified
+  // by the caller of |UpdateClient::Update| or |UpdateClient:Install|.
+  const std::vector<std::string> ids;
+
+  // Contains the map of ids to components for all the CRX in this context.
+  IdToComponentPtrMap components;
+
+  // Called before an update check, when update metadata is needed.
+  UpdateEngine::CrxDataCallback crx_data_callback;
+
+  // Called when there is a state change for any update in this context.
+  const UpdateEngine::NotifyObserversCallback notify_observers_callback;
+
+  // Called when the all updates associated with this context have completed.
+  UpdateEngine::Callback callback;
+
+  // Creates instances of CrxDownloader;
+  CrxDownloader::Factory crx_downloader_factory;
+
+  std::unique_ptr<UpdateChecker> update_checker;
+
+  // The time in seconds to wait until doing further update checks.
+  int retry_after_sec = 0;
+
+  // Contains the ids of the components to check for updates. It is possible
+  // for a component to be uninstalled after it has been added in this context
+  // but before an update check is made. When this happens, the component won't
+  // have a CrxComponent instance, therefore, it can't be included in an
+  // update check.
+  std::vector<std::string> components_to_check_for_updates;
+
+  // The error reported by the update checker.
+  int update_check_error = 0;
+
+  size_t num_components_ready_to_check = 0;
+  size_t num_components_checked = 0;
+
+  // Contains the ids of the components that the state machine must handle.
+  base::queue<std::string> component_queue;
+
+  // The time to wait before handling the update for a component.
+  // The wait time is proportional with the cost incurred by updating
+  // the component. The more time it takes to download and apply the
+  // update for the current component, the longer the wait until the engine
+  // is handling the next component in the queue.
+  base::TimeDelta next_update_delay;
+
+  // The unique session id of this context. The session id is serialized in
+  // every protocol request. It is also used as a key in various data stuctures
+  // to uniquely identify an update context.
+  const std::string session_id;
+
+ private:
+  friend class base::RefCounted<UpdateContext>;
+  ~UpdateContext();
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateContext);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATE_ENGINE_H_
diff --git a/src/updater_components/update_client/update_query_params.cc b/src/updater_components/update_client/update_query_params.cc
new file mode 100644
index 0000000..d9e1612
--- /dev/null
+++ b/src/updater_components/update_client/update_query_params.cc
@@ -0,0 +1,149 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/update_query_params.h"
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/system/sys_info.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
+#include "components/update_client/update_query_params_delegate.h"
+#include "components/version_info/version_info.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace update_client {
+
+namespace {
+
+const char kUnknown[] = "unknown";
+
+// The request extra information is the OS and architecture, this helps
+// the server select the right package to be delivered.
+const char kOs[] =
+#if defined(OS_MACOSX)
+    "mac";
+#elif defined(OS_WIN)
+    "win";
+#elif defined(OS_ANDROID)
+    "android";
+#elif defined(OS_CHROMEOS)
+    "cros";
+#elif defined(OS_LINUX)
+    "linux";
+#elif defined(OS_FUCHSIA)
+    "fuchsia";
+#elif defined(OS_OPENBSD)
+    "openbsd";
+#else
+#error "unknown os"
+#endif
+
+const char kArch[] =
+#if defined(__amd64__) || defined(_WIN64)
+    "x64";
+#elif defined(__i386__) || defined(_WIN32)
+    "x86";
+#elif defined(__arm__)
+    "arm";
+#elif defined(__aarch64__)
+    "arm64";
+#elif defined(__mips__) && (__mips == 64)
+    "mips64el";
+#elif defined(__mips__)
+    "mipsel";
+#elif defined(__powerpc64__)
+    "ppc64";
+#else
+#error "unknown arch"
+#endif
+
+const char kChrome[] = "chrome";
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+const char kCrx[] = "chromecrx";
+#else
+const char kCrx[] = "chromiumcrx";
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
+UpdateQueryParamsDelegate* g_delegate = nullptr;
+
+}  // namespace
+
+// static
+std::string UpdateQueryParams::Get(ProdId prod) {
+  return base::StringPrintf(
+      "os=%s&arch=%s&os_arch=%s&nacl_arch=%s&prod=%s%s&acceptformat=crx2,crx3",
+      kOs, kArch, base::SysInfo().OperatingSystemArchitecture().c_str(),
+      GetNaclArch(), GetProdIdString(prod),
+      g_delegate ? g_delegate->GetExtraParams().c_str() : "");
+}
+
+// static
+const char* UpdateQueryParams::GetProdIdString(UpdateQueryParams::ProdId prod) {
+  switch (prod) {
+    case UpdateQueryParams::CHROME:
+      return kChrome;
+      break;
+    case UpdateQueryParams::CRX:
+      return kCrx;
+      break;
+  }
+  return kUnknown;
+}
+
+// static
+const char* UpdateQueryParams::GetOS() {
+  return kOs;
+}
+
+// static
+const char* UpdateQueryParams::GetArch() {
+  return kArch;
+}
+
+// static
+const char* UpdateQueryParams::GetNaclArch() {
+#if defined(ARCH_CPU_X86_FAMILY)
+#if defined(ARCH_CPU_X86_64)
+  return "x86-64";
+#elif defined(OS_WIN)
+  bool x86_64 = (base::win::OSInfo::GetInstance()->wow64_status() ==
+                 base::win::OSInfo::WOW64_ENABLED);
+  return x86_64 ? "x86-64" : "x86-32";
+#else
+  return "x86-32";
+#endif
+#elif defined(ARCH_CPU_ARMEL)
+  return "arm";
+#elif defined(ARCH_CPU_ARM64)
+  return "arm64";
+#elif defined(ARCH_CPU_MIPSEL)
+  return "mips32";
+#elif defined(ARCH_CPU_MIPS64EL)
+  return "mips64";
+#elif defined(ARCH_CPU_PPC64)
+  return "ppc64";
+#else
+// NOTE: when adding new values here, please remember to update the
+// comment in the .h file about possible return values from this function.
+#error "You need to add support for your architecture here"
+#endif
+}
+
+// static
+std::string UpdateQueryParams::GetProdVersion() {
+  return version_info::GetVersionNumber();
+}
+
+// static
+void UpdateQueryParams::SetDelegate(UpdateQueryParamsDelegate* delegate) {
+  DCHECK(!g_delegate || !delegate || (delegate == g_delegate));
+  g_delegate = delegate;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/update_query_params.h b/src/updater_components/update_client/update_query_params.h
new file mode 100644
index 0000000..e315e14
--- /dev/null
+++ b/src/updater_components/update_client/update_query_params.h
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace update_client {
+
+class UpdateQueryParamsDelegate;
+
+// Generates a string of URL query parameters to be used when getting
+// component and extension updates. These parameters generally remain
+// fixed for a particular build. Embedders can use the delegate to
+// define different implementations. This should be used only in the
+// browser process.
+class UpdateQueryParams {
+ public:
+  enum ProdId {
+    CHROME = 0,
+    CRX,
+  };
+
+  // Generates a string of URL query parameters for Omaha. Includes the
+  // following fields: "os", "arch", "nacl_arch", "prod", "prodchannel",
+  // "prodversion", and "lang"
+  static std::string Get(ProdId prod);
+
+  // Returns the value we use for the "prod=" parameter. Possible return values
+  // include "chrome", "chromecrx", "chromiumcrx", and "unknown".
+  static const char* GetProdIdString(ProdId prod);
+
+  // Returns the value we use for the "os=" parameter. Possible return values
+  // include: "mac", "win", "android", "cros", "linux", and "openbsd".
+  static const char* GetOS();
+
+  // Returns the value we use for the "arch=" parameter. Possible return values
+  // include: "x86", "x64", and "arm".
+  static const char* GetArch();
+
+  // Returns the value we use for the "nacl_arch" parameter. Note that this may
+  // be different from the "arch" parameter above (e.g. one may be 32-bit and
+  // the other 64-bit). Possible return values include: "x86-32", "x86-64",
+  // "arm", "mips32", and "ppc64".
+  static const char* GetNaclArch();
+
+  // Returns the current version of Chrome/Chromium.
+  static std::string GetProdVersion();
+
+  // Use this delegate.
+  static void SetDelegate(UpdateQueryParamsDelegate* delegate);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(UpdateQueryParams);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_H_
diff --git a/src/updater_components/update_client/update_query_params_delegate.cc b/src/updater_components/update_client/update_query_params_delegate.cc
new file mode 100644
index 0000000..e14665a
--- /dev/null
+++ b/src/updater_components/update_client/update_query_params_delegate.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/update_query_params_delegate.h"
+
+namespace update_client {
+
+UpdateQueryParamsDelegate::UpdateQueryParamsDelegate() {
+}
+
+UpdateQueryParamsDelegate::~UpdateQueryParamsDelegate() {
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/update_query_params_delegate.h b/src/updater_components/update_client/update_query_params_delegate.h
new file mode 100644
index 0000000..6230e2e
--- /dev/null
+++ b/src/updater_components/update_client/update_query_params_delegate.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_DELEGATE_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_DELEGATE_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace update_client {
+
+// Embedders can specify an UpdateQueryParamsDelegate to provide additional
+// custom parameters. If not specified (Set is never called), no additional
+// parameters are added.
+class UpdateQueryParamsDelegate {
+ public:
+  UpdateQueryParamsDelegate();
+  virtual ~UpdateQueryParamsDelegate();
+
+  // Returns additional parameters, if any. If there are any parameters, the
+  // string should begin with a & character.
+  virtual std::string GetExtraParams() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpdateQueryParamsDelegate);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATE_QUERY_PARAMS_DELEGATE_H_
diff --git a/src/updater_components/update_client/update_query_params_unittest.cc b/src/updater_components/update_client/update_query_params_unittest.cc
new file mode 100644
index 0000000..e7ee538
--- /dev/null
+++ b/src/updater_components/update_client/update_query_params_unittest.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/update_query_params.h"
+#include "base/strings/stringprintf.h"
+#include "base/system/sys_info.h"
+#include "components/update_client/update_query_params_delegate.h"
+#include "components/version_info/version_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPrintf;
+
+namespace update_client {
+
+namespace {
+
+bool Contains(const std::string& source, const std::string& target) {
+  return source.find(target) != std::string::npos;
+}
+
+class TestUpdateQueryParamsDelegate : public UpdateQueryParamsDelegate {
+  std::string GetExtraParams() override { return "&cat=dog"; }
+};
+
+}  // namespace
+
+void TestParams(UpdateQueryParams::ProdId prod_id, bool extra_params) {
+  std::string params = UpdateQueryParams::Get(prod_id);
+
+  // This doesn't so much test what the values are (since that would be an
+  // almost exact duplication of code with update_query_params.cc, and wouldn't
+  // really test anything) as it is a verification that all the params are
+  // present in the generated string.
+  EXPECT_TRUE(
+      Contains(params, StringPrintf("os=%s", UpdateQueryParams::GetOS())));
+  EXPECT_TRUE(
+      Contains(params, StringPrintf("arch=%s", UpdateQueryParams::GetArch())));
+  EXPECT_TRUE(Contains(
+      params,
+      StringPrintf("os_arch=%s",
+                   base::SysInfo().OperatingSystemArchitecture().c_str())));
+  EXPECT_TRUE(Contains(
+      params,
+      StringPrintf("prod=%s", UpdateQueryParams::GetProdIdString(prod_id))));
+  if (extra_params)
+    EXPECT_TRUE(Contains(params, "cat=dog"));
+}
+
+void TestProdVersion() {
+  EXPECT_EQ(version_info::GetVersionNumber(),
+            UpdateQueryParams::GetProdVersion());
+}
+
+TEST(UpdateQueryParamsTest, GetParams) {
+  TestProdVersion();
+
+  TestParams(UpdateQueryParams::CRX, false);
+  TestParams(UpdateQueryParams::CHROME, false);
+
+  TestUpdateQueryParamsDelegate delegate;
+  UpdateQueryParams::SetDelegate(&delegate);
+
+  TestParams(UpdateQueryParams::CRX, true);
+  TestParams(UpdateQueryParams::CHROME, true);
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/updater_state.cc b/src/updater_components/update_client/updater_state.cc
new file mode 100644
index 0000000..5d05c86
--- /dev/null
+++ b/src/updater_components/update_client/updater_state.cc
@@ -0,0 +1,102 @@
+
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/updater_state.h"
+
+#include <utility>
+
+#include "base/enterprise_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
+
+namespace update_client {
+
+// The value of this constant does not reflect its name (i.e. "domainjoined"
+// vs something like "isenterprisemanaged") because it is used with omaha.
+// After discussion with omaha team it was decided to leave the value as is to
+// keep continuity with previous chrome versions.
+const char UpdaterState::kIsEnterpriseManaged[] = "domainjoined";
+
+UpdaterState::UpdaterState(bool is_machine) : is_machine_(is_machine) {}
+
+UpdaterState::~UpdaterState() {}
+
+std::unique_ptr<UpdaterState::Attributes> UpdaterState::GetState(
+    bool is_machine) {
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+  UpdaterState updater_state(is_machine);
+  updater_state.ReadState();
+  return std::make_unique<Attributes>(updater_state.BuildAttributes());
+#else
+  return nullptr;
+#endif  // OS_WIN or Mac
+}
+
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+void UpdaterState::ReadState() {
+  is_enterprise_managed_ = base::IsMachineExternallyManaged();
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  updater_name_ = GetUpdaterName();
+  updater_version_ = GetUpdaterVersion(is_machine_);
+  last_autoupdate_started_ = GetUpdaterLastStartedAU(is_machine_);
+  last_checked_ = GetUpdaterLastChecked(is_machine_);
+  is_autoupdate_check_enabled_ = IsAutoupdateCheckEnabled();
+  update_policy_ = GetUpdatePolicy();
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+}
+#endif  // OS_WIN or Mac
+
+UpdaterState::Attributes UpdaterState::BuildAttributes() const {
+  Attributes attributes;
+
+#if defined(OS_WIN)
+  // Only Windows implements this attribute in a meaningful way.
+  attributes["ismachine"] = is_machine_ ? "1" : "0";
+#endif  // OS_WIN
+  attributes[kIsEnterpriseManaged] = is_enterprise_managed_ ? "1" : "0";
+
+  attributes["name"] = updater_name_;
+
+  if (updater_version_.IsValid())
+    attributes["version"] = updater_version_.GetString();
+
+  const base::Time now = base::Time::NowFromSystemTime();
+  if (!last_autoupdate_started_.is_null())
+    attributes["laststarted"] =
+        NormalizeTimeDelta(now - last_autoupdate_started_);
+  if (!last_checked_.is_null())
+    attributes["lastchecked"] = NormalizeTimeDelta(now - last_checked_);
+
+  attributes["autoupdatecheckenabled"] =
+      is_autoupdate_check_enabled_ ? "1" : "0";
+
+  DCHECK((update_policy_ >= 0 && update_policy_ <= 3) || update_policy_ == -1);
+  attributes["updatepolicy"] = base::NumberToString(update_policy_);
+
+  return attributes;
+}
+
+std::string UpdaterState::NormalizeTimeDelta(const base::TimeDelta& delta) {
+  const base::TimeDelta two_weeks = base::TimeDelta::FromDays(14);
+  const base::TimeDelta two_months = base::TimeDelta::FromDays(60);
+
+  std::string val;  // Contains the value to return in hours.
+  if (delta <= two_weeks) {
+    val = "0";
+  } else if (two_weeks < delta && delta <= two_months) {
+    val = "408";  // 2 weeks in hours.
+  } else {
+    val = "1344";  // 2*28 days in hours.
+  }
+
+  DCHECK(!val.empty());
+  return val;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/updater_state.h b/src/updater_components/update_client/updater_state.h
new file mode 100644
index 0000000..ec11d4a
--- /dev/null
+++ b/src/updater_components/update_client/updater_state.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UPDATER_STATE_H_
+#define COMPONENTS_UPDATE_CLIENT_UPDATER_STATE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/time/time.h"
+#include "base/version.h"
+
+namespace update_client {
+
+class UpdaterState {
+ public:
+  using Attributes = std::map<std::string, std::string>;
+
+  static const char kIsEnterpriseManaged[];
+
+  // Returns a map of items representing the state of an updater.
+  // If |is_machine| is true, this indicates that the updater state corresponds
+  // to the machine instance of the updater. Returns nullptr on
+  // the platforms and builds where this feature is not supported.
+  static std::unique_ptr<Attributes> GetState(bool is_machine);
+
+  ~UpdaterState();
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(UpdaterStateTest, Serialize);
+
+  explicit UpdaterState(bool is_machine);
+
+  // This function is best-effort. It updates the class members with
+  // the relevant values that could be retrieved.
+  void ReadState();
+
+  // Builds the map of state attributes by serializing this object state.
+  Attributes BuildAttributes() const;
+
+  static std::string GetUpdaterName();
+  static base::Version GetUpdaterVersion(bool is_machine);
+  static bool IsAutoupdateCheckEnabled();
+  static base::Time GetUpdaterLastStartedAU(bool is_machine);
+  static base::Time GetUpdaterLastChecked(bool is_machine);
+
+  static int GetUpdatePolicy();
+
+  static std::string NormalizeTimeDelta(const base::TimeDelta& delta);
+
+  // True if the Omaha updater is installed per-machine.
+  // The MacOS implementation ignores the value of this member but this may
+  // change in the future.
+  bool is_machine_;
+  std::string updater_name_;
+  base::Version updater_version_;
+  base::Time last_autoupdate_started_;
+  base::Time last_checked_;
+  bool is_enterprise_managed_ = false;
+  bool is_autoupdate_check_enabled_ = false;
+  int update_policy_ = 0;
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UPDATER_STATE_H_
diff --git a/src/updater_components/update_client/updater_state_mac.mm b/src/updater_components/update_client/updater_state_mac.mm
new file mode 100644
index 0000000..a7bceb6
--- /dev/null
+++ b/src/updater_components/update_client/updater_state_mac.mm
@@ -0,0 +1,119 @@
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#include "base/enterprise_util.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/version.h"
+#include "components/update_client/updater_state.h"
+
+namespace update_client {
+
+namespace {
+
+const base::FilePath::CharType kKeystonePlist[] = FILE_PATH_LITERAL(
+    "Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/"
+    "Contents/Info.plist");
+
+// Gets a value from the updater settings. Returns a retained object.
+// T should be a toll-free Foundation framework type. See Apple's
+// documentation for toll-free bridging.
+template<class T>
+base::scoped_nsobject<T> GetUpdaterSettingsValue(NSString* value_name) {
+  CFStringRef app_id = CFSTR("com.google.Keystone.Agent");
+  base::ScopedCFTypeRef<CFPropertyListRef> plist(
+      CFPreferencesCopyAppValue(base::mac::NSToCFCast(value_name), app_id));
+  return base::scoped_nsobject<T>(
+      base::mac::ObjCCastStrict<T>(static_cast<id>(plist.get())),
+      base::scoped_policy::RETAIN);
+}
+
+base::Time GetUpdaterSettingsTime(NSString* value_name) {
+  base::scoped_nsobject<NSDate> date =
+      GetUpdaterSettingsValue<NSDate>(value_name);
+  base::Time result =
+      base::Time::FromCFAbsoluteTime([date timeIntervalSinceReferenceDate]);
+
+  return result;
+}
+
+base::Version GetVersionFromPlist(const base::FilePath& info_plist) {
+  base::mac::ScopedNSAutoreleasePool scoped_pool;
+  NSData* data =
+      [NSData dataWithContentsOfFile:
+          base::mac::FilePathToNSString(info_plist)];
+  if ([data length] == 0) {
+    return base::Version();
+  }
+  NSDictionary* all_keys = base::mac::ObjCCastStrict<NSDictionary>(
+      [NSPropertyListSerialization propertyListWithData:data
+          options:NSPropertyListImmutable
+           format:nil
+            error:nil]);
+  if (all_keys == nil) {
+    return base::Version();
+  }
+  CFStringRef version =
+      base::mac::GetValueFromDictionary<CFStringRef>(
+          base::mac::NSToCFCast(all_keys),
+          kCFBundleVersionKey);
+  if (version == NULL) {
+    return base::Version();
+  }
+  return base::Version(base::SysCFStringRefToUTF8(version));
+}
+
+}  // namespace
+
+std::string UpdaterState::GetUpdaterName() {
+  return std::string("Keystone");
+}
+
+base::Version UpdaterState::GetUpdaterVersion(bool /*is_machine*/) {
+  // System Keystone trumps user one, so check this one first
+  base::FilePath local_library;
+  bool success = base::mac::GetLocalDirectory(NSLibraryDirectory,
+                                              &local_library);
+  DCHECK(success);
+  base::FilePath system_bundle_plist = local_library.Append(kKeystonePlist);
+  base::Version system_keystone = GetVersionFromPlist(system_bundle_plist);
+  if (system_keystone.IsValid()) {
+    return system_keystone;
+  }
+
+  base::FilePath user_bundle_plist =
+      base::mac::GetUserLibraryPath().Append(kKeystonePlist);
+  return GetVersionFromPlist(user_bundle_plist);
+}
+
+base::Time UpdaterState::GetUpdaterLastStartedAU(bool /*is_machine*/) {
+  return GetUpdaterSettingsTime(@"lastCheckStartDate");
+}
+
+base::Time UpdaterState::GetUpdaterLastChecked(bool /*is_machine*/) {
+  return GetUpdaterSettingsTime(@"lastServerCheckDate");
+}
+
+bool UpdaterState::IsAutoupdateCheckEnabled() {
+  // Auto-update check period override (in seconds).
+  // Applies only to older versions of Keystone.
+  base::scoped_nsobject<NSNumber> timeInterval =
+      GetUpdaterSettingsValue<NSNumber>(@"checkInterval");
+  if (!timeInterval.get()) return true;
+  int value = [timeInterval intValue];
+
+  return 0 < value && value < (24 * 60 * 60);
+}
+
+int UpdaterState::GetUpdatePolicy() {
+  return -1;  // Keystone does not support update policies.
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/updater_state_unittest.cc b/src/updater_components/update_client/updater_state_unittest.cc
new file mode 100644
index 0000000..e916adf
--- /dev/null
+++ b/src/updater_components/update_client/updater_state_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/updater_state.h"
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/version.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+class UpdaterStateTest : public testing::Test {
+ public:
+  UpdaterStateTest() {}
+  ~UpdaterStateTest() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpdaterStateTest);
+};
+
+TEST_F(UpdaterStateTest, Serialize) {
+  UpdaterState updater_state(false);
+
+  updater_state.updater_name_ = "the updater";
+  updater_state.updater_version_ = base::Version("1.0");
+  updater_state.last_autoupdate_started_ = base::Time::NowFromSystemTime();
+  updater_state.last_checked_ = base::Time::NowFromSystemTime();
+  updater_state.is_enterprise_managed_ = false;
+  updater_state.is_autoupdate_check_enabled_ = true;
+  updater_state.update_policy_ = 1;
+
+  auto attributes = updater_state.BuildAttributes();
+
+  // Sanity check all members.
+  EXPECT_STREQ("the updater", attributes.at("name").c_str());
+  EXPECT_STREQ("1.0", attributes.at("version").c_str());
+  EXPECT_STREQ("0", attributes.at("laststarted").c_str());
+  EXPECT_STREQ("0", attributes.at("lastchecked").c_str());
+  EXPECT_STREQ("0", attributes.at("domainjoined").c_str());
+  EXPECT_STREQ("1", attributes.at("autoupdatecheckenabled").c_str());
+  EXPECT_STREQ("1", attributes.at("updatepolicy").c_str());
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#if defined(OS_WIN)
+  // The value of "ismachine".
+  EXPECT_STREQ("0", UpdaterState::GetState(false)->at("ismachine").c_str());
+  EXPECT_STREQ("1", UpdaterState::GetState(true)->at("ismachine").c_str());
+
+  // The name of the Windows updater for Chrome.
+  EXPECT_STREQ("Omaha", UpdaterState::GetState(false)->at("name").c_str());
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  // MacOS does not serialize "ismachine".
+  EXPECT_EQ(0UL, UpdaterState::GetState(false)->count("ismachine"));
+  EXPECT_EQ(0UL, UpdaterState::GetState(true)->count("ismachine"));
+  EXPECT_STREQ("Keystone", UpdaterState::GetState(false)->at("name").c_str());
+#endif  // OS_WIN
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
+  // Tests some of the remaining values.
+  updater_state = UpdaterState(false);
+
+  // Don't serialize an invalid version if it could not be read.
+  updater_state.updater_version_ = base::Version();
+  attributes = updater_state.BuildAttributes();
+  EXPECT_EQ(0u, attributes.count("version"));
+
+  updater_state.updater_version_ = base::Version("0.0.0.0");
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("0.0.0.0", attributes.at("version").c_str());
+
+  updater_state.last_autoupdate_started_ =
+      base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(15);
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("408", attributes.at("laststarted").c_str());
+
+  updater_state.last_autoupdate_started_ =
+      base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(90);
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("1344", attributes.at("laststarted").c_str());
+
+  // Don't serialize the time if it could not be read.
+  updater_state.last_autoupdate_started_ = base::Time();
+  attributes = updater_state.BuildAttributes();
+  EXPECT_EQ(0u, attributes.count("laststarted"));
+
+  updater_state.last_checked_ =
+      base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(15);
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("408", attributes.at("lastchecked").c_str());
+
+  updater_state.last_checked_ =
+      base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(90);
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("1344", attributes.at("lastchecked").c_str());
+
+  // Don't serialize the time if it could not be read (the value is invalid).
+  updater_state.last_checked_ = base::Time();
+  attributes = updater_state.BuildAttributes();
+  EXPECT_EQ(0u, attributes.count("lastchecked"));
+
+  updater_state.is_enterprise_managed_ = true;
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("1", attributes.at("domainjoined").c_str());
+
+  updater_state.is_autoupdate_check_enabled_ = false;
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("0", attributes.at("autoupdatecheckenabled").c_str());
+
+  updater_state.update_policy_ = 0;
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("0", attributes.at("updatepolicy").c_str());
+
+  updater_state.update_policy_ = -1;
+  attributes = updater_state.BuildAttributes();
+  EXPECT_STREQ("-1", attributes.at("updatepolicy").c_str());
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/updater_state_win.cc b/src/updater_components/update_client/updater_state_win.cc
new file mode 100644
index 0000000..89c2c18
--- /dev/null
+++ b/src/updater_components/update_client/updater_state_win.cc
@@ -0,0 +1,136 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/updater_state.h"
+
+#include <windows.h>
+
+#include <string>
+#include <utility>
+
+#include "base/enterprise_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+#include "base/win/win_util.h"
+
+// TODO(sorin): implement this in terms of
+// chrome/installer/util/google_update_settings (crbug.com/615187).
+
+namespace update_client {
+
+namespace {
+
+// Google Update group policy settings.
+const wchar_t kGoogleUpdatePoliciesKey[] =
+    L"SOFTWARE\\Policies\\Google\\Update";
+const wchar_t kCheckPeriodOverrideMinutes[] = L"AutoUpdateCheckPeriodMinutes";
+const wchar_t kUpdatePolicyValue[] = L"UpdateDefault";
+const wchar_t kChromeUpdatePolicyOverride[] =
+    L"Update{8A69D345-D564-463C-AFF1-A69D9E530F96}";
+
+// Don't allow update periods longer than six weeks (Chrome release cadence).
+const int kCheckPeriodOverrideMinutesMax = 60 * 24 * 7 * 6;
+
+// Google Update registry settings.
+const wchar_t kRegPathGoogleUpdate[] = L"Software\\Google\\Update";
+const wchar_t kRegPathClientsGoogleUpdate[] =
+    L"Software\\Google\\Update\\Clients\\"
+    L"{430FD4D0-B729-4F61-AA34-91526481799D}";
+const wchar_t kRegValueGoogleUpdatePv[] = L"pv";
+const wchar_t kRegValueLastStartedAU[] = L"LastStartedAU";
+const wchar_t kRegValueLastChecked[] = L"LastChecked";
+
+base::Time GetUpdaterTimeValue(bool is_machine, const wchar_t* value_name) {
+  const HKEY root_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  base::win::RegKey update_key;
+
+  if (update_key.Open(root_key, kRegPathGoogleUpdate,
+                      KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
+    DWORD value(0);
+    if (update_key.ReadValueDW(value_name, &value) == ERROR_SUCCESS) {
+      return base::Time::FromTimeT(value);
+    }
+  }
+
+  return base::Time();
+}
+
+}  // namespace
+
+std::string UpdaterState::GetUpdaterName() {
+  return std::string("Omaha");
+}
+
+base::Version UpdaterState::GetUpdaterVersion(bool is_machine) {
+  const HKEY root_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  base::string16 version;
+  base::win::RegKey key;
+
+  if (key.Open(root_key, kRegPathClientsGoogleUpdate,
+               KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
+      key.ReadValue(kRegValueGoogleUpdatePv, &version) == ERROR_SUCCESS) {
+    return base::Version(base::UTF16ToUTF8(version));
+  }
+
+  return base::Version();
+}
+
+base::Time UpdaterState::GetUpdaterLastStartedAU(bool is_machine) {
+  return GetUpdaterTimeValue(is_machine, kRegValueLastStartedAU);
+}
+
+base::Time UpdaterState::GetUpdaterLastChecked(bool is_machine) {
+  return GetUpdaterTimeValue(is_machine, kRegValueLastChecked);
+}
+
+bool UpdaterState::IsAutoupdateCheckEnabled() {
+  // Check the auto-update check period override. If it is 0 or exceeds the
+  // maximum timeout, then for all intents and purposes auto updates are
+  // disabled.
+  base::win::RegKey policy_key;
+  DWORD value = 0;
+  if (policy_key.Open(HKEY_LOCAL_MACHINE, kGoogleUpdatePoliciesKey,
+                      KEY_QUERY_VALUE) == ERROR_SUCCESS &&
+      policy_key.ReadValueDW(kCheckPeriodOverrideMinutes, &value) ==
+          ERROR_SUCCESS &&
+      (value == 0 || value > kCheckPeriodOverrideMinutesMax)) {
+    return false;
+  }
+
+  return true;
+}
+
+// Returns -1 if the policy is not found or the value was invalid. Otherwise,
+// returns a value in the [0, 3] range, representing the value of the
+// Chrome update group policy.
+int UpdaterState::GetUpdatePolicy() {
+  const int kMaxUpdatePolicyValue = 3;
+
+  base::win::RegKey policy_key;
+
+  if (policy_key.Open(HKEY_LOCAL_MACHINE, kGoogleUpdatePoliciesKey,
+                      KEY_QUERY_VALUE) != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  DWORD value = 0;
+  // First try to read the Chrome-specific override.
+  if (policy_key.ReadValueDW(kChromeUpdatePolicyOverride, &value) ==
+          ERROR_SUCCESS &&
+      value <= kMaxUpdatePolicyValue) {
+    return value;
+  }
+
+  // Try to read default override.
+  if (policy_key.ReadValueDW(kUpdatePolicyValue, &value) == ERROR_SUCCESS &&
+      value <= kMaxUpdatePolicyValue) {
+    return value;
+  }
+
+  return -1;
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/url_fetcher_downloader.cc b/src/updater_components/update_client/url_fetcher_downloader.cc
new file mode 100644
index 0000000..eaf78ad
--- /dev/null
+++ b/src/updater_components/update_client/url_fetcher_downloader.cc
@@ -0,0 +1,167 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/url_fetcher_downloader.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "components/update_client/network.h"
+#include "components/update_client/utils.h"
+#include "url/gurl.h"
+
+namespace {
+
+constexpr base::TaskTraits kTaskTraits = {
+    base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+    base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
+
+}  // namespace
+
+namespace update_client {
+
+UrlFetcherDownloader::UrlFetcherDownloader(
+    std::unique_ptr<CrxDownloader> successor,
+    scoped_refptr<NetworkFetcherFactory> network_fetcher_factory)
+    : CrxDownloader(std::move(successor)),
+      network_fetcher_factory_(network_fetcher_factory) {}
+
+UrlFetcherDownloader::~UrlFetcherDownloader() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::PostTaskAndReply(
+      FROM_HERE, kTaskTraits,
+      base::BindOnce(&UrlFetcherDownloader::CreateDownloadDir,
+                     base::Unretained(this)),
+      base::BindOnce(&UrlFetcherDownloader::StartURLFetch,
+                     base::Unretained(this), url));
+}
+
+void UrlFetcherDownloader::CreateDownloadDir() {
+  base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_url_fetcher_"),
+                               &download_dir_);
+}
+
+void UrlFetcherDownloader::StartURLFetch(const GURL& url) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (download_dir_.empty()) {
+    Result result;
+    result.error = -1;
+
+    DownloadMetrics download_metrics;
+    download_metrics.url = url;
+    download_metrics.downloader = DownloadMetrics::kUrlFetcher;
+    download_metrics.error = -1;
+    download_metrics.downloaded_bytes = -1;
+    download_metrics.total_bytes = -1;
+    download_metrics.download_time_ms = 0;
+
+    main_task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete,
+                                  base::Unretained(this), false, result,
+                                  download_metrics));
+    return;
+  }
+
+  const auto file_path = download_dir_.AppendASCII(url.ExtractFileName());
+  network_fetcher_ = network_fetcher_factory_->Create();
+  network_fetcher_->DownloadToFile(
+      url, file_path,
+      base::BindOnce(&UrlFetcherDownloader::OnResponseStarted,
+                     base::Unretained(this)),
+      base::BindRepeating(&UrlFetcherDownloader::OnDownloadProgress,
+                          base::Unretained(this)),
+      base::BindOnce(&UrlFetcherDownloader::OnNetworkFetcherComplete,
+                     base::Unretained(this)));
+
+  download_start_time_ = base::TimeTicks::Now();
+}
+
+void UrlFetcherDownloader::OnNetworkFetcherComplete(base::FilePath file_path,
+                                                    int net_error,
+                                                    int64_t content_size) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  const base::TimeTicks download_end_time(base::TimeTicks::Now());
+  const base::TimeDelta download_time =
+      download_end_time >= download_start_time_
+          ? download_end_time - download_start_time_
+          : base::TimeDelta();
+
+  // Consider a 5xx response from the server as an indication to terminate
+  // the request and avoid overloading the server in this case.
+  // is not accepting requests for the moment.
+  int error = -1;
+  if (!file_path.empty() && response_code_ == 200) {
+    DCHECK_EQ(0, net_error);
+    error = 0;
+  } else if (response_code_ != -1) {
+    error = response_code_;
+  } else {
+    error = net_error;
+  }
+
+  const bool is_handled = error == 0 || IsHttpServerError(error);
+
+  Result result;
+  result.error = error;
+  if (!error) {
+    result.response = file_path;
+  }
+
+  DownloadMetrics download_metrics;
+  download_metrics.url = url();
+  download_metrics.downloader = DownloadMetrics::kUrlFetcher;
+  download_metrics.error = error;
+  // Tests expected -1, in case of failures and no content is available.
+  download_metrics.downloaded_bytes = error ? -1 : content_size;
+  download_metrics.total_bytes = total_bytes_;
+  download_metrics.download_time_ms = download_time.InMilliseconds();
+
+  VLOG(1) << "Downloaded " << content_size << " bytes in "
+          << download_time.InMilliseconds() << "ms from " << final_url_.spec()
+          << " to " << result.response.value();
+
+  // Delete the download directory in the error cases.
+  if (error && !download_dir_.empty())
+    base::PostTask(
+        FROM_HERE, kTaskTraits,
+        base::BindOnce(IgnoreResult(&base::DeleteFile), download_dir_, true));
+
+  main_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&UrlFetcherDownloader::OnDownloadComplete,
+                                base::Unretained(this), is_handled, result,
+                                download_metrics));
+}
+
+// This callback is used to indicate that a download has been started.
+void UrlFetcherDownloader::OnResponseStarted(const GURL& final_url,
+                                             int response_code,
+                                             int64_t content_length) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  VLOG(1) << "url fetcher response started for: " << final_url.spec();
+
+  final_url_ = final_url;
+  response_code_ = response_code;
+  total_bytes_ = content_length;
+}
+
+void UrlFetcherDownloader::OnDownloadProgress(int64_t current) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  CrxDownloader::OnDownloadProgress();
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/url_fetcher_downloader.h b/src/updater_components/update_client/url_fetcher_downloader.h
new file mode 100644
index 0000000..a9330b8
--- /dev/null
+++ b/src/updater_components/update_client/url_fetcher_downloader.h
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_URL_FETCHER_DOWNLOADER_H_
+#define COMPONENTS_UPDATE_CLIENT_URL_FETCHER_DOWNLOADER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "components/update_client/crx_downloader.h"
+
+namespace update_client {
+
+class NetworkFetcher;
+class NetworkFetcherFactory;
+
+// Implements a CRX downloader using a NetworkFetcher object.
+class UrlFetcherDownloader : public CrxDownloader {
+ public:
+  UrlFetcherDownloader(
+      std::unique_ptr<CrxDownloader> successor,
+      scoped_refptr<NetworkFetcherFactory> network_fetcher_factory);
+  ~UrlFetcherDownloader() override;
+
+ private:
+  // Overrides for CrxDownloader.
+  void DoStartDownload(const GURL& url) override;
+
+  void CreateDownloadDir();
+  void StartURLFetch(const GURL& url);
+  void OnNetworkFetcherComplete(base::FilePath file_path,
+                                int net_error,
+                                int64_t content_size);
+  void OnResponseStarted(const GURL& final_url,
+                         int response_code,
+                         int64_t content_length);
+  void OnDownloadProgress(int64_t content_length);
+
+  THREAD_CHECKER(thread_checker_);
+
+  scoped_refptr<NetworkFetcherFactory> network_fetcher_factory_;
+  std::unique_ptr<NetworkFetcher> network_fetcher_;
+
+  // Contains a temporary download directory for the downloaded file.
+  base::FilePath download_dir_;
+
+  base::TimeTicks download_start_time_;
+
+  GURL final_url_;
+  int response_code_ = -1;
+  int64_t total_bytes_ = -1;
+
+  DISALLOW_COPY_AND_ASSIGN(UrlFetcherDownloader);
+};
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_URL_FETCHER_DOWNLOADER_H_
diff --git a/src/updater_components/update_client/utils.cc b/src/updater_components/update_client/utils.cc
new file mode 100644
index 0000000..4952c37
--- /dev/null
+++ b/src/updater_components/update_client/utils.cc
@@ -0,0 +1,167 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/utils.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstring>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "components/crx_file/id_util.h"
+#include "components/update_client/component.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/network.h"
+#include "components/update_client/update_client.h"
+#include "components/update_client/update_client_errors.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
+#include "url/gurl.h"
+
+namespace update_client {
+
+bool HasDiffUpdate(const Component& component) {
+  return !component.crx_diffurls().empty();
+}
+
+bool IsHttpServerError(int status_code) {
+  return 500 <= status_code && status_code < 600;
+}
+
+bool DeleteFileAndEmptyParentDirectory(const base::FilePath& filepath) {
+  if (!base::DeleteFile(filepath, false))
+    return false;
+
+  const base::FilePath dirname(filepath.DirName());
+  if (!base::IsDirectoryEmpty(dirname))
+    return true;
+
+  return base::DeleteFile(dirname, false);
+}
+
+std::string GetCrxComponentID(const CrxComponent& component) {
+  return GetCrxIdFromPublicKeyHash(component.pk_hash);
+}
+
+std::string GetCrxIdFromPublicKeyHash(const std::vector<uint8_t>& pk_hash) {
+  const std::string result =
+      crx_file::id_util::GenerateIdFromHash(&pk_hash[0], pk_hash.size());
+  DCHECK(crx_file::id_util::IdIsValid(result));
+  return result;
+}
+
+bool VerifyFileHash256(const base::FilePath& filepath,
+                       const std::string& expected_hash_str) {
+  std::vector<uint8_t> expected_hash;
+  if (!base::HexStringToBytes(expected_hash_str, &expected_hash) ||
+      expected_hash.size() != crypto::kSHA256Length) {
+    return false;
+  }
+
+  base::MemoryMappedFile mmfile;
+  if (!mmfile.Initialize(filepath))
+    return false;
+
+  uint8_t actual_hash[crypto::kSHA256Length] = {0};
+  std::unique_ptr<crypto::SecureHash> hasher(
+      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+  hasher->Update(mmfile.data(), mmfile.length());
+  hasher->Finish(actual_hash, sizeof(actual_hash));
+
+  return memcmp(actual_hash, &expected_hash[0], sizeof(actual_hash)) == 0;
+}
+
+bool IsValidBrand(const std::string& brand) {
+  const size_t kMaxBrandSize = 4;
+  if (!brand.empty() && brand.size() != kMaxBrandSize)
+    return false;
+
+  return std::find_if_not(brand.begin(), brand.end(), [](char ch) {
+           return base::IsAsciiAlpha(ch);
+         }) == brand.end();
+}
+
+// Helper function.
+// Returns true if |part| matches the expression
+// ^[<special_chars>a-zA-Z0-9]{min_length,max_length}$
+bool IsValidInstallerAttributePart(const std::string& part,
+                                   const std::string& special_chars,
+                                   size_t min_length,
+                                   size_t max_length) {
+  if (part.size() < min_length || part.size() > max_length)
+    return false;
+
+  return std::find_if_not(part.begin(), part.end(), [&special_chars](char ch) {
+           if (base::IsAsciiAlpha(ch) || base::IsAsciiDigit(ch))
+             return true;
+
+           for (auto c : special_chars) {
+             if (c == ch)
+               return true;
+           }
+
+           return false;
+         }) == part.end();
+}
+
+// Returns true if the |name| parameter matches ^[-_a-zA-Z0-9]{1,256}$ .
+bool IsValidInstallerAttributeName(const std::string& name) {
+  return IsValidInstallerAttributePart(name, "-_", 1, 256);
+}
+
+// Returns true if the |value| parameter matches ^[-.,;+_=a-zA-Z0-9]{0,256}$ .
+bool IsValidInstallerAttributeValue(const std::string& value) {
+  return IsValidInstallerAttributePart(value, "-.,;+_=", 0, 256);
+}
+
+bool IsValidInstallerAttribute(const InstallerAttribute& attr) {
+  return IsValidInstallerAttributeName(attr.first) &&
+         IsValidInstallerAttributeValue(attr.second);
+}
+
+void RemoveUnsecureUrls(std::vector<GURL>* urls) {
+  DCHECK(urls);
+  base::EraseIf(*urls,
+                [](const GURL& url) { return !url.SchemeIsCryptographic(); });
+}
+
+CrxInstaller::Result InstallFunctionWrapper(
+    base::OnceCallback<bool()> callback) {
+  return CrxInstaller::Result(std::move(callback).Run()
+                                  ? InstallError::NONE
+                                  : InstallError::GENERIC_ERROR);
+}
+
+// TODO(cpu): add a specific attribute check to a component json that the
+// extension unpacker will reject, so that a component cannot be installed
+// as an extension.
+std::unique_ptr<base::DictionaryValue> ReadManifest(
+    const base::FilePath& unpack_path) {
+  base::FilePath manifest =
+      unpack_path.Append(FILE_PATH_LITERAL("manifest.json"));
+  if (!base::PathExists(manifest))
+    return std::unique_ptr<base::DictionaryValue>();
+  JSONFileValueDeserializer deserializer(manifest);
+  std::string error;
+  std::unique_ptr<base::Value> root = deserializer.Deserialize(nullptr, &error);
+  if (!root)
+    return std::unique_ptr<base::DictionaryValue>();
+  if (!root->is_dict())
+    return std::unique_ptr<base::DictionaryValue>();
+  return std::unique_ptr<base::DictionaryValue>(
+      static_cast<base::DictionaryValue*>(root.release()));
+}
+
+}  // namespace update_client
diff --git a/src/updater_components/update_client/utils.h b/src/updater_components/update_client/utils.h
new file mode 100644
index 0000000..14567e6
--- /dev/null
+++ b/src/updater_components/update_client/utils.h
@@ -0,0 +1,91 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UPDATE_CLIENT_UTILS_H_
+#define COMPONENTS_UPDATE_CLIENT_UTILS_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "components/update_client/update_client.h"
+
+class GURL;
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+}
+
+namespace update_client {
+
+class Component;
+struct CrxComponent;
+
+// Defines a name-value pair that represents an installer attribute.
+// Installer attributes are component-specific metadata, which may be serialized
+// in an update check request.
+using InstallerAttribute = std::pair<std::string, std::string>;
+
+// Returns true if the |component| contains a valid differential update url.
+bool HasDiffUpdate(const Component& component);
+
+// Returns true if the |status_code| represents a server error 5xx.
+bool IsHttpServerError(int status_code);
+
+// Deletes the file and its directory, if the directory is empty. If the
+// parent directory is not empty, the function ignores deleting the directory.
+// Returns true if the file and the empty directory are deleted.
+bool DeleteFileAndEmptyParentDirectory(const base::FilePath& filepath);
+
+// Returns the component id of the |component|. The component id is in a
+// format similar with the format of an extension id.
+std::string GetCrxComponentID(const CrxComponent& component);
+
+// Returns a CRX id from a public key hash.
+std::string GetCrxIdFromPublicKeyHash(const std::vector<uint8_t>& pk_hash);
+
+// Returns true if the actual SHA-256 hash of the |filepath| matches the
+// |expected_hash|.
+bool VerifyFileHash256(const base::FilePath& filepath,
+                       const std::string& expected_hash);
+
+// Returns true if the |brand| parameter matches ^[a-zA-Z]{4}?$ .
+bool IsValidBrand(const std::string& brand);
+
+// Returns true if the name part of the |attr| parameter matches
+// ^[-_a-zA-Z0-9]{1,256}$ and the value part of the |attr| parameter
+// matches ^[-.,;+_=a-zA-Z0-9]{0,256}$ .
+bool IsValidInstallerAttribute(const InstallerAttribute& attr);
+
+// Removes the unsecure urls in the |urls| parameter.
+void RemoveUnsecureUrls(std::vector<GURL>* urls);
+
+// Adapter function for the old definitions of CrxInstaller::Install until the
+// component installer code is migrated to use a Result instead of bool.
+CrxInstaller::Result InstallFunctionWrapper(
+    base::OnceCallback<bool()> callback);
+
+// Deserializes the CRX manifest. The top level must be a dictionary.
+std::unique_ptr<base::DictionaryValue> ReadManifest(
+    const base::FilePath& unpack_path);
+
+// Converts a custom, specific installer error (and optionally extended error)
+// to an installer result.
+template <typename T>
+CrxInstaller::Result ToInstallerResult(const T& error, int extended_error = 0) {
+  static_assert(std::is_enum<T>::value,
+                "Use an enum class to define custom installer errors");
+  return CrxInstaller::Result(
+      static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) +
+          static_cast<int>(error),
+      extended_error);
+}
+
+}  // namespace update_client
+
+#endif  // COMPONENTS_UPDATE_CLIENT_UTILS_H_
diff --git a/src/updater_components/update_client/utils_unittest.cc b/src/updater_components/update_client/utils_unittest.cc
new file mode 100644
index 0000000..71a8b88
--- /dev/null
+++ b/src/updater_components/update_client/utils_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/update_client/utils.h"
+
+#include <iterator>
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "components/update_client/updater_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using std::string;
+
+namespace {
+
+base::FilePath MakeTestFilePath(const char* file) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components/test/data/update_client")
+      .AppendASCII(file);
+}
+
+}  // namespace
+
+namespace update_client {
+
+TEST(UpdateClientUtils, VerifyFileHash256) {
+  EXPECT_TRUE(VerifyFileHash256(
+      MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"),
+      std::string(
+          "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87")));
+
+  EXPECT_FALSE(VerifyFileHash256(
+      MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"),
+      std::string("")));
+
+  EXPECT_FALSE(VerifyFileHash256(
+      MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"),
+      std::string("abcd")));
+
+  EXPECT_FALSE(VerifyFileHash256(
+      MakeTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"),
+      std::string(
+          "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")));
+}
+
+// Tests that the brand matches ^[a-zA-Z]{4}?$
+TEST(UpdateClientUtils, IsValidBrand) {
+  // The valid brand code must be empty or exactly 4 chars long.
+  EXPECT_TRUE(IsValidBrand(std::string("")));
+  EXPECT_TRUE(IsValidBrand(std::string("TEST")));
+  EXPECT_TRUE(IsValidBrand(std::string("test")));
+  EXPECT_TRUE(IsValidBrand(std::string("TEst")));
+
+  EXPECT_FALSE(IsValidBrand(std::string("T")));      // Too short.
+  EXPECT_FALSE(IsValidBrand(std::string("TE")));     //
+  EXPECT_FALSE(IsValidBrand(std::string("TES")));    //
+  EXPECT_FALSE(IsValidBrand(std::string("TESTS")));  // Too long.
+  EXPECT_FALSE(IsValidBrand(std::string("TES1")));   // Has digit.
+  EXPECT_FALSE(IsValidBrand(std::string(" TES")));   // Begins with white space.
+  EXPECT_FALSE(IsValidBrand(std::string("TES ")));   // Ends with white space.
+  EXPECT_FALSE(IsValidBrand(std::string("T ES")));   // Contains white space.
+  EXPECT_FALSE(IsValidBrand(std::string("<TE")));    // Has <.
+  EXPECT_FALSE(IsValidBrand(std::string("TE>")));    // Has >.
+  EXPECT_FALSE(IsValidBrand(std::string("\"")));     // Has "
+  EXPECT_FALSE(IsValidBrand(std::string("\\")));     // Has backslash.
+  EXPECT_FALSE(IsValidBrand(std::string("\xaa")));   // Has non-ASCII char.
+}
+
+TEST(UpdateClientUtils, GetCrxComponentId) {
+  static const uint8_t kHash[16] = {
+      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+  };
+  CrxComponent component;
+  component.pk_hash.assign(kHash, kHash + sizeof(kHash));
+
+  EXPECT_EQ(std::string("abcdefghijklmnopabcdefghijklmnop"),
+            GetCrxComponentID(component));
+}
+
+TEST(UpdateClientUtils, GetCrxIdFromPublicKeyHash) {
+  static const uint8_t kHash[16] = {
+      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+  };
+
+  EXPECT_EQ(std::string("abcdefghijklmnopabcdefghijklmnop"),
+            GetCrxIdFromPublicKeyHash({std::cbegin(kHash), std::cend(kHash)}));
+}
+
+// Tests that the name of an InstallerAttribute matches ^[-_=a-zA-Z0-9]{1,256}$
+TEST(UpdateClientUtils, IsValidInstallerAttributeName) {
+  // Test the length boundaries.
+  EXPECT_FALSE(IsValidInstallerAttribute(
+      make_pair(std::string(0, 'a'), std::string("value"))));
+  EXPECT_TRUE(IsValidInstallerAttribute(
+      make_pair(std::string(1, 'a'), std::string("value"))));
+  EXPECT_TRUE(IsValidInstallerAttribute(
+      make_pair(std::string(256, 'a'), std::string("value"))));
+  EXPECT_FALSE(IsValidInstallerAttribute(
+      make_pair(std::string(257, 'a'), std::string("value"))));
+
+  const char* const valid_names[] = {"A", "Z", "a", "a-b", "A_B",
+                                     "z", "0", "9", "-_"};
+  for (const char* name : valid_names)
+    EXPECT_TRUE(IsValidInstallerAttribute(
+        make_pair(std::string(name), std::string("value"))));
+
+  const char* const invalid_names[] = {
+      "",   "a=1", " name", "name ", "na me", "<name", "name>",
+      "\"", "\\",  "\xaa",  ".",     ",",     ";",     "+"};
+  for (const char* name : invalid_names)
+    EXPECT_FALSE(IsValidInstallerAttribute(
+        make_pair(std::string(name), std::string("value"))));
+}
+
+// Tests that the value of an InstallerAttribute matches
+// ^[-.,;+_=a-zA-Z0-9]{0,256}$
+TEST(UpdateClientUtils, IsValidInstallerAttributeValue) {
+  // Test the length boundaries.
+  EXPECT_TRUE(IsValidInstallerAttribute(
+      make_pair(std::string("name"), std::string(0, 'a'))));
+  EXPECT_TRUE(IsValidInstallerAttribute(
+      make_pair(std::string("name"), std::string(256, 'a'))));
+  EXPECT_FALSE(IsValidInstallerAttribute(
+      make_pair(std::string("name"), std::string(257, 'a'))));
+
+  const char* const valid_values[] = {"",  "a=1", "A", "Z",      "a",
+                                      "z", "0",   "9", "-.,;+_="};
+  for (const char* value : valid_values)
+    EXPECT_TRUE(IsValidInstallerAttribute(
+        make_pair(std::string("name"), std::string(value))));
+
+  const char* const invalid_values[] = {" ap", "ap ", "a p", "<ap",
+                                        "ap>", "\"",  "\\",  "\xaa"};
+  for (const char* value : invalid_values)
+    EXPECT_FALSE(IsValidInstallerAttribute(
+        make_pair(std::string("name"), std::string(value))));
+}
+
+TEST(UpdateClientUtils, RemoveUnsecureUrls) {
+  const GURL test1[] = {GURL("http://foo"), GURL("https://foo")};
+  std::vector<GURL> urls(std::begin(test1), std::end(test1));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(1u, urls.size());
+  EXPECT_EQ(urls[0], GURL("https://foo"));
+
+  const GURL test2[] = {GURL("https://foo"), GURL("http://foo")};
+  urls.assign(std::begin(test2), std::end(test2));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(1u, urls.size());
+  EXPECT_EQ(urls[0], GURL("https://foo"));
+
+  const GURL test3[] = {GURL("https://foo"), GURL("https://bar")};
+  urls.assign(std::begin(test3), std::end(test3));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(2u, urls.size());
+  EXPECT_EQ(urls[0], GURL("https://foo"));
+  EXPECT_EQ(urls[1], GURL("https://bar"));
+
+  const GURL test4[] = {GURL("http://foo")};
+  urls.assign(std::begin(test4), std::end(test4));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(0u, urls.size());
+
+  const GURL test5[] = {GURL("http://foo"), GURL("http://bar")};
+  urls.assign(std::begin(test5), std::end(test5));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(0u, urls.size());
+}
+
+TEST(UpdateClientUtils, ToInstallerResult) {
+  enum EnumA {
+    ENTRY0 = 10,
+    ENTRY1 = 20,
+  };
+
+  enum class EnumB {
+    ENTRY0 = 0,
+    ENTRY1,
+  };
+
+  const auto result1 = ToInstallerResult(EnumA::ENTRY0);
+  EXPECT_EQ(110, result1.error);
+  EXPECT_EQ(0, result1.extended_error);
+
+  const auto result2 = ToInstallerResult(ENTRY1, 10000);
+  EXPECT_EQ(120, result2.error);
+  EXPECT_EQ(10000, result2.extended_error);
+
+  const auto result3 = ToInstallerResult(EnumB::ENTRY0);
+  EXPECT_EQ(100, result3.error);
+  EXPECT_EQ(0, result3.extended_error);
+
+  const auto result4 = ToInstallerResult(EnumB::ENTRY1, 20000);
+  EXPECT_EQ(101, result4.error);
+  EXPECT_EQ(20000, result4.extended_error);
+}
+
+}  // namespace update_client
diff --git a/src/v8/gypfiles/toolchain.gypi b/src/v8/gypfiles/toolchain.gypi
index 14183a9..a91e8e8 100644
--- a/src/v8/gypfiles/toolchain.gypi
+++ b/src/v8/gypfiles/toolchain.gypi
@@ -181,7 +181,7 @@
               'CAN_USE_VFP3_INSTRUCTIONS',
             ],
           }],
-          [ 'arm_fpu=="vfpv3"', {
+          [ 'arm_fpu=="vfpv3" or arm_fpu=="neon-vfpv4"', {
             'defines': [
               'CAN_USE_VFP3_INSTRUCTIONS',
               'CAN_USE_VFP32DREGS',
diff --git a/src/v8/src/arm/cpu-arm.cc b/src/v8/src/arm/cpu-arm.cc
index f5d2ab1..ecece7d 100644
--- a/src/v8/src/arm/cpu-arm.cc
+++ b/src/v8/src/arm/cpu-arm.cc
@@ -8,7 +8,7 @@
 #include <sys/mman.h>  // for cache flushing.
 #undef MAP_TYPE
 #else
-#include <sys/syscall.h>  // for cache flushing.
+#define __ARM_NR_cacheflush 0x0f0002  // for cache flushing.
 #endif
 #endif
 
diff --git a/src/v8/src/base/cpu.cc b/src/v8/src/base/cpu.cc
index 47184b9..1ab7b08 100644
--- a/src/v8/src/base/cpu.cc
+++ b/src/v8/src/base/cpu.cc
@@ -6,6 +6,7 @@
 
 #if defined(STARBOARD)
 #include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/cpu_features.h"
 #endif
 
 #if V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
@@ -308,6 +309,54 @@
 
 #endif  //  V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
 
+#if defined(STARBOARD)
+
+bool CPU::StarboardDetectCPU() {
+#if (SB_API_VERSION >= 11)
+  SbCPUFeatures features;
+  if (!SbCPUFeaturesGet(&features)) {
+    return false;
+  }
+  architecture_ = features.arm.architecture_generation;
+  switch (features.architecture) {
+    case kSbCPUFeaturesArchitectureArm:
+    case kSbCPUFeaturesArchitectureArm64:
+      has_neon_ = features.arm.has_neon;
+      has_thumb2_ = features.arm.has_thumb2;
+      has_vfp_ = features.arm.has_vfp;
+      has_vfp3_ = features.arm.has_vfp3;
+      has_vfp3_d32_ = features.arm.has_vfp3_d32;
+      has_idiva_ = features.arm.has_idiva;
+      break;
+    case kSbCPUFeaturesArchitectureX86:
+    case kSbCPUFeaturesArchitectureX86_64:
+      // Following flags are mandatory for V8
+      has_cmov_ = features.x86.has_cmov;
+      has_sse2_ = features.x86.has_sse2;
+      // These flags are optional
+      has_sse3_ = features.x86.has_sse3;
+      has_ssse3_ = features.x86.has_ssse3;
+      has_sse41_ = features.x86.has_sse41;
+      has_sahf_ = features.x86.has_sahf;
+      has_avx_ = features.x86.has_avx;
+      has_fma3_ = features.x86.has_fma3;
+      has_bmi1_ = features.x86.has_bmi1;
+      has_bmi2_ = features.x86.has_bmi2;
+      has_lzcnt_ = features.x86.has_lzcnt;
+      has_popcnt_ = features.x86.has_popcnt;
+      break;
+    default:
+      return false;
+  }
+
+  return true;
+#else  // SB_API_VERSION >= 11
+  return false;
+#endif
+}
+
+#endif
+
 CPU::CPU()
     : stepping_(0),
       model_(0),
@@ -348,6 +397,13 @@
       is_fp64_mode_(false),
       has_non_stop_time_stamp_counter_(false) {
   memcpy(vendor_, "Unknown", 8);
+
+#if defined(STARBOARD)
+  if (StarboardDetectCPU()) {
+    return;
+  }
+#endif
+
 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
   int cpu_info[4];
 
diff --git a/src/v8/src/base/cpu.h b/src/v8/src/base/cpu.h
index 4b4becf..9ef56d3 100644
--- a/src/v8/src/base/cpu.h
+++ b/src/v8/src/base/cpu.h
@@ -156,6 +156,9 @@
   bool is_fp64_mode_;
   bool has_non_stop_time_stamp_counter_;
   bool has_msa_;
+#if defined(STARBOARD)
+  bool StarboardDetectCPU();
+#endif
 };
 
 }  // namespace base