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, ®istered_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, ®istered_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, ®istered_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, ®istered_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(®istration_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, ®istered_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(®_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
+ */